rsp*_*984 8 c++ memory compiler-construction strict-aliasing type-punning
我面临着关于C++严格别名规则及其可能含义的困惑.请考虑以下代码:
int main() {
int32_t a = 5;
float* f = (float*)(&a);
*f = 1.0f;
int32_t b = a; // Probably not well-defined?
float g = *f; // What about this?
}
Run Code Online (Sandbox Code Playgroud)
看看C++规范,3.10.10节,从技术上讲,没有一个给定的代码似乎违反了给定的"别名规则":
如果程序试图通过以下类型之一以外的左值访问对象的存储值,则行为未定义:
...合格的访问者类型列表...
*f = 1.0f;不会违反规则,因为无法访问存储的值,即我只是通过指针写入内存.我不是从记忆中读书或试图在这里解释一个值.int32_t b = a;不违反规则,因为我通过其原始类型进行访问.float g = *f;出于同样的原因,这条线并没有违反规则.在另一个线程中,成员CortAmmon实际上在响应中提出了相同的点,并且添加了通过写入活动对象而产生的任何可能的未定义行为,如 *f = 1.0f;将在标准的"对象生存期"定义(似乎是对于POD类型来说是微不足道的).
但是:互联网上有大量证据表明上述代码将在现代编译器上产生UB.例如,见这里和这里.
在大多数情况下,论证是编译器可以自由考虑&a,f而不是相互混叠,因此可以自由重新安排指令.
现在最大的问题是,如果这种编译器行为实际上是对标准的"过度解释".
唯一一次标准谈论"混叠"的唯一一次是在3.10.10的脚注中,其中明确指出那些是控制混叠的规则.
正如我之前提到的,我没有看到任何上述代码违反了标准,但是很多人(可能还有编译人员)认为它是非法的.
我真的很感激这里的一些澄清.
小更新:
正如成员BenVoigt指出的那样,int32_t可能float在某些平台上不一致,因此给定的代码可能违反了"存储足够的对齐和大小"规则.我想声明在大多数平台上int32_t有意选择与之对齐,float并且这个问题的假设是类型确实对齐.
小更新#2:
正如几位成员所指出的那样,这条线int32_t b = a;可能违反了标准,尽管没有绝对的确定性.我同意这一观点,并且不改变问题的任何方面,请读者从我上面的陈述中排除该行,即没有任何代码违反标准.
你的第三个要点(也许是第一个)也错了.
你说"这条线float g = *f;不会因为同样的原因而违反规则.","同样的原因"(有点模糊)似乎指的是"通过其原始类型访问".但这不是你正在做的事情.您通过类型的左值(从表达式获取)访问int32_t(命名).所以你违反了标准.afloat*f
我也相信(但在这一点上不太确定)存储值是对(存储)值的访问,因此甚至*f = 1.0f;违反了规则.