这个严格的别名示例是否正确?

zmb*_*zmb 11 c c++ strict-aliasing

在过去一周左右的时间里,我一直在阅读严格的别名规则并进入本文:了解C/C++严格别名.

本文通过几种方式将两个交换32位整数的两半进行交换,给出了良好的示例和违反严格别名规则的示例.但是,我无法理解其中一个例子.

此代码被描述为已损坏.

uint32_t
swaphalves(uint32_t a)
{
    a = (a >> 16) | (a << 16);
    return a;
}
Run Code Online (Sandbox Code Playgroud)

给出的理由是:

这个版本看起来很合理,但你不知道|的左右两侧 将各自获得原始版本,a或者如果其中一个将获得另一个的结果.这里没有序列点,因此我们对此处的操作顺序一无所知,并且您可能会使用不同级别的优化从同一编译器获得不同的结果.

我不同意.这段代码对我来说很好看.a在该a = (a >> 16 | (a << 16);行中只有一个写入,我希望a在写入之前进行两次读取.此外,没有指针或引用,也没有不兼容的类型.

我在此代码中是否缺少严格的别名冲突,或者文章是否不正确?

小智 4

这段代码中的任何地方都没有指针和引用,因此严格的别名规则甚至没有进入其中。事实上,作者调用序列点而不是严格的别名来证明它是未定义的断言。然而,这种推理似乎是错误的,并且代码片段具有完美定义的语义。正如Prasoon Saurav 更详细地解释的那样:

\n\n
\n

(\xc2\xa71.9/15) 运算符操作数的值计算在运算符结果的值计算之前排序。

\n
\n\n

因此对于运算符来说,和 =的求值顺序在赋值之前。这些都没有问题:尽管其各部分相对于彼此都没有排序,但没有写入需要排序的内容。a(a >> 16) | (a << 16)a

\n\n

(从技术上讲,这提出了一个问题,即赋值的副作用如何根据其值计算进行排序,但我找不到任何相关内容。大概它在标准中的某个地方,但我没有副本方便。出于下一段中的原因,我强烈怀疑它是在值计算之后排序的。)

\n\n

您还可以应用常识:写入a需要(a >> 16) | (a << 16)首先评估以写入正确的值,因此它不会在评估过程中发生。这篇文章的另一个问题是,即使

\n\n
uint32_t\nswaphalves(uint32_t a)\n{\n    a = (a >> 16) | (a << 16);\n    return a;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

由于序列点而具有未定义的行为,

\n\n
uint32_t\nswaphalves(uint32_t a)\n{\n    return (a >> 16) | (a << 16);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

不会(没有要排序的写入),因此占据本文其余大部分内容的更复杂的版本(unions、memcpy)是毫无意义的。

\n