求值顺序
来自 cppreference.cn
任何 C 运算符的操作数的求值顺序,包括函数调用表达式中函数实参的求值顺序,以及任何表达式中子表达式的求值顺序都是未指明的(除非在下面注明)。编译器将以任何顺序对它们求值,并且在再次求值同一表达式时可以选择另一个顺序。
C 中没有从左到右或从右到左求值的概念,这不应与运算符的从左到右和从右到左结合性相混淆:表达式 f1() + f2() + f3()
由于运算符 + 的从左到右结合性而被解析为 (f1() + f2()) + f3()
,但是对 f3
的函数调用可以在运行时首先、最后或在 f1()
或 f2()
之间求值。
目录 |
[编辑] 定义
[编辑] 求值
编译器对每个表达式或子表达式执行两种求值(两者都是可选的)
- 值计算:计算表达式返回的值。这可能涉及确定对象的标识(左值求值)或读取先前分配给对象的值(右值求值)
- 副作用:访问(读取或写入)由 volatile 左值指定的对象,修改(写入)对象,原子同步(自 C11 起),修改文件,修改浮点环境(如果支持),或调用执行任何这些操作的函数。
如果表达式没有产生副作用,并且编译器可以确定该值未使用,则该表达式可能不被求值。
[编辑] 排序
"先序于" 是同一线程内求值之间的一种非对称的、传递的、成对的关系(如果涉及原子类型和内存屏障,它可能会跨线程扩展)。
- 如果在子表达式 E1 和 E2 之间存在顺序点,则 E1 的值计算和副作用先序于 E2 的每个值计算和副作用
|
(自 C11 起) |
[编辑] 规则
1) 在所有函数实参和函数指示符的求值之后,以及在实际函数调用之前,存在一个顺序点。
2) 在第一个(左)操作数的求值之后和以下二元运算符的第二个(右)操作数的求值之前,存在一个顺序点:
&&
(逻辑与)、||
(逻辑或)和 ,
(逗号)。3) 在第一个(左)操作数的求值之后和条件运算符
?:
的第二个或第三个操作数(无论哪个被求值)的求值之前,存在一个顺序点
5) 在完整声明符的末尾存在一个顺序点。
6) 在库函数返回之前立即存在一个顺序点。
|
(自 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++ 文档关于 求值顺序
|