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

C++ 这真的打破了严格的别名规则吗?,c++,language-lawyer,strict-aliasing,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*&

当我使用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);
}

编译器警告消失了。堆栈分配对严格的别名有影响吗?它是
char[]
而不是
char*
这一事实是否意味着它实际上不能对任何类型进行别名?

警告是完全正确的。指向
数据
的衰减指针不指向类型为
int
的对象,强制转换它不会改变这一点。见:

如果,在对象的生存期结束后和存储之前 当被占用的对象被重用或释放时,将创建一个新对象 在原始对象占用的存储位置创建,a 指向原始对象的指针,引用 对于原始对象,或将显示原始对象的名称 自动引用新对象,并在 新对象已启动,可用于操作新对象,如果
(7.1)-[…]
(7.2)新对象的类型与 原始对象(忽略顶级cv限定符)

新对象不是一个
char
数组,而是一个
int
,它形式化了定点的概念,添加了
launder

[注意:如果不满足这些条件,则指向新对象的指针 可以从表示其地址的指针中获取 通过调用
std::launder进行存储(18.6[support.dynamic])-结束说明
]

也就是说,您的代码片段可以通过以下方式更正:

std::cout << *std::launder(reinterpret_cast<int*>(data));
std::cout改变一下怎么样

std::cout << *reinterpret_cast<int*>(data);

std::cout
*myInt=34
此表达式格式良好,因为
数据
为int类型的对象提供存储,并且
myInt
是指向int类型对象的指针。因此,取消引用此类指针可以访问int类型的对象

对于<代码>*重新解释铸件(数据)数据
,结果是指向
数据
的初始元素的指针,这意味着
重新解释
的操作数是指向
数据
主题的指针
根据以下规则:

如果在与成员子对象或数组元素e关联的存储器中创建对象,则创建的对象是e的包含对象的子对象,前提是:

  • e的包含对象的生存期已开始,但尚未结束,以及
  • 新对象的存储正好覆盖与e关联的存储位置,并且
  • 新对象的类型与e相同(忽略cv限定)
int类型的对象不满足这些规则。因此,
reinterpret\u cast
的操作数不是指向指针可与int类型的对象相互转换的对象的指针。因此,
reinterpret\u cast
的结果不是指向int类型的对象的指针

程序试图通过以下类型之一以外的glvalue访问对象的存储值。行为未定义。

  • 对象的动态类型
  • [……]

@molbdnilo char*始终可以alias@ShafikYaghmour当然可以。我怎么会忘记呢?可能是因为
数据
已经是
数据[0]
的别名了?还有
int const*数据
int数据[1]更接近您可能想考虑使用<代码> STD::AligNeDyStult:为什么GCC 7.2以来警告完全消失了?LIVE()因此,即使
char[]
s的生存期已经结束,指针
data
衰减为仍然是访问其中存储的int的有效方法?我明白了,谢谢。我主要担心的是编译器可能会假设
数据
不能别名
int
,因此可能会优化效果
*myInt=34
会在print语句中显示。@supercat:memcpy和memmove的相对效率与此问答有什么关系?@supercat:请仔细阅读Linus。不同之处在于一个CPU周期。如果您负担不起一个周期,那么您应该编写汇编,而不是C。为什么自gcc 7.2以来警告就完全消失了?LIVE()潜在问题仍然存在。@HolyBlackCat这里没有“潜在问题”。@curiousguy我不是以英语为母语的人,所以可能“潜在”这个词不合适。关键是警告之所以存在,是因为OP的代码会导致未定义的行为(如另一个答案中所解释的),正确的做法是更改代码以删除所述UB,而不是像这个答案那样试图使警告静音。@HolyBlackCat我看不到任何UB。@curiousguy如果你不同意另一个答案,你能发表你自己的一篇文章,解释为什么OP的代码的行为是明确定义的,为什么警告是不正确的吗?“没有“打破严格的别名”这样的东西。这是一个荒谬的想法。”你应该读一读。
std::cout << *reinterpret_cast<int*>(data);
int *tmp   = reinterpret_cast<int*>(data);
std::cout << *tmp;