调用placement new时内存填充是否重要?相关代码合法性分析
关于Placement New填充字节与内存构造的问题解答
先看你给出的代码示例:
struct A { size_t x; // 8 bytes int i; // 4 bytes }; // padding 4 bytes struct B { size_t x; // 8 bytes bool b; // 1 byte }; // padding 7 bytes int main() { A a; auto p = new(&a) B; // auto p = new(std::launder(&a)) B; // is a bit better }
下面逐个解答你的问题:
1. 调用placement new时内存填充是否重要?
非常重要。填充字节的处理属于未定义行为,C++标准没有规定编译器如何处理这些字节。如果你的内存区域和其他有效对象存在重叠(比如你提到的两个A连续存储的场景),填充字节被修改的话会直接破坏相邻对象的数据,引发难以排查的bug。
2. 上述代码是否定义良好?
代码的构造操作基本合规,但有几个关键注意点:
- 当用
new(&a) B在A对象的内存上构造B后,原A对象a的生命周期立即结束,之后绝对不能再访问a,只能通过指针p访问新构造的B对象。 - 注释里的
std::launder确实更规范:原指针&a的静态类型是A*,而现在内存中实际是B对象,std::launder可以让指针正确关联到新的B对象,避免因类型不匹配导致的未定义行为。 - 前提是A和B的内存大小相同(这里64位系统下都是16字节),如果B的大小超过A,会直接触发非法的内存越界访问。
3. placement new会修改填充字节还是仅操作B的成员字节?
C++标准对此没有任何规定,属于未定义行为。不同编译器的实现差异很大:
- 部分编译器只会初始化B的成员对应的字节,填充字节保持原有状态;
- 部分编译器可能会把整个B占用的内存(包括填充部分)清零,或者填充调试用的占位值(比如
0xcdcdcdcd)。
你绝对不能假设填充字节会保持不变,也不能依赖填充字节的任何值。回到你说的连续存储A的场景:如果第一个A的填充字节刚好覆盖第二个A的x成员,一旦placement new修改了这些填充字节,第二个A的x就会被破坏,这种情况完全取决于编译器实现,没有任何保障。
内容的提问来源于stack exchange,提问作者Alexander S
相关产品推荐
相关产品推荐

