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

时间:2014-07-21 16:01:24

标签: c++ c strict-aliasing

在过去一周左右的时间里,我一直在阅读严格的别名规则,并遇到了这篇文章:Understanding C/C++ Strict Aliasing

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

此代码被描述为已损坏。

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

给出的理由是:

  

这个版本看起来很合理,但你不知道该版本的右侧和左侧是否合适   |将各自获得a的原始版本,或者如果其中一个获得结果   另一个。这里没有序列点,所以我们对顺序一无所知   这里的操作,您可能会从使用不同的相同编译器得到不同的结果   优化水平。

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

我是否在此代码中错过了严格的别名冲突,或文章是否错误?

1 个答案:

答案 0 :(得分:4)

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

  

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

因此,对于=运算符,a(a >> 16) | (a << 16)的评估在分配之前进行了排序。这些都不成问题:虽然它们的部分相对于彼此都没有排序,但是对a的写入仍然不需要按顺序排列。

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

您还可以应用常识:写入a需要首先评估(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)毫无意义。