当我使用g ++编译此示例代码时,我收到此警告:
警告:解除引用类型惩罚指针将破坏严格别名规则
[-Wstrict-aliasing]
代码:
#include <iostream>
int main()
{
alignas(int) char data[sizeof(int)];
int *myInt = new (data) int;
*myInt = 34;
std::cout << *reinterpret_cast<int*>(data);
}
在这种情况下,没有data
别名为int,因此将其强制转换为int不会违反严格的别名规则吗?或者我在这里遗漏了什么?
编辑:奇怪,当我像这样定义data
时:
alignas(int) char* data = new char[sizeof(int)];
编译器警告消失了。堆栈分配是否与严格别名产生差异?它是char[]
而不是char*
的事实是否意味着它实际上可以为任何类型设置别名?
答案 0 :(得分:20)
警告绝对合理。指向data
的衰减指针不会指向int
类型的对象,并且将其转换为不会更改它。见[basic.life]/7:
如果在对象的生命周期结束之后和存储之前 对象占用的是重用或释放的,一个新的对象是 在原始对象占用的存储位置创建, a 指向原始对象的指针,引用的引用 到原始对象,或原始对象的名称 自动引用新对象,一旦生命周期 新对象已经开始,可用于操纵新对象,如果:
(7.1) - [...]
(7.2) - 新对象的类型与 原始对象(忽略顶级cv限定符),
新对象不是char
的数组,而是int
。 P0137形式化了指向的概念,添加了launder
:
[注意:如果不满足这些条件,则指向新对象的指针 可以从表示其地址的指针获得 通过调用
std::launder
(18.6 [support.dynamic])进行存储。 - 结束记录 ]
即。你的片段可以这样纠正:
std::cout << *std::launder(reinterpret_cast<int*>(data));
..或者只是从placement new的结果中初始化一个新指针,这也会删除警告。
答案 1 :(得分:0)
*myInt = 34;
此表达式的格式正确,因为data
为int类型的对象提供存储,而myInt
是指向int类型的对象的指针。因此,取消引用这样的指针可以访问int类型的对象。
对于*reinterpret_cast<int*>(data);
这个表达式,它将违反严格的指针别名。
首先,对data
进行了数组到指针的转换,结果是指向data
初始元素的指针,这意味着reinterpret_cast<int*>
的操作数是指向a的指针。 data
的主题。
根据以下规则:
如果在与成员子对象或数组元素e关联的存储器中创建了对象,则在以下情况下,创建的对象是e的包含对象的子对象:
- e包含对象的生存期已经开始并且没有结束,并且
- 新对象的存储空间正好覆盖了与e关联的存储位置,并且
- 新对象与e具有相同的类型(忽略cv限定)。
int类型的对象不满足这些规则。因此,reinterpret_cast<int*>
的操作数不是指向可以与int类型的对象进行指针转换的对象的指针。因此,reinterpret_cast<int*>
的结果不是指向int类型对象的指针。
程序尝试通过以下类型之一以外的glvalue访问对象的存储值,其中行为未定义:
- 对象的动态类型。
- [...]
答案 2 :(得分:-3)
如何改变
std::cout << *reinterpret_cast<int*>(data);
到
int *tmp = reinterpret_cast<int*>(data);
std::cout << *tmp;
?
就我而言,它摆脱了警告。