C语言的求值顺序

Evaluation Order of C Language

  1. x+=x*=x 是未定义的行为吗?
  2. 谁能解释一下 Order of evaluation 中的这条规则?什么是 "single evaluation"? "single evaluation"的反义词是什么?

    14) With respect to an indeterminately-sequenced function call, the operation of compound assignment operators, and both prefix and postfix forms of increment and decrement operators are single evaluations.

1) 是的。

C17 6.5.16 赋值运算符,§3

The evaluations of the operands are unsequenced.

由于 C17 6.5 §2 而成为 UB

If a side effect on a scalar object is unsequenced relative to either a different side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined.

每个作业都是副作用。 C99 有相同的规则,但更容易理解:

C99 6.5 §2

Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be read only to determine the value to be stored.


2) 所有带有 C11 顺序变化的文本读起来都相当混乱。他们使用未在任何地方正式定义的术语,例如 "single evaluation".

规范文本为C17 6.5.15.2 §3

A compound assignment of the form E1 op= E2 is equivalent to the simple assignment expression E1 = E1 op (E2), except that the lvalue E1 is evaluated only once, and with respect to an indeterminately-sequenced function call, the operation of a compound assignment is a single evaluation.

我想这只是意味着像 int x; ... x += 1 这样的操作应该产生像这样的机器代码:

  • 在寄存器中存储 1
  • 添加 x 注册
  • 写寄存器到x

否则,假设在更新 x 的那些操作之间有一些序列。但是复合赋值无论如何都不是原子的,所以我不太明白这里的标准是什么。

x+=x*=x 具有未定义的行为,因为 x 在序列点之间分配了两次。


C11、C17中对应14)的文字说

A compound assignment of the form E1 op= E2 is equivalent to the simple assignment expression E1 = E1 op (E2), except that the lvalue E1 is evaluated only once, and with respect to an indeterminately-sequenced function call, the operation of a compound assignment is a single evaluation.

我认为它的意思是

int x = 0;

int foo(void) {
    x = 5;
    return x;
}

int main(void) {
    int y = foo() + (x += 2); 
}

将具有任一行为

int main(void) {
    int _tmp = x += 2;
    int y = foo() + _tmp; 
}

int main(void) {
    int _tmp = foo();
    int y = _tmp + (x += 2); 
}

并且不能拆分为例如

int main(void) {
    int _tmp = x;
    int _tmp2 = foo();
    x = _tmp + 2;
    int y = _tmp2 + x; 
}

注意这个保证在C11中是新增的,在C89、C99中是不存在的。