C++ 这真的打破了严格的别名规则吗?
当我使用g++编译此示例代码时,我得到以下警告: 警告:取消引用类型双关指针将破坏严格的别名规则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*&
[-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;