I'm studying the basics of the C language. I arrived at the chapter of structures with bit fields. The book shows an example of a struct with two different types of data: various bools and various unsigned ints.
The book declares that the structure has a size of 16 bits and that without using padding the structure would measure 10 bits.
This is the structure that the book uses in the example:
#include <stdio.h> #include <stdbool.h> struct test{ bool opaque : 1; unsigned int fill_color : 3; unsigned int : 4; bool show_border : 1; unsigned int border_color : 3; unsigned int border_style : 2; unsigned int : 2; }; int main(void) { struct test Test; printf("%zu\n", sizeof(Test)); return 0; }
Why on my compiler instead does the exact same structure measure 16 bytes (rather than bits) with padding and 16 bytes without padding?
I'm using
GCC (tdm-1) 4.9.2 compiler; Code::Blocks as IDE. Windows 7 64 Bit Intel CPU 64 bit
This is result I'm getting:
Here is a picture of the page where the example is:
In above student structure size of the structure without bit field is size of (StdId) + size of (Age) = 8 bytes + 8 Bytes = 16 bytes. After using bit fields to its members, it is 8 bits + 4 bits = 12 bits = 1.5 bytes which is very much less. Hence we can save lot of memory.
These space-saving structure members are called bit fields, and their width in bits can be explicitly declared. Bit fields are used in programs that must force a data structure to correspond to a fixed hardware representation and are unlikely to be portable.
Again, storage of bit fields in memory is done with a byte-by-byte, rather than bit-by-bit, transfer.
Bit fields can only be declared as part of a structure. The address-of operator (&) cannot be applied to bit-field components.
The Microsoft ABI lays out bitfields in a different way than GCC normally does it on other platforms. You can choose to use the Microsoft-compatible layout with the -mms-bitfields
option, or disable it with -mno-ms-bitfields
. It's likely that your GCC version uses -mms-bitfields
by default.
According to the documentation, When -mms-bitfields
is enabled:
- Every data object has an alignment requirement. The alignment requirement for all data except structures, unions, and arrays is either the size of the object or the current packing size (specified with either the aligned attribute or the pack pragma), whichever is less. For structures, unions, and arrays, the alignment requirement is the largest alignment requirement of its members. Every object is allocated an offset so that: offset % alignment_requirement == 0
- Adjacent bit-fields are packed into the same 1-, 2-, or 4-byte allocation unit if the integral types are the same size and if the next bit-field fits into the current allocation unit without crossing the boundary imposed by the common alignment requirements of the bit-fields.
Since bool
and unsigned int
have different sizes, they are packed and aligned separately, which increases the struct size substantially. The alignment of unsigned int
is 4 bytes, and having to realign three times in the middle of the struct leads to a 16 byte total size.
You can get the same behavior of the book by changing bool
to unsigned int
, or by specifying -mno-ms-bitfields
(though this will mean you can't inter-operate with code compiled on Microsoft compilers).
Note that the C standard does not specify how bitfields are laid out. So what your book says may be true for some platforms but not for all of them.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With