未定义的行为:c =(b = a + 2) - (a = 1);

时间:2014-08-08 07:16:53

标签: c expression undefined-behavior

我在书C programming : A modern approach -

中读到了这一点
  

根据C标准声明,如

 c = (b=a+2) - (a=1) ;
  

导致未定义的行为。

但是没有提到原因。我的理解是:

  1. 所有变量仅在序列点之间修改过一次。 (所以不应该是UB)

  2. 未定义子表达式的评估顺序。 (但这并不意味着它会调用未定义的行为,对吗?)

  3. 还有什么导致它成为未定义的行为?

4 个答案:

答案 0 :(得分:2)

1和2是完全正确的。对于操作数的评估顺序,C中的大多数操作符都没有指定。这意味着可以首先评估(b=a+2)(a=1),并且您无法知道哪个顺序适用于任何给定的情况

此外,如果在两个序列点之间修改变量,则不允许对该变量进行任何其他访问,除了计算要存储在其中的值。

C99在6.5中强调这一点(强调我的):

  

在前一个和下一个序列点之间,一个对象应该具有它   通过表达式的评估,最多修改一次存储值。   此外,先前的值应该只读以确定要存储的值。

因此像a = a+1这样的代码是完全定义好的,而像a = a++这样的代码会导致未定义的行为。

这一切归结为"抽象机器"这是确定程序执行顺序的规则。将值写入变量是副作用,C标准规定所有副作用必须在下一个序列点之前发生。现在,如果您有多个与同一变量相关的副作用,则无法确保它们将按顺序排列,直到达到下一个序列点为止。

避免由排序和评估顺序引起的错误的实用建议是保持表达式简单,尽可能少的操作符和尽可能少的副作用。在原始示例的情况下,编写代码的更好方法是:

b = a + 2;
a = 1;
c = b - a;

上述代码既不会被编译器也不会被人类读者误解。


仅仅为了记录,C11有不同的文字,但意思相同:

  

如果标量对象的副作用相对于其中任何一个都没有排序   对同一个标量对象或值有不同的副作用   使用相同标量对象的值进行计算,行为是   未定义。如果有多个允许的排序   表达式的子表达式,如果这样的话,行为是不确定的   任何顺序都会出现无序的副作用。

答案 1 :(得分:0)

3。读取已修改的值只能作为计算新值的一部分。

ab=a+2的阅读与a=1中的写作无关,因此UB

答案 2 :(得分:0)

如果对变量的评估和同一变量的修改未被排序,则行为未定义。

您可能希望读取新值或旧值,但C中的变量不是那样的原子。您最终可能会在更新中期读取变量,或者编译器可能会搞砸其他内容,因为当评估顺序发生变化时,变量的假设值会在优化中期发生变化。

一般来说,不确定性的代码也可能是无意义的。

答案 3 :(得分:0)

如果在MIMD CPU上运行,(b=a+2)可能由一个核心处理,而a=1由另一个核心处理,导致a被写入,同时读取,导致保护错误。 (这需要一个旨在生成多处理代码的编译,通用编译器不会这样做,但这样的编译器确实存在于利基市场)