C语言中调用约定与函数实参求值顺序的区别及代码疑问
C语言函数实参的不确定输出问题
先看这段C代码:
#include <stdio.h> void fun(int x, int y, int z) /* 函数定义 */ { printf("%d %d %d\n", x, y, z); } int main() { int a = 3; fun(++a, ++a, ++a); /* 函数调用 */ return 0; }
这段代码在不同编译器下会产生不同输出:
6 6 66 5 44 5 6
问题解答
1. 这种行为的核心原因:实参求值顺序未定义
这和调用约定(比如cdecl)没有关系,完全是因为C语言标准没有规定函数实参的求值顺序。
直白点说:调用fun(++a, ++a, ++a)时,编译器可以自由选择先计算哪个++a——既可以从左到右算,也可以从右到左算,甚至乱序计算,只要最终把三个参数的值传给函数就行:
- 如果编译器先把三个
++a全部计算完毕,再把结果传给函数,此时a被自增三次变成6,三个参数的值都是6,就会输出6 6 6 - 如果编译器从右往左计算实参:先算最右侧的
++a(a变成4),再算中间的++a(a变成5),最后算左侧的++a(a变成6),而函数的x对应左侧实参、y对应中间、z对应右侧,所以输出6 5 4 - 如果编译器从左往右计算实参:先算左侧的
++a(a变成4),再算中间的(a变成5),最后算右侧的(a变成6),对应函数参数就是4 5 6
2. 调用约定不影响这个问题
调用约定(比如常见的cdecl)管的是参数如何传递给函数——比如参数是从右往左压入栈,还是从左往右压栈,以及调用结束后谁来清理栈空间。但它完全不管参数的值什么时候计算。
就算你能修改调用约定,编译器依然可以自由选择实参的求值顺序,所以输出结果还是会不确定。
3. 新手避坑建议
这种在同一个函数调用里多次修改同一个变量的代码,属于C语言里的未定义行为——编译器想怎么处理就怎么处理,没有统一标准结果。
要避免这种问题,最好把参数的计算拆分出来,比如改成这样:
int main() { int a = 3; int x = ++a; int y = ++a; int z = ++a; fun(x, y, z); // 此时输出固定为4 5 6,不会有任何歧义 return 0; }
内容的提问来源于stack exchange,提问作者Quorthon
相关产品推荐
相关产品推荐

