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

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

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

此代码被描述为已损坏。

uint32_t swaphalves(uint32_t a) { a = (a >> 16) | (a << 16); return a; } 

给出的理由是:

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

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

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

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

(§1.9/ 15)运算符操作数的值计算在运算符结果的值计算之前排序。

所以关于=运算符, a(a >> 16) | (a << 16)的评估 (a >> 16) | (a << 16)在作业之前排序。 这些都不成问题:虽然它们的部分相对于彼此都没有排序,但是没有写入需要排序a遗骸。

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

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

 uint32_t swaphalves(uint32_t a) { a = (a >> 16) | (a << 16); return a; } 

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

 uint32_t swaphalves(uint32_t a) { return (a >> 16) | (a << 16); } 

不会(没有写入序列),因此更复杂的版本(工会,memcpy)占据了文章的其余部分是没有意义的。