Generally speaking, do not use bitfields. Certainly not in code that you want to be portable. Their semantics are much more loosely defined than the uninitiated tend to assume, and they can be surprising in multiple ways. Aspects that are important for some potential uses are unspecified or implementation-defined and do vary between implementations.
You can rely, however, on every object other than a bitfield to have a representation comprising a contiguous sequence of one or more (complete) bytes (C23 6.2.6.1/2).* Supposing, then, that your system's bytes are 8 bits wide, you cannot have a struct
or union
whose representation comprises exactly 45 bits, as 45 is not a multiple of 8. In your case, the inner struct will have a size of at least 6 bytes, so the union containing it must also be at least that large. The outer struct contains an additional member requiring 19 bits, so the overall struct must be at least 6 + 4 = 10 bytes in size.
My first recommendation would be my lead: don't use bitfields. For example,
struct foo {
union {
uint64_t fieldA;
struct {
uint16_t fieldB1 : 12;
uint64_t fieldB2 : 33;
};
};
uint32_t fieldC;
};
Of course, that does not achieve your objective of packing it into 64 bits, but C does not define any way to ensure such packing for a structure or union. Also, that's already leaning on implementation-defined behavior with respect to which type specifiers bitfield members may have.
You could consider a union of structs, such as @someprogrammerdude recommended. But as long as 64 bits is enough, you could also consider an ordinary packed integer, perhaps with supporting macros or functions:
typedef uint64_t my_fields;
#define FIELDA(mf) ((mf) >> 19)
#define SET_FIELDA(mf, v) do { \
my_fields *temp = &(mf); \
*temp = (*temp & (~(uint64_t)0 >> 45)) | (((uint64_t) (v)) << 19); \
} while (0)
// ...
Since you expressed some concern that you might need to be flexible toward changes in the data structure, wrapping accesses with macros or functions and abstracting the data type provide a great deal of flexibility for doing so.
* This appears to include C23 bit-precise integer types, so I guess those will including padding bits under many circumstances.