简体   繁体   中英

Why does struct size change with enum bitfield in struct (standard C)

Playing with the code presented in this question , I observed an increase in size of a struct when an 8 bit wide enum is used instead of an uint8_t type.

Please see these code examples:

Code Option A

typedef enum { A, B, C, MAX = 0xFF } my_enum;

struct my_compact_struct_option_a
{
    my_enum field1 : 8; // limiting enum size to 8 bits 
    uint8_t second_element[6];
    uint16_t third_element;
};

The offset of the second variable in this struct second_element is 1. This indicates that the enum field1 is limited to the size uint8_t . However, the size of the struct is 12 bytes. That's unexpected for me.

Compare this to

Code Option B

typedef uint8_t my_type;

struct my_compact_struct_option_b
{
    my_type field1; 
    uint8_t second_element[6];
    uint16_t third_element;
};

Here, offset of second_element is also 1, however, the size of this struct is 10 bytes. That's expected.

Why is the struct in the first code example increased to 12 bytes?

You can always try this code for yourself .

The alignment requirement of a bit-field is not specified by the C Standard, so it is (implicitly) implementation-defined. From this Draft C17 Standard (bold emphasis mine):

6.7.2.1 Structure and union specifiers


11 An implementation may allocate any addressable storage unit large enough to hold a bit-field. If enough space remains, a bit-field that immediately follows another bit-field in a structure shall be packed into adjacent bits of the same unit. If insufficient space remains, whether a bit-field that does not fit is put into the next unit or overlaps adjacent units is implementation-defined. The order of allocation of bit-fields within a unit (high-order to low-order or low-order to high-order) is implementation-defined. The alignment of the addressable storage unit is unspecified.

Thus, while that paragraph dictates that (say) a further added my_enum field2 : 8; should be 'packed' into the same storage unit as field1 (assuming there remains sufficient space), 2 it allows freedom for compilers to decide what they consider the best alignment requirement (and size) for that storage unit.

It would appear that most compilers (from those available on Compiler Explorer ) choose to impose the alignment requirement of the 'base' type on bit-fields. 1 Further, as an enum in C has a (fixed) underlying type of int , that would mean that your field1 (and, consequently, your my_compact_struct_option_a ) has the alignment requirement of an 'int' – likely to be 4 bytes on many/most systems.


1 This makes sense, when you think about how access to the bit-field would be realized: If it is to be 'used' as an int , then it needs to be accessed as an int .

2 For example, adding further bit-field members to your structure, immediately after field1 , doesn't increase the overall size of the structure until the total size of those added bit-fields exceeds 24 bits.

As mentioned in the other answer, the C standard states that an implementation may use any storage unit large enough to hold the bitfield. Since by default an enum is effectively an int , most compilers will use an int sized storage unit for the bitfield. In particular, both gcc and MSVC will create a 4 byte enum and a 12 byte struct .

In the case specified in the comments:

 struct foo { int a : 8; char b; };

gcc and clang give it a size of 4, while MSVC gives it a size of 8.

So what appears to be happening is that a is residing in an int sized storage unit, since that is the base type of the bitfield. The alignment of the struct is then 4 because that is the size of the largest field, specifically the int sized unit that holds the bitfield.

Where gcc and clang seem to differ from MSVC is that gcc and clang will allow non-bitfields to occupy the same storage unit as bitfields if there is sufficient space to do so, while MSVC keeps bitfields in their own storage units.


If you want to make the enum type smaller, there are implementation specific ways of doing this.

In gcc, use can either use the packed attribute:

typedef enum __attribute__((__packed__)) { A, B, C, MAX = 0xFF } my_enum;

Or you can pass the -fshort-enums flag to shrink the size of all enums. Both will cause my_enum to be 1 byte in size and struct my_compact_struct_option_a will be 10 bytes.

clang lets you specify the size of the enum with the following syntax:

typedef enum : char { A, B, C, MAX = 0xFF } my_enum;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM