尽管包含位字段的对象的分配是由实现定义的,但C标准does规定,如果一个 struct 的连续位字段成员中仍有足够的空间,则应将其打包到同一个"单元"中.
从this Draft C11 Standard开始(粗体强调):
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.
因此,在第一个 struct 中,前三个字段could be压缩到一个8位单元中,但第四个字段必须放在一个单独的32位单元中(这可能需要4字节对齐,从而将 struct 的总体大小增加到8字节).What also may be significant in such a case is that padding bytes will be added to the structure, which may cause issues if your code relies on contiguous bits.
然而,在第二种情况下,所有四位字段可以放在single个32位单元中,因此 struct 大小可以减少到只有4个字节.
但请注意,这在编译器和平台之间可能有所不同,特别是,如Compiler Explorer example linked in the comments中所示,一个实现有权为第一个 struct 的前三个成员分配一个32位单元.
在Windows上,使用Visual Studio 2022中的clang cl编译器,在编译和运行以下代码时,我得到了上述 struct 之间的大小差异:
#include <stdint.h>
#include <stdio.h>
struct {
uint8_t saveable : 1;
uint8_t namespace : 1;
uint8_t changeable : 1;
uint32_t reserved : 29;
} s1;
struct {
uint32_t saveable : 1;
uint32_t namespace : 1;
uint32_t changeable : 1;
uint32_t reserved : 29;
} s2;
int main()
{
printf("Size of s1 = %zu\n", sizeof(s1));
printf("Size of s2 = %zu\n", sizeof(s2));
return 0;
}
输出:
Size of s1 = 8
Size of s2 = 4