这真的打破了严格别名规则吗?

时间:2014-11-18 20:49:17

标签: c++ language-lawyer strict-aliasing

当我使用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*的事实是否意味着它实际上可以为任何类型设置别名?

3 个答案:

答案 0 :(得分:20)

警告绝对合理。指向data的衰减指针不会指向int 类型的对象,并且将其转换为不会更改它。见[basic.life]/7

  

如果在对象的生命周期结束之后和存储之前   对象占用的是重用或释放的,一个新的对象是   在原始对象占用的存储位置创建, a   指向原始对象的指针,引用的引用   到原始对象,或原始对象的名称   自动引用新对象,一旦生命周期   新对象已经开始,可用于操纵新对象,如果
  (7.1) - [...]
(7.2) - 新对象的类型与   原始对象(忽略顶级cv限定符)

新对象不是char的数组,而是intP0137形式化了指向的概念,添加了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;

就我而言,它摆脱了警告。