C语言结构体中联合体放置位置影响整体大小的问题求解
问题原因
- C语言位域与普通成员的合并规则限制:C标准没有要求编译器将非位域类型的成员塞进前面位域剩余的未使用比特空间中。主流编译器(如GCC)的实现逻辑是:非位域的普通类型(包括联合体、结构体、基础类型等非位域定义的成员)必须从完整可寻址的字节边界开始存储,哪怕前面位域剩余的空间足够放下该成员。
- 你的
too_long_CommandFrameStruct中第一个成员crc是占4位的位域,存储在第0字节的低4位,剩余的高4位无法被后续的非位域成员cmdData(大小2字节)利用,编译器会插入4比特的填充,让cmdData从第1字节的起始位置开始存储。后续cmdData占2字节、cmdType4位、id8位加起来需要3字节,总大小就是1(第0字节,crc+填充)+2(cmdData)+1(cmdType4位+id低4位)+1(id高4位)=5字节,和你观察到的结果一致。 - 当你把
cmdData放到结构体首位时,它本身从0偏移的字节边界开始存储,占2字节,后续的三个位域可以连续打包在剩下的2字节中,总大小刚好4字节。
保留原有成员顺序的解决方案
核心思路是让cmdData对应的存储也以位域的形式声明,让编译器可以将它和前后的位域合并到连续的比特空间,不会插入填充。你可以通过匿名联合体的方式保留原有cmdData的访问方式,无需修改业务代码:
#include <stdio.h> #include <stddef.h> union UnspecificFrame_t { unsigned short RegValue; struct /* 单比特位定义 */ { unsigned bit0 :1; unsigned bit1 :1; unsigned bit2 :1; unsigned bit3 :1; unsigned bit4 :1; unsigned bit5 :1; unsigned bit6 :1; unsigned bit7 :1; unsigned bit8 :1; unsigned bit9 :1; unsigned bit10 :1; unsigned bit11 :1; unsigned bit12 :1; unsigned bit13 :1; unsigned bit14 :1; unsigned bit15 :1; } __attribute__((packed, aligned(1))) Bits; }; union Commands { union UnspecificFrame_t unSpecFrame; }; /* 保留原有成员顺序,大小为4字节 */ struct fixed_CommandFrameStruct { unsigned int crc :4; unsigned int _cmdData_buf :16; // 位域占16位,和前后位域连续存储 unsigned int cmdType :4; unsigned int id :8; // 匿名联合体,不额外占用空间,保留原来的cmdData访问方式 union { union Commands cmdData; }; }__attribute__((packed, aligned(1))) fixed_CommandFrameStruct_t; int main() { printf("fixed size: %d\n", sizeof(fixed_CommandFrameStruct_t)); // 输出4 // 可以正常访问cmdData的成员,比如 fixed_CommandFrameStruct_t.cmdData.unSpecFrame.RegValue = 0x1234; return 0; }
如果你的编译器支持C11的匿名结构体/联合体特性,以上代码可以直接使用,不需要修改原有访问结构体成员的业务逻辑,且内存布局完全符合你SPI帧的顺序要求。
内容的提问来源于stack exchange,提问作者T.B.
相关产品推荐
相关产品推荐

