GCC 9.1下类与结构体继承时内存填充异常问题问询
解答你的C++内存布局疑问
这是个非常贴近编译器实现细节的有趣问题,我结合GCC 9.1在x86-64架构下的-O3优化行为,拆解这两个问题:
1. 显式与隐式默认构造函数为何影响内存填充?
核心原因在于POD类型的兼容性约束和GCC的布局优化策略:
- 当你给
Base结构体添加显式默认构造函数(哪怕是Base() = default;)时,Base就不再是C++标准定义的POD类型了——POD要求类/结构体不能有用户显式声明的构造函数(哪怕是默认生成的)。 - 对于非POD类型,GCC在
-O3优化下会启用基类padding重用优化:Base的成员foo(8字节)+bar(4字节)总大小是12字节,但因为double的对齐要求是8字节,Base本身会被填充到16字节(末尾有4字节的padding)。此时编译器可以把Derived新增的baz(4字节)直接放进这个padding区域,所以Derived的总大小保持16字节。 - 当你移除显式默认构造函数,
Base会被编译器判定为POD类型。POD类型的布局必须严格兼容C语言的结构体布局,而C语言没有继承机制,编译器为了保证兼容性,不会允许派生类的成员占用基类的padding区域。因此Derived的布局是完整的Base(16字节)加上baz(4字节),再对齐到8字节的边界,最终大小就是24字节。
2. 访问修饰符为何会改变结构体/类的大小?
这里的关键是标准布局类型的判定和编译器的兼容性策略:
- 当你把
Base改为类时,它的成员默认是private访问控制,而Derived作为结构体,成员默认是public。此时Derived不再是标准布局类型——标准布局要求基类的非静态成员和派生类的非静态成员不能有不同的访问控制。 - 非标准布局类型不需要遵守C语言的兼容性约束,GCC会再次启用基类padding重用优化:把
Derived的baz放进Base末尾的4字节padding里,所以Derived的总大小回到16字节。 - 反过来,当
Base是结构体(默认public成员)且是POD类型时,Derived作为公有继承的结构体也是标准布局/POD类型,必须遵守C兼容性规则,无法重用padding,因此大小是24字节。
内容的提问来源于stack exchange,提问作者Salgar
相关产品推荐
相关产品推荐

