这是否违反严格别名规则?任意类型指针能否别名char指针?
关于C++严格别名规则相关问题的解答
1. Foo模板类示例是否违反严格别名规则?
示例代码:
template <typename T> struct Foo { struct ControlBlock { unsigned long long numReferences; }; Foo() { char* buffer = new char[sizeof(T) + sizeof(ControlBlock)]; // 构造控制块 new (buffer) ControlBlock{}; // 在控制块后构造T类型对象 this->ptr = buffer + sizeof(ControlBlock); new (this->ptr) T{}; } char* ptr; T* get() { // 此处将char*转为T* return (T*)ptr; } };
结论:
在对齐要求满足的前提下,该示例不违反严格别名规则。
原因:
严格别名规则的核心要求是:只能通过与内存中实际存在的对象类型兼容的左值访问内存区域。只有char/unsigned char/std::byte属于例外,允许用这三类的左值访问任意类型的对象,反向不成立。
这段代码合法的原因不是“T可以别名char”,而是你已经通过placement new操作,在ptr指向的内存位置构造了一个类型为T的有效对象。此时这块内存的实际有效对象类型就是T,你将指向该内存的char*转换为T*,再通过T*访问这个T类型对象,完全符合严格别名规则的要求。
额外注意:这段代码存在一个独立于严格别名的风险:
buffer + sizeof(ControlBlock)得到的地址,必须满足T类型的对齐要求。如果ControlBlock的大小不是T对齐要求的整数倍,会出现未对齐访问的未定义行为。
2. char转float访问示例是否合法?
示例代码:
char* buffer = (char*)malloc(16); float* pFloat = buffer; *pFloat = 6; // 任意类型指针可以别名char指针吗? // 如果上面的代码非法,那么下面的写法呢: new (pFloat) float; // 在指针指向位置placement new构造float对象 *pFloat = 7; // 现在是否合法?
分场景结论:
- 直接执行
*pFloat = 6:非法,属于未定义行为
此时malloc返回的内存仅属于原始字符存储,还没有构造任何float类型的对象。你用float类型的左值去访问这块还没有float对象的原始存储,既不符合严格别名规则,也违反了对象生命周期的要求。 - 执行placement new后执行
*pFloat =7:对齐满足的前提下合法
placement new在该地址构造了一个有效的float对象,此时这块内存的实际对象类型为float,用float类型左值访问它完全符合规则。
3. 关于float缓冲区使用的疑问
- 如果你拿到的是
char*类型的原始内存,要用作float数组缓冲区:C标准要求你必须为每个float元素逐个执行placement new构造,才能合法访问元素。如果float的默认构造是平凡的,主流编译器通常会允许直接访问,但这属于编译器拓展,C20之前的标准层面属于未定义行为。 - 如果你直接把
malloc返回的地址赋值给float*,也不能直接作为float缓冲区使用。malloc仅分配原始内存,不会构造任何对象,你仍然需要先为每个元素执行构造操作。只有你用new float[N]分配内存时,才会自动完成所有元素的默认构造,分配完成即可直接使用。
补充:C20引入了“隐式生存期类型”的规则,对于
float这类默认构造平凡的类型,调用malloc、new char[]这类分配函数时,会在存储中隐式创建对应类型的对象。C20及更高标准下,只要地址对齐满足要求,不需要显式调用placement new即可直接访问。
内容的提问来源于stack exchange,提问作者Zebrafish
相关产品推荐
相关产品推荐

