#pragma pack对内部结构体的打包设置为何会影响外部结构体成员的对齐?
问题原因解释
- 结构体的对齐约束是类型本身的固有属性,由该结构体定义时的编译选项决定,和它在哪里被使用无关。你用
#pragma pack(1)定义struct a的时候,相当于强制将该类型的对齐约束上限设为1字节,因此struct a类型的任何变量、成员都只要求1字节对齐,不需要满足4字节对齐的要求。 - 外部结构体
struct b布局成员时,只会按照每个成员自身的对齐要求插入填充:前面uint32_t foo占偏移0-3,bool bar占偏移4,下一个成员struct a foobar自身只要求1字节对齐,所以编译器不会在偏移4后面插入3字节的填充,直接把foobar放在偏移5的位置,自然就触发了静态断言失败。
最优对齐方案
根据你能不能修改struct a的定义,分两种情况选择最优方案:
可以修改struct a定义的场景
- 如果没有必须紧凑布局的需求,直接去掉
#pragma pack(1)是最优解,既满足对齐要求,又符合编译器默认行为,性能最优。 - 如果你确实需要
struct a内部是紧凑无填充的(比如用于协议解析、存储映射等场景),可以在定义struct a的时候同时指定对齐属性,兼顾内部打包和整体对齐要求:
#pragma pack(1) struct a { uint32_t one; uint32_t two; } __attribute__((aligned(4))); #pragma pack()
这种方案的优势是一次修改全局生效,所有用到struct a的地方都会自动满足4字节对齐要求,不需要在每个使用位置单独处理。
无法修改struct a定义的场景
如果struct a是第三方库提供的类型,你没法修改它的定义,直接在struct b的foobar成员处添加对齐属性即可:
struct b { uint32_t foo; bool bar; struct a foobar __attribute__((aligned(4))); };
这种方案的优势是侵入性低,只会影响当前struct b的布局,不会改动第三方类型的定义,也不会影响其他地方对struct a的使用。
不推荐手动在成员之间加保留字段做填充的方案,这种方案可移植性差,当平台对齐要求、成员类型发生变化时容易出问题。
补充说明
ARMv7架构对非对齐访问的支持有限:默认配置下访问未对齐的32位变量会直接触发硬件异常,即便开启了非对齐访问支持,也会带来明显的性能损耗,因此这类对齐问题必须处理,不要抱有侥幸心理。
内容的提问来源于stack exchange,提问作者MarcusS
相关产品推荐
相关产品推荐

