函数参数
- 函数参数在本质上与局部变量相同在栈上分配空间
- 函数参数的初始值是函数调用时的实参值
- 函数参数的求值顺序依赖于编译器的实现
- 操作符的求值顺序依赖于编译器的实现(+-*/...)
下面的程序输出什么?为什么呢?
int k = 1;
printf("%d, %d\n", k++, k++);
实例分析: 函数参数的求值顺序
#include <stdio.h>
int func(int i, int j)
{
printf("i = %d, j = %d\n", i, j);
}
int main()
{
int k = 1;
func(k++, k++);
printf("%d\n", k);
}
输出:【gcc】
i = 2, j = 1
3
特别说明:此处暂时没有找到有其它求值顺序的编译器来输出说明。
由于C语言未明确规定函数参数的求值顺序,其交由具体的编译器厂商决定,因此为了提高程序的可移植性,不可依赖某一编译器的求值顺序行为。
程序中的顺序点
- 程序中存在一定的顺序点
- 顺序点是指执行过程中修改变量值(内存)的最晚时刻
- 在程序到达顺序点的时候,之前所作的一切操作必须完成
C 语言中的顺序点
- 每个完整表达式结束时,即分号处
- &&, ||, ?: 及 逗号表达式 的每个参数计算之后
- 函数调用时所有实参求值完成后(进入函数体之前)
下面的程序运行结束后 k 的值为多少呢?
int k = 2;
k = k++ + k++;
编程实验: 程序中的顺序点
实验 1
#include <stdio.h>
int main()
{
int k = 2;
int a = 1;
k = k++ + k ++; // 注意这里!
printf("k = %d\n", k);
if( a-- && a ) // 注意这里!
{
printf("a = %d\n", a);
}
return 0;
}
输出【gcc】:
k = 6
输出【vs2010】:
k = 6
VS2010 汇编
k = k++ + k ++;
003D356C mov eax,dword ptr [k]
003D356F add eax,dword ptr [k]
003D3572 mov dword ptr [k],eax ; k = 2 + 2 = 4
003D3575 mov ecx,dword ptr [k]
003D3578 add ecx,1
003D357B mov dword ptr [k],ecx ; k = k + 1 = 4 + 1 = 5
003D357E mov edx,dword ptr [k]
003D3581 add edx,1
003D3584 mov dword ptr [k],edx ; k = k + 1 = 4 + 1 = 6
gcc 汇编
int k = 2;
080483cd: movl $0x2,0x1c(%esp)
k = k++ + k ++;
080483dd: mov 0x1c(%esp),%eax
080483e1: add %eax,%eax
080483e3: mov %eax,0x1c(%esp) ; k = 2 + 2 = 4
080483e7: addl $0x1,0x1c(%esp) ; k = k + 1 = 4 + 1 = 5
080483ec: addl $0x1,0x1c(%esp) ; k = k + 1 = 5 + 1 = 6
实验 2
#include <stdio.h>
int func(int i, int j)
{
printf("i = %d, j = %d\n", i, j);
}
int main()
{
int k = 1;
func(k++, k++);
printf("%d\n", k);
}
输出:【gcc】
i = 2, j = 1
3
输出:【用于 80x86 的 Microsoft (R) 32 位 C/C++ 优化编译器 16.00.30319.01 版】
i = 1, j = 1
3
分析:
C 文件会被编译成汇编文件。一条C代码可能对应多条汇编代码,汇编代码的顺序也许没有特定的规定。对于不同的编译器,可能有不同的编译方式,但都必须满足这一原则:在程序到达顺序点时,所有改变内存的操作必须完成。
注意:
在实际工程中, 需要遵循一定的规则避免非 C 语言规定而与编译器相关的写法。
对于“函数参数的求值顺序”、“程序中的顺序点“不必过度深究,遇到奇怪的问题时,思考是否是这里导致的问题即可。
小结
- 函数的参数在栈上分配空间
- 函数的实参并没有固定的计算次序
- 顺序点时 C 语言中变量修改的最晚时机
以上内容参考狄泰软件学院系列课程,请大家保护原创!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。