How does the compiler determine the size of the structure of the bit field?

For instance:

struct a { uint32_t foreColor_ : 32; uint32_t backColor_ : 32; uint16_t lfHeight_ : 16; uint16_t flags_: 4; bool lfBold_: 1; bool lfItalic_: 1; bool lfUnderLine_: 1; bool lfDashLine_: 1; bool lfStrike_: 1; bool lfSubscript_: 1; bool lfSuperscript_: 1; }; 

- 16 bytes, but

 struct a { uint32_t foreColor_ : 32; uint32_t backColor_ : 32; uint16_t lfHeight_ : 16; uint8_t flags_: 4; bool lfBold_: 1; bool lfItalic_: 1; bool lfUnderLine_: 1; bool lfDashLine_: 1; //for ime bool lfStrike_: 1; bool lfSubscript_: 1; bool lfSuperscript_: 1; }; 

has a length of 12 bytes.

I thought the flags should be the same length, but that is not the case.

Why?

+6
source share
2 answers

The standard (9.6 from the working draft ) says the following:

indicates a bit field; its length begins with the name of the bit field of the colon. The bitfield attribute is not part of the class member type. A constant expression must be an integral constant expression with a value greater than or equal to zero. a constant expression may be greater than the number of bits in the representation of an object (3.9) such as bit fields; in such cases, additional bits are used as complement bits and do not participate in the representation of the values ​​(3.9) of the bit field. The distribution of bit fields within a class object is determined by the implementation. Alignment of bit fields from implementation . Bit fields are packed into some addressable distribution unit. [Note. Distribution blocks of bit field blocks on some machines, not others. Bit fields are assigned from right to left on some machines, from left to right on others. -end note]

(my emphasis)

So this will depend on your compiler. What apparently happens in your case - and I would call it pretty normal behavior, is that it only combines bit fields of the same type and then packs the structure into a 4 byte boundary, so in the first case, we we have:

 struct a { uint32_t foreColor_ : 32; // 4 bytes (total) uint32_t backColor_ : 32; // 8 bytes uint16_t lfHeight_ : 16; // 10 bytes uint16_t flags_: 4; // 12 bytes bool lfBold_: 1; // 13 bytes bool lfItalic_: 1; bool lfUnderLine_: 1; bool lfDashLine_: 1; bool lfStrike_: 1; bool lfSubscript_: 1; bool lfSuperscript_: 1; // still 13 bytes }; 

which is then padded with 16 bytes, and in the second -

 struct a { uint32_t foreColor_ : 32; // 4 bytes (total) uint32_t backColor_ : 32; // 8 bytes uint16_t lfHeight_ : 16; // 10 bytes uint8_t flags_: 4; // 11 bytes bool lfBold_: 1; // 12 bytes bool lfItalic_: 1; bool lfUnderLine_: 1; bool lfDashLine_: 1; bool lfStrike_: 1; bool lfSubscript_: 1; bool lfSuperscript_: 1; // still 12 bytes }; 

Who does not need to fill in and remains at 12 bytes.

+6
source

This is a specific compiler, the compiler performs various alignments to optimize access to fields.

If you want (need) to rely on alignment. (e.g. network header processing) You need to use #pragma push, pop.

Or __ attribute __ (packed) is a GCC extension.

 struct { ... } __attribute__(packed) 

This will cause the compiler to crush it.

0
source

Source: https://habr.com/ru/post/953141/


All Articles