含位域的C结构体sizeof计算结果与预期不符的疑问(对齐与内存打包相关)
含位域的C结构体sizeof计算结果与预期不符的疑问(对齐与内存打包相关)
嘿,我来帮你捋清楚这个问题——你算的结果和实际输出不一样,核心是没摸透GCC在Windows x64环境下对位域的内存打包和对齐的具体规则,而且C标准本身对位域的很多细节是未定义的,不同编译器有自己的实现逻辑,咱们就针对你遇到的GCC情况拆解:
先看你的第一个结构体test:
typedef struct { unsigned char x : 4; short int y : 8; short int z : 8; } test;
你的预期是4字节,实际也是4,这个结果是对的,但你的推理里关于padding的部分有点偏差:
x是unsigned char类型的位域,占4位,它会被分配到1个字节的存储单元(因为char的大小是1字节),剩下4位。但y是short类型的位域,GCC会保证short类型的位域的存储单元对齐到short的对齐边界——也就是2字节的倍数,所以x的1字节后面会补1字节的padding,让y的存储单元从2字节的地址开始。y和z都是short类型的位域,刚好各占8位,能完美打包到同一个2字节的short存储单元里。- 总大小就是1(x)+1(padding)+2(y+z)=4字节,刚好符合
short的对齐要求(结构体整体大小要是对齐边界的倍数),所以最终是4字节。
接下来是你最困惑的test2结构体:
typedef struct { char x : 4; short int y : 10; short int z : 10; } test2;
你预期6字节,但实际是4,这是因为你假设y和z会各自占用2字节的存储单元,但GCC的实现里有个关键规则:同类型的连续位域会尽可能打包到同一个存储单元,即使前面有不同类型的位域,只要剩余空间能衔接,就会跨单元打包(当然,这是GCC在Windows下的行为,不是所有编译器都这样)。
x是char类型的位域,占4位,用掉1字节的前4位,剩下4位。y是10位的short位域,它不会直接新开一个2字节的单元,而是先把x剩下的4位用掉,再从下一个字节里拿6位,刚好凑够10位——这时候y跨了两个字节(第一个字节的后4位+第二个字节的前6位)。z是10位的short位域,接着y用掉的第二个字节的后2位,再拿第三个字节的8位,刚好10位。- 现在所有位域加起来用了3字节(24位),但因为结构体的整体对齐要符合编译器的默认策略——GCC在Windows x64下,结构体的整体大小会默认对齐到4字节(和
int的大小一致),所以会补1字节的padding,总大小就是4字节。
最后再给你划几个GCC(Windows x64)下的位域实现要点:
- 位域的存储单元优先使用其声明类型的大小,但允许跨单元打包(只要能放下),同类型的连续位域会尽可能合并。
- 不同类型的位域,如果前一个存储单元有剩余空间,后一个位域可以接着用,不需要新开完整的存储单元。
- 结构体的整体大小会对齐到编译器默认的对齐边界(Windows x64下GCC默认是4字节,或者最大成员的对齐要求的倍数,取大的那个)。
- 很多位域的细节是C标准未定义的,比如跨类型打包、存储单元的选择,所以不同编译器(甚至同一编译器的不同平台)结果可能不一样,这也是为什么你的预期和实际输出有差异。
内容来源于stack exchange
相关产品推荐
相关产品推荐

