不可依赖未声明的求值顺序
10.2.2 ID_evaluationOrderReliance
依赖未声明的求值顺序会导致意料之外的错误,也会降低代码的可移植性。
C 标准用“序列点(sequence point)”定义求值顺序,序列点前面的表达式先于后面的表达式求值并落实相关副作用。逻辑与、逻辑或、三元、逗号等运算符以及函数调用的左括号与序列点相关,其左子表达式先于右子表达式求值并落实副作用,赋值、算术、位运算等运算符与序列点无关,其左右子表达式的求值顺序是未声明的,函数调用表达式中各参数的求值顺序也是未声明的,详见“C 求值顺序”。
C++ 标准与 C 标准大致相同,C++17 明确了赋值、移位、数组下标等表达式的求值顺序,详见“C++ 求值顺序”。
示例:
Stack s {1, 2, 3}; // A stack, the top is 1
int pop(void); // Pop and return the top element from the stack
int x = pop() - pop(); // Non-compliant
设 pop 函数弹出并返回栈顶元素,减号左右的两个 pop 函数哪个先执行呢?这是标准未声明的,x 的值可以是 1 - 2,也可以是 2 - 1,所以不可臆断或依赖这种未声明的求值顺序。
应改为:
int a = pop();
int b = pop();
x = a - b; // Compliant, or ‘b - a’, depends on your needs
这样便明确了 x 的值为 1 - 2。
又如:
fun(pop(), pop()); // Non-compliant
设 fun 是函数名称或获取函数指针的表达式,标准规定 fun 会先于参数求值,但参数之间的求值顺序是未声明的。
相关
依据
ISO/IEC 9899:1999 5.1.2.3(2)
ISO/IEC 9899:1999 Annex C
ISO/IEC 9899:2011 5.1.2.3(3)
ISO/IEC 9899:2011 Annex C