First, here, that the ISO C standard speaks about bit fields, citing the 2011 draft N1570 of the ISO C standard, section 6.7.2.1:
The bit field must be of a type that is a qualified or unqualified version of _Bool , signed int , unsigned int or some other implementation type. The implementation is determined, regardless of whether atom types are allowed.
...
A bit field is interpreted as having a signed or unsigned integer consisting of a specified number of bits. If a value of 0 or 1 is equally stored in a bit field with a non-zero width of type _Bool , the value of the bit field must be compared with the stored value; a _Bool bit field has the semantics of a _Bool .
An implementation can allocate any addressable storage unit large enough to hold a bit field. If there is enough space left, a bit field that immediately follows another bit field in the structure should be packed into adjacent bits of the same block. If there is not enough space, will there be a bit field that does not fit, is placed in the next block or the overlap of adjacent blocks is determined by the implementation. The recipient of something distribution of bit fields within the unit (from high to low or from low to high order) is determined by the implementation. Alignment address storage unit not specified.
For any type of struct , type alignment is at least the maximum alignment of any member of that type, and the size of any type is a multiple of its alignment. For example, if a structure contains a member (not a bit field) int , and int requires 4-byte alignment, then the structure itself requires 4-byte alignment or more.
Many compilers allow bit fields of integer types other than _Bool and int .
For at least some compilers, aligning a struct containing a bit field is at least an alignment of the declared type of a bit field. For example, for gcc 4.7.2 on x86_64, given:
struct sb { _Bool bf:1; }; struct si { unsigned bf:1; };
gcc gives struct sb size and alignment of 1 byte (the size and alignment of _Bool ), and struct si size and alignment of 4 bytes (this is the size and alignment of int ). It does the same with bit fields of types defined for implementation; bit defined as long long bf:1; , results in 8-byte size and alignment for the enclosing structure. This is done, although in both cases the bf bit is an object whose width is 1 bit.
I have seen similar behavior with the Sun compiler on SPARC / Solaris 9.
The experiment shows that several bit fields defined either as _Bool or unsigned can be packed into adjacent bits in one byte (this is actually necessary), so the bit fields themselves do not have strict alignment requirements.
I understand that the layout of structure elements is mainly determined by the implementation, and I do not believe that gcc behavior violates the C standard.
So my question (finally!) Is why does gcc (along with at least one unrelated C compiler, and possibly more) do this? Can gcc authors suggest that the declared type of the bit field should affect the size and alignment of the contained structure? Are they correct in this assumption? Is there a requirement in the C standard itself that I skipped?
Here is a test program that demonstrates behavior. If you want to run it on your system, you may need to comment on parts of it if you use an old compiler that does not support some of the new functions, or one that does not allow certain types of field bits. I would be interested to know if there are compilers that do not behave like gcc.
#include <stdio.h> #include <limits.h> #include <stdint.h> int main(void) { struct sb { _Bool bf:1; }; struct s8 { uint8_t bf:1; }; struct s16 { uint16_t bf:1; }; struct s32 { uint32_t bf:1; }; struct s64 { uint64_t bf:1; }; printf("sizeof (struct sb) = %2zu (%2zu bits)\n", sizeof (struct sb), sizeof (struct sb) * CHAR_BIT); printf("sizeof (struct s8) = %2zu (%2zu bits)\n", sizeof (struct s8), sizeof (struct s8) * CHAR_BIT); printf("sizeof (struct s16) = %2zu (%2zu bits)\n", sizeof (struct s16), sizeof (struct s16) * CHAR_BIT); printf("sizeof (struct s32) = %2zu (%2zu bits)\n", sizeof (struct s32), sizeof (struct s32) * CHAR_BIT); printf("sizeof (struct s64) = %2zu (%2zu bits)\n", sizeof (struct s64), sizeof (struct s64) * CHAR_BIT); return 0; }
Here is the output that I get on my system:
sizeof (struct sb) = 1 ( 8 bits) sizeof (struct s8) = 1 ( 8 bits) sizeof (struct s16) = 2 (16 bits) sizeof (struct s32) = 4 (32 bits) sizeof (struct s64) = 8 (64 bits)