Namespaces
Variants

Order of evaluation

From cppreference.net

任何C运算符操作数的求值顺序,包括函数调用表达式中函数参数的求值顺序,以及任何表达式中子表达式的求值顺序都是未指定的(除非下文另有说明)。编译器可以按任意顺序对它们进行求值,并且在再次对同一表达式求值时可能选择不同的顺序。

C语言中不存在从左到右或从右到左求值的概念,这不应与运算符从左到右和从右到左的结合性混淆:表达式 f1 ( ) + f2 ( ) + f3 ( ) 由于 operator + 的左结合性被解析为 ( f1 ( ) + f2 ( ) ) + f3 ( ) ,但在运行时对 f3 ( ) 的函数调用可能先于、后于或在 f1 ( ) f2 ( ) 之间执行。

目录

定义

求值顺序

编译器对每个表达式或子表达式执行两种评估(两者均为可选):

  • 值计算  :计算表达式返回的数值。这可能涉及确定对象的身份( 左值求值 )或读取先前赋给对象的值(右值求值)。
  • 副作用  :对由 volatile 左值指定的对象进行访问(读取或写入)、修改(写入)对象 、原子同步 (C11起) 、修改文件、修改浮点环境(若支持),或调用执行上述任意操作的函数。

如果表达式不产生副作用且编译器能确定其值未被使用,该表达式 可能不会被求值

求值顺序

Sequenced-before 是同一线程内各求值之间的非对称、可传递的成对关系(若涉及原子类型与内存屏障,该关系可能跨线程延伸)。

  • 若在子表达式 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 前完成。下次对同一表达式求值时顺序可能相反。
(since C11)

规则

1) 在所有函数实参和函数指示符求值之后、实际函数调用之前,存在一个序列点。
2) 在以下二元运算符的第一个(左)操作数求值与第二个(右)操作数求值之间存在一个序列点: && (逻辑与)、 || (逻辑或)以及 , (逗号运算符)。
3) 在条件运算符 ?: 的第一个(左侧)操作数求值之后,以及第二个或第三个操作数(无论哪个被求值)求值之前,存在一个序列点
4) 在完整表达式(非子表达式的表达式:通常以分号或 if / switch / while / do 控制语句 结尾)求值之后、下一个完整表达式之前存在一个序列点。
5) 完整声明符的末尾存在一个序列点。
6) 库函数返回前立即存在一个序列点。
7) 格式化 I/O 中每个转换说明符关联的操作之后存在序列点(特别地, scanf 将不同字段写入同一变量,以及 printf 使用 % n 多次读取并修改或修改同一变量均是合法行为)
8) 库函数 qsort bsearch 每次调用比较函数的前后均存在序列点,同时比较函数的调用与 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 behavior
3) 只要存在至少一种允许的子表达式排序方式能够实现此类非顺序副作用,上述规则即适用。

参见

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

C++ 文档 关于 求值顺序