关于c ++:序列点与运算符优先级

Sequence Points vs Operator Precedence

本问题已经有最佳答案,请猛点这里访问。

Possible Duplicate:
Unsequenced value computations (a.k.a sequence points)
Undefined Behavior and Sequence Points
Operator Precedence vs Order of Evaluation

我仍在努力思考以下表达式如何导致未定义的行为:

1
a = a++;

经过这样的调查,我发现了以下问题:

序列点和运算符优先级之间的区别?0o

我通读了所有的答案,但在细节上仍有困难。其中一个答案将上述代码示例的行为描述为不明确,即如何修改a。例如,可以归结为以下两个方面:

1
2
a=(a+1);a++;
a++;a=a;

究竟是什么使a的修改模糊不清?这是否与不同平台上的CPU指令有关,以及优化器如何利用未定义的行为?换句话说,由于生成的汇编程序,它似乎未定义?

我看不出编译器使用a=(a+1);a++;的原因,它看起来很奇怪,没有什么意义。编译器拥有什么才能使其如此工作?

编辑:

为了清楚起见,我确实理解正在发生的事情,我只是不理解当有关于运算符优先级的规则(本质上定义了表达式的计算顺序)时,如何定义它。在这种情况下,赋值最后发生,因此需要首先对a++进行评估,以确定要赋值给a的值。所以我期望的是,在固定后增量期间,首先修改a,然后生成一个值,分配回a(第二次修改)。但是操作员优先权的规则似乎使我的行为非常清楚,我找不到有任何"回旋空间"让它有未定义的行为。


你链接到的问题的第一个答案可以准确解释发生了什么。我会试着把它改成更清楚的措辞。

运算符优先级定义通过表达式计算值的顺序。表达式(a++)的结果很容易理解。

但是,修改变量a并不是表达式的一部分。是的,真的。这是你难以理解的部分,但这只是C和C++如何定义的部分。

表达式产生值,但某些表达式可能有副作用。表达式a = 1的值为1,但它也具有将变量a设置为1的副作用。至于C和C++如何定义事物,这是两个不同的步骤。同样,a++具有价值和副作用。

序列点定义在这些序列点之后计算的表达式何时可见副作用。运算符优先级与序列点无关。这就是C/C++定义事物的方式。


优先级规则指定表达式的计算顺序,但在计算期间不必发生副作用。它们可以在下一个序列点之前的任何时间发生。

在这种情况下,增量的副作用既不在赋值之前也不在赋值之后排序,因此表达式具有未定义的行为。


这可能是一个过于简单的解释,但我认为这是因为没有办法解决什么时候用"a"完成了代码。是在增量之后完成的,还是在分配之后?最终决议是圆形的。增量后的赋值更改了应用增量值时的语义。也就是说,代码在"a"递增之前不使用"a",但在分配之后才使用"a"。这几乎是死锁的语言版本。

正如我所说,我相信这不是一个很好的"学术"解释,但这就是我如何把它藏在自己的耳朵里。希望能有所帮助。


让我分析一下a = a++语句中的基本问题。我们希望实现以下所有目标:

  • 确定值a(a++的返回值,1)

  • 递增a(a++的副作用,2)

  • 将旧值分配给a(分配的效力,3)

有两种可能的排序方法:

  • 将原来的a存放到a中(无操作),然后增加a。同a = a; ++a;。这是序列1-3-2。

  • 评估a,增加a,将原值赋回a。同b = a; ++a; a = b;。这是序列1-2-3。

  • 由于没有规定的顺序,因此允许进行任何一种操作。但是他们有不同的最终结果。两个序列都不比另一个更自然。


    该声明有两个结果和两个任务:

    1
    a=a

    (因为它是一个后增量)和

    1
    a=a+1

    这些分配显然会导致a的最终值不同。

    C标准的起草者没有指出两个任务中的哪一个应该首先写入a,哪一个应该写入a,因此编译器编写者可以在任何给定的情况下自由选择他们喜欢的内容。

    结果是,它(这个特定的语句)不会崩溃,但您的程序不能再依赖于具有特定值的。


    这里的要点是,在某些CPU体系结构(如IntelItanium)上,这两个操作可以由编译器在指令级别上并行处理,但要使您的构造定义良好,这是不允许的。在序列点规范的时候,这些体系结构大多是假设的,而且由于Itanium是一种失败,所以可以很好地证明,到2012年,这其中大部分都是语言中不必要的复杂性。基本上,任何仍在使用中的体系结构都没有可能的缺点——即使对于Itanium来说,性能优势也是微乎其微的,编写一个甚至可以利用它的编译器的头疼是巨大的。

    还要注意的是,在C++ 11中,序列点被替换为之前的排序和之后的排序,这使得更多的情况像这样定义良好。