Does designated initialization guarantee any omitted bitfield is initialized to zero? https://en.cppreference.com/w/c/language/struct_initialization.html says "All members that are not initialized explicitly are empty-initialized.", but does not mention bitfields.
Example:
union command {
struct {
uint32_t op: 10;
uint32_t flags: 4;
uint32_t arg0: 8;
uint32_t arg1: 8;
uint32_t reserved: 2;
} bits;
uint8_t bytes[sizeof(uint32_t)];
};
union command cmd = {
.bits = {
.op = 0x123,
.flags = 0b1010,
.arg0 = 0xAB,
.arg1 = 0xCD,
};
};
ASSERT(cmd.bits.reserved == 0);
Since I don't explicitly initialize reserved
, I would expect it to be empty-initialized to 0, but I have seen this assert fail on our microcontroller. If my understanding is correct, then I suspect it might be a bug in the compiler we use, as it is quite old and have been seen to misbehave in the past.
They are set to zero and so are any padding bits.
A struct
is an aggregate type in C "standardese" and union
has similar rules as aggregates. A bit-field is a struct
member.
C23 6.7.11, bold emphasis mine:
If there are fewer initializers in a brace-enclosed list than there are elements or members of an aggregate, or fewer characters in a string literal used to initialize an array of known size than there are elements in the array, the remainder of the aggregate is subject to default initialization.
...default initialization, which initializes an object as follows:
- if it has pointer type, it is initialized to a null pointer;
- if it has decimal floating type, it is initialized to positive zero, and the quantum exponent is implementation-defined;
- if it has arithmetic type, and it does not have decimal floating type, it is initialized to (positive or unsigned) zero;
- if it is an aggregate, every member is initialized (recursively) according to these rules, and any padding is initialized to zero bits;
- if it is a union, the first named member is initialized (recursively) according to these rules, and any padding is initialized to zero bits.
Regarding designated initializers specifically, the rules are a bit complex. Initialization of arrays/structs/unions is also covered by chapter 6.7.11, emphasis mine:
Each brace-enclosed initializer list has an associated current object. When no designations are present, subobjects of the current object are initialized in order according to the type of the current object: array elements in increasing subscript order, structure members in declaration order, and the first named member of a union. In contrast, a designation causes the following initializer to begin initialization of the subobject described by the designator. Initialization then continues forward in order, beginning with the next subobject after that described by the designator.
Each designator list begins its description with the current object associated with the closest surrounding brace pair. Each item in the designator list (in order) specifies a particular member of its current object and changes the current object for the next designator (if any) to be that member. The current object that results at the end of the designator list is the subobject to be initialized by the following initializer.
The initialization shall occur in initializer list order, each initializer provided for a particular subobject overriding any previously listed initializer for the same subobject; all subobjects that are not initialized explicitly are subject to default initialization.
Also please note that uint32_t
does not necessarily correspond to unsigned int
so the compiler need not support bit-fields of that type. Best practice is to avoid bit-fields entirely.