在线文档教程
C
C 语法

Order of evaluation

评估顺序

(包括函数调用表达式中函数参数的评估顺序,以及任何表达式中子表达式的评估顺序)的任何C运算符的操作数的评估顺序是未指定的(除非在下面指出)。编译器将以任何顺序对它们进行评估,并且可以在再次评估同一个表达式时选择另一个顺序。

在C中没有从左到右或从右到左的评估概念,这不应该与操作符的从左到右和从右到左的关联性相混淆:表达式f1() + f2() + f3()被解析为(f1() + f2()) + f3()由于左operator +的从右到关联性,但函数调用f3可以在运行时首先,最后或之间f1()f2()在运行时进行评估。

定义

评估

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

  • 值计算:计算表达式返回的值。这可能涉及确定对象的身份(左值评估)或读取先前分配给对象的值(右值评估)

如果表达式不产生副作用,并且编译器可以确定该值未被使用,则表达式可能不会被评估

排序

“sequenced-before”是同一线程内的评估之间的不对称,传递,成对关系(如果涉及原子类型和内存障碍,它可以跨线程延伸)。

  • 如果一个序列点存在于子表达式E1和E2之间,那么E1的值计算和副作用都被排序 - 在 E2的每个值计算和副作用之前

如果在评估B之前对评估A进行排序,那么在开始评估B之前评估A将会完成。如果A在B之前未被测序,而B在A之前被测序,那么在评估A开始之前B的评估将完成。如果A不B之前测序和B不是A之前测序,然后两种可能性存在:A,B的评价是未测序:它们可以以任何顺序执行,并且可以重叠(单个执行线程内,编译器可以交织包含A和B)的A和B评估的CPU指令是不确定地排序的:它们可以以任何顺序执行但可能不重叠:A在B之前将是完整的,或者B将在A之前完成。在下一次评估同一个表达式时是相反的。(自C11以来)

  • 如果在评估B之前对评估A进行排序,那么在开始评估B之前评估A将会完成。

(since C11)

规则

1)评估所有函数参数和函数指示符后,以及实际函数调用之前,有一个序列点。

2)在评估第一个(左)操作数之后以及评估以下二元运算符的第二个(右)操作数之前,有一个序列点:( &&逻辑AND),||(逻辑OR)和,(逗号)。

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)在初始化列表表达式中,(自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)只要子表达式的至少一个允许排序允许这样一个不确定的副作用,上述规则就适用。