命名空间
变体
操作

求值顺序

来自 cppreference.com
< c‎ | 语言

任何 C 运算符的操作数的求值顺序,包括函数调用表达式中函数参数的求值顺序,以及任何表达式中子表达式的求值顺序都是未指定的(除非在下面注明)。编译器将以任何顺序对它们求值,并且在再次求值相同表达式时可以选择其他顺序。

C 语言中没有从左到右或从右到左求值的概念,这与运算符的从左到右和从右到左结合性无关:表达式 f1() + f2() + f3() 被解析为 (f1() + f2()) + f3() 这是因为运算符 + 的从左到右结合性,但在运行时,对 f3 的函数调用可能先于、后于或介于 f1()f2() 之间进行求值。

内容

[编辑] 定义

[编辑] 求值

编译器对每个表达式或子表达式执行两种求值(都是可选的)

  • 值计算:计算表达式返回的值。这可能涉及确定对象的标识 (左值求值) 或读取以前分配给对象的 value (右值求值)
  • 副作用:访问 (读取或写入) 由 volatile 左值指定的 object,修改 (写入) object、原子同步(自 C11 起),修改文件,修改浮点环境 (如果支持),或调用执行上述任何操作的函数。

如果表达式没有产生副作用,并且编译器可以确定该值未使用,则 可能不会对表达式进行求值.

[编辑] 排序

"顺序在先" 是在同一线程内进行的求值之间的一种非对称、传递、成对关系(如果涉及原子类型和内存屏障,则可能会跨线程扩展)。

  • 如果在子表达式 E1 和 E2 之间存在一个 顺序点,则 E1 的值计算和副作用都顺序在先于 E2 的每个值计算和副作用
  • 如果求值 A 顺序在先于求值 B,则求值 A 将在求值 B 开始之前完成。
  • 如果 A 不顺序在先于 B,而 B 顺序在先于 A,则求值 B 将在求值 A 开始之前完成。
  • 如果 A 不顺序在先于 B,而 B 不顺序在先于 A,则存在两种可能性
    • A 和 B 的求值是无序的:它们可以按任何顺序执行,并且可能重叠(在一个执行线程内,编译器可能会交织构成 A 和 B 的 CPU 指令)
    • A 和 B 的求值是不可确定顺序的:它们可以按任何顺序执行,但不能重叠:A 将在 B 完成之前完成,或者 B 将在 A 完成之前完成。下次求值相同表达式时,顺序可能相反。
(自 C11 起)

[编辑] 规则

1) 在所有函数参数和函数指示符的求值之后,以及在实际函数调用之前存在一个顺序点。
2) 在以下二元运算符的第一个 (左侧) 操作数求值之后,以及在第二个 (右侧) 操作数求值之前存在一个顺序点:&& (逻辑 AND)、|| (逻辑 OR) 和 , (逗号)。
3) 在条件运算符 ?: 的第一个 (左侧) 操作数求值之后,以及在第二个或第三个操作数 (无论哪个被求值) 求值之前存在一个顺序点。
4) 在完整表达式 (不是子表达式的表达式:通常以分号或 控制语句 if/switch/while/do 结尾) 求值之后,以及在下一个完整表达式之前存在一个顺序点。
5) 在完整声明符结尾处存在一个顺序点。
6) 在库函数返回之前存在一个顺序点。
7) 在格式化 I/O 中每个转换说明符相关的操作之后存在一个顺序点 (尤其是,对于 scanf 将不同的字段写入同一个变量,以及对于 printf 使用 %n 多次读取和修改或修改同一个变量是格式良好的)
8) 在库函数 qsortbsearch 执行的每个比较函数调用之前和之后都存在顺序点,以及在任何比较函数调用和 qsort 执行的相关对象的移动之间。
(自 C99 起)
9) 任何运算符的操作数的值计算 (但不包括副作用) 顺序在先于运算符结果的值计算 (但不包括副作用)。
10) 直接赋值运算符和所有复合赋值运算符的副作用 (修改左侧参数) 顺序在先于左侧和右侧参数的值计算 (但不包括副作用)。
11) 后缀自增和后缀自减运算符的值计算顺序在先于其副作用。
12) 未顺序在先于或顺序在先于另一个函数调用的函数调用是不可确定顺序的 (构成不同函数调用的 CPU 指令不能交织,即使函数是内联的)
13)初始化 列表表达式中,所有求值都是不可确定顺序的
14) 对于不可确定顺序的函数调用、复合赋值运算符的操作以及自增和自减运算符的前缀和后缀形式,它们都是单次求值。
(自 C11 起)

[编辑] 未定义行为

1) 如果对标量对象的副作用与对同一标量对象的另一个副作用无序,则 行为未定义.
i = ++i + i++; // undefined behavior
i = i++ + 1; // undefined behavior
f(++i, ++i); // undefined behavior
f(i = -1, i = -1); // undefined behavior
2) 如果对标量对象的副作用与使用同一标量对象的值进行的值计算无序,则行为未定义。
f(i, i++); // undefined behavior
a[i] = i++; // undefined bevahior
3) 只要子表达式的至少一个允许排序允许这种无序副作用,上述规则就适用。

[编辑] 参见

运算符优先级,它定义了表达式是如何从其源代码表示构建的。

C++ 文档 针对 求值顺序