C++中结构体紧密打包与成员指定对齐的兼容性及实现方案咨询
嗨,这个需求我之前做嵌入式开发和性能优化的时候常碰到,刚好可以给你唠唠细节~
首先直接给你核心结论:你现在用的__attribute__((packed))给结构体整体加紧密打包,同时给成员加__attribute__((aligned(64)))的写法,在GCC、Clang这类支持GNU扩展的编译器上是可以正常工作的,而且不属于未定义行为——不过要注意,这属于编译器的实现定义行为,不是标准C++规定的内容,换用MSVC这类其他编译器就得调整语法了。
你例子里sizeof(B)==128的原因其实很好理解:结构体加packed后,a和b会紧密排列,总共占1+4=5字节。但c要求64字节对齐,所以编译器会在b和c之间插入59字节的填充,让c的起始地址落在64字节边界上。另外,为了保证当你声明B类型的数组时,下一个数组元素的c也能满足64字节对齐,编译器会把整个结构体的大小向上对齐到64的整数倍——68字节(5+59+4)向上取整就是128,这就是你看到sizeof(B)为128的原因。
再给你提几个要注意的点:
- 移植性问题:
__attribute__是GNU系编译器的扩展,如果你要在MSVC上实现同样的效果,得换成#pragma pack(1)来做紧密打包,成员对齐用__declspec(align(64)),写法差异不小。 - 性能与架构兼容:紧密打包的结构体里,那些没有特殊对齐的成员(比如你的
int b)可能会处于不对齐的地址上,在x86架构上编译器会生成额外指令来访问,性能略有下降;在一些严格对齐的架构(比如部分ARM、PowerPC)上,甚至会直接触发硬件异常。
最后给你几个替代方案,按需选择:
手动控制填充(更可控):
如果你不想依赖编译器自动填充,可以手动在b和c之间插入填充数组,精准计算需要的填充字节数:struct B __attribute__((packed)) { char a; int b; char pad[64 - (sizeof(char) + sizeof(int)) % 64]; int c; };不过要注意,这种写法下结构体的整体大小是68字节,如果你需要数组中的每个
B实例的c都对齐,还得给结构体加上__attribute__((aligned(64))),这样总大小还是会被调整到128字节。用C++11的
alignas(更贴近标准):
如果你用的是C++11及以上版本,可以用标准的alignas替代GNU的aligned属性,写法更统一:struct B __attribute__((packed)) { char a; int b; alignas(64) int c; };效果和你原来的写法一致,但
alignas是标准语法,不过结构体的packed还是编译器扩展,移植性依旧要看编译器支持。拆分结构体(更清晰):
把需要特殊对齐的成员单独放到一个嵌套结构体里,给嵌套结构体加对齐属性,外层用packed,结构更清晰,也能避免一些编译器的奇怪行为:struct AlignedInt64 { alignas(64) int val; }; struct B __attribute__((packed)) { char a; int b; AlignedInt64 c; };
不管用哪种写法,都要在目标编译器和架构上实际测试,因为这类对齐相关的细节不同编译器的处理可能有细微差别。
内容来源于stack exchange

