So, I'm writing a struct that's going to be used for de-serializing a binary stream of data. To get the point across, here is a cut-down version:
typedef struct
{
bool flag1 : 1;
bool flag2 : 1;
bool flag3 : 1;
bool flag4 : 1;
uint32_t reserved : 28;
} frame_flags_t;
typedef struct
{
/* Every frame starts with a magic value. */
uint32_t magic;
frame_flags_t flags;
uint8_t reserved_1;
/* A bunch of other things */
uint32_t crc;
} frame_t;
My question is, if do the following:
frame_t f;
memcpy(&f, raw_data_p, sizeof(frame_t));
Am I guaranteed that f.flags.flag1
is really the first bit (after the magic
member, assuming a neatly packed struct (which it is))? And that .flags2
will be the one following that, and etc?
From what I understand the C and C standards don't guarantee this. Does GCC?
CodePudding user response:
Am I guaranteed that
f.flags.flag1
is really the first bit (after themagic
member, assuming a neatly packed struct (which it is))?
The C language does not guarantee that, no.
And that
.flags2
will be the one following that, and etc?
The C language does require that consecutive bitfields assigned to the same addressable storage unit be laid out without gaps between them. That is likely to mean that the flags end up occupying adjacent bits in the same byte, but it does not have to mean that.
From what I understand the C and C standards don't guarantee this. Does GCC?
No. Structure layout rules are a characteristic of an application binary interface (ABI), which is a property of operating system hardware combinations. For example, there is an ABI for Linux running on x86_64, a different one for Linux running on 32-bit x86, and still different ones for Windows running on those platforms. GCC supports a wide variety of ABIs, and it lays out structures according to the rules of the target ABI. It cannot make any blanket guarantees about details of structure layout.
For example, the relevant ABI for Linux / x86_64 is https://www.intel.com/content/dam/develop/external/us/en/documents/mpx-linux64-abi.pdf. With respect to bitfield layout, it says:
Bit-fields obey the same size and alignment rules as other structure and union members.
Also:
- bit-fields are allocated from right to left
- bit-fields must be contained in a storage unit appropriate for its declared type
- bit-fields may share a storage unit with other struct / union members
That's actually not altogether consistent, but the way it's interpreted for your frame_flags_t
is that:
- the structure has size 4, consisting of a single "addressible storage unit" of that size into which all the bitfields are packed
flag1
uses the least-significant bitflag2
uses the second-least-significant bitflag3
uses the third-least-significant bitflag4
uses the fourth-least-significant bit
Furthermore, the overall frame_t
structure has a 4-byte alignment requirement on Linux / x86_64, and it will be laid out with the minimum padding required to align all members. On such a machine, therefore, there will be no padding between the magic
member and the flags
member. x86_64 is little-endian, so that will indeed put the flag bits in the first byte following magic
on Linux / x86_64.
CodePudding user response:
Also, this is for ARM v7,
On this target you can safely use the bitfields and their behaviour is clearly specified by ABI.