如何在函数调用中评估参数?

考虑以下代码:

void res(int a,int n) { printf("%d %d, ",a,n); } void main(void) { int i; for(i=0;i<5;i++) res(i++,i); //prints 0 1, 2 3, 4 5 for(i=0;i<5;i++) res(i,i++); //prints 1 0, 3 2, 5 4 } 

查看输出,似乎每次都不会从右到左评估参数。 到底发生了什么?

未指定函数调用中参数的评估顺序。 编译器可以按照它可能决定的顺序对它们进行评估。

从C99标准6.5.2.2/10“函数调用/语义”:

函数指示符的评估顺序,实际参数和实际参数中的子表达式是未指定的,但在实际调用之前有一个序列点。

如果您需要确保特定的订购,使用临时工具是通常的解决方法:

 int i; for(i=0;i<5;i++) { int tmp = i; int tmp2 = i++; res(tmp2,tmp); } 

更重要的是(因为它导致未定义的行为,而不仅仅是未指定的行为)是你通常不能在表达式中多次使用操作数来增加/减少运算符。 那是因为:

在前一个和下一个序列点之间,对象的存储值最多只能通过表达式的计算修改一次。 此外,先前的值应该是只读的,以确定要存储的值。 (6.5 / 2“表达式”)

根据标准:参数评估的顺序未指定。 此外,请注意,在评估参数时,没有序列点(类似英里post)。 因此,将相同的变量修改为参数的一部分,不止一次会引起未定义的行为。 这是FAQ 3.2 。 您发布的代码具有不明确的行为。

令人惊讶的是,为什么标准将其保留为未指定:简单的原因是这允许编译器执行一些优化。 (参见与GOTW#56的 Q2有关的讨论。)

但是,在大多数实现中,这由所谓的调用约定决定。 调用约定不仅决定了顺序,还强制要求在调用者或被调用者上清理堆栈。

另请注意, main始终返回int