从系统堆栈角度理解实参与形参
实参与形参,相信是很多朋友学习C语言的一大难点。晦涩难懂难以理解。
百度上搜到的只是说形参和实参的功能是作数据传送。那么到底是如何进行数据传递的呢?下面我就从系统堆栈角度讲解一下:
先举一个小栗子:
#include<stdio.h>
void exchange(int,int);
void exchange(int one, int another){
int tmp;
tmp = one;
one = another;
another = tmp;
}
int main(){
int num1 = 14;
int num2 = 7;
exchange(num1,num2);
printf("交换后的数值:%d %d", num1, num2);
return 0;
}
这是一个交换两个数值的一个小程序,看起来是没有什么问题的,但是编译运行,
立马发现问题所在了,没错,其值并没有被交换。
因为在一般传值调用的机制中只能把实参传送给形参,而不能把形参的值反向地传送给实参。
在上述程序中,exchange()函数交换的只是函数中的形参的值,而主函数中的实参的值根本
没有被交换。下面从对堆栈角度说一下(不理解堆栈的请自行百度)
如图所示:当主函数定义实参num1,num2时,其值会自动入栈,然后调用exchange()函数,
在此之前,操作系统会将主函数main()的现场信息(目前只有eip)指针入栈保存,然后开始调用
exchange()子函数,将子函数的首地址常量赋值给栈底指针ebp,然后使栈顶指针esp和栈底指针ebp
指向同一空间,形成空栈,然后此空栈由子函数exchange()调用。调用过程中会将其定义的形参
从右向左入栈,即形参another先入栈,按照从右向左的顺序,实参num2将其值7传递给another,
形参one后入栈,num1将其值14传递给one,最后子函数定义的变量tmp入栈。然后开始执行
exchange()子函数:
tmp = one;
one = another;
another = tmp;
经过赋值,one和another的值发生交换。
然后子函数exchange()调用完毕,栈顶指针回到栈底指针位置,恢复子函数调用之前的状态,
然后输出实参num1和num2,可以很清楚的发现,num1和num2的值并没有发生改变,
只是将其值传递给子函数内的形参one和another,exchange()子函数调换了形参one和another的值,
因此上述代码不能完成值的交换。真正需要交换的不是实参的值,而是实参的地址,代码如下:
#include<stdio.h>
void realexchange(int *, int *);
void realexchange(int *one ,int *another){
int tmp;
tmp = *one;
*one = *another;
*another = tmp;
}
int main(){
int num1 = 14;
int num2 = 7;
realexchange(&num1,&num2);
printf("交换后的两个值:%d %d",num1,num2);
return 0;
}
这段修改后的代码表达意思如图所示:
子函数中形参得到的传递的值是实参num1和num2的地址,经过exchange()子函数交换之后,
num1和num2的地址被交换, 所以最后输出的值就是经过交换的!
也就是说,实参表达式的值入栈所占用的空间,就是对应的形参的空间。而实参表达式的值入栈,
是将其值复制一份去入栈,因此exchange()函数第一次失败了。