我对 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?
}
查看 C++ 规范第 3.10.10 节,从技术上讲,给定的代码似乎都没有违反那里给出的“别名规则”:
如果程序尝试通过以下类型之一以外的左值访问对象的存储值,则行为未定义:
...合格的访问器类型列表...
-
*f = 1.0f;
不会违反规则,因为无法访问储值,即我只是通过指针写入内存。我不是凭记忆阅读或试图解释这里的值。
- 线路
int32_t b = a;
不违反规则,因为我是通过其原始类型访问的。
- 线路
float g = *f;
不会因为同样的原因违反规则。
In 另一个线程,成员 CortAmmon 实际上在响应中提出了相同的观点,并补充说,通过写入而产生的任何可能的未定义行为活的物体,如*f = 1.0f;
,将由标准的“对象生命周期”定义来解释(这对于 POD 类型来说似乎是微不足道的)。
然而:有plenty互联网上的证据表明上述代码将在现代编译器上产生 UB。看here and here例如。
大多数情况下的争论是编译器可以自由考虑&a
and f
因为不会互相别名,因此可以自由地重新安排指令。
现在最大的问题是,这种编译器行为实际上是否是对标准的“过度解释”。
该标准唯一一次专门讨论“别名”是在 3.10.10 的脚注中,其中明确指出这些是管理别名的规则。
正如我之前提到的,我没有看到上述任何代码违反标准,但很多人(可能还有编译器人员)会认为它是非法的。
我真的非常感谢这里的一些澄清。
小更新:
正如成员 BenVoigt 正确指出的那样,int32_t
可能不符合float
在某些平台上,因此给定的代码可能违反“足够对齐和大小的存储”规则。我想声明的是int32_t
故意选择与float
在大多数平台上,并且这个问题的假设是类型确实一致。
小更新#2:
正如几位成员所指出的,这条线int32_t b = a;
可能违反了标准,尽管不是绝对肯定的。我同意这一观点,并且在不改变问题的任何方面的情况下,请读者从我上面的声明中排除这句话,即没有任何代码违反标准。
你的第三个要点是错误的(也许第一个也是错误的)。
你说“这条线float g = *f;
不会因为同样的原因而违反规则。”,其中“只是同样的原因”(有点模糊)似乎指的是“通过其原始类型访问”。但这不是你正在做的。你是访问一个int32_t
(named a
) 通过类型的左值float
(从表达式获得*f
)。所以你违反了标准。
我还相信(但对此不太确定)存储值是对(该)存储值的访问,所以即使*f = 1.0f;
违反规则。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)