序列点与运算符优先级

时间:2012-09-07 15:35:17

标签: c++ sequence-points

  

可能重复:
  Unsequenced value computations (a.k.a sequence points)
  Undefined Behavior and Sequence Points
  Operator Precedence vs Order of Evaluation

我仍然试图围绕以下表达式如何导致未定义的行为:

a = a++;

在搜索到这个问题时,我发现了以下问题:

Difference between sequence points and operator precedence? 0_o

我仔细阅读了所有答案,但我仍然对细节感到困难。其中一个答案根据a的修改方式将上述代码示例的行为描述为含糊不清的。例如,它可以归结为以下任何一种:

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

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

我没有看到编译器使用a=(a+1);a++;的原因,它只是看起来古怪而且没有多大意义。什么会让编译器使它以这种方式运行?

编辑:

为了清楚起见,我确实理解发生了什么,我只是不明白当有关于运算符优先级的规则(基本上定义表达式的求值顺序)时,它是如何被定义的。在这种情况下,分配最后发生,因此需要首先评估a++,以确定要分配给a的值。所以我期望在修复后增量期间首先修改a,但随后产生一个值以分配回a(第二次修改)。但运营商优先权的规则似乎使我的行为非常清楚,我找不到哪里有任何" wiggle-room"因为它有不确定的行为。

6 个答案:

答案 0 :(得分:8)

The first answer in the question you linked to准确地解释了发生了什么。我会尝试改写它以使其更清晰。

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

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

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

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

答案 1 :(得分:1)

这可能是一个过于简单的解释,但我认为这是因为当代码用“a”“完成”时无法解决。它是在增量或分配后完成的吗?该决议最终成为通告。增量后的赋值会更改应用递增值时的语义。也就是说,代码不是用“a”完成,直到“a”增加,但是直到分配完成后a才会增加。它几乎是死锁的语言版本。

正如我所说,我确信这不是一个很好的“学术”解释,但这就是我如何在自己的耳朵之间加以解释。希望在某种程度上有所帮助。

答案 2 :(得分:1)

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

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

答案 3 :(得分:0)

这里的要点是,在一些CPU架构上,比如Intel Itanium,这两个操作可以由编译器在指令级并行化 - 但是使你的构造定义良好会禁止这样做。在序列点规范时,这些架构大多是假设的,并且由于Itanium是一个失败,因此可以说,截至2012年,这大部分都是语言中不必要的复杂性。在任何仍然普遍使用的架构中基本上没有任何可能的缺点 - 即使对于Itanium,性能优势也很小,编写甚至可以利用它的编译器的头痛是巨大的。

另请注意,在C ++ 11中,序列点被替换为之前的序列和之后的序列,这使得更多这样的情况得到了很好的定义。

答案 4 :(得分:0)

该声明a=a++有两个结果和两个作业:

a=a

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

a=a+1

这些作业显然会导致a的最终值不同。

c标准的起草人没有说明应该首先将两个作业中的哪一个写入a以及哪一个,因此编译器编写者可以在任何给定的情况下自由选择他们喜欢的任何一个。

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

答案 5 :(得分:0)

让我在声明a = a++中解决基本问题。我们希望实现以下所有方面:

  • 确定值aa++的返回值,#1)

  • 增量aa++的副作用,#2)

  • 将旧值分配给a(作业的效果,#3)

有两种可能的方法可以测序:

  1. 将原始a存储到a(无操作);然后增加a。与a = a; ++a;相同。这是序列#1-#3-#2。

  2. 评估a,增加a,将原始值分配回a。与b = a; ++a; a = b;相同。这是序列#1-#2-#3。

  3. 由于没有规定的序列,因此允许这些操作中的任何一个。但他们有不同的最终结果。两种序列都不比另一种序列更自然。