函数的调用过程(函数调用栈帧的创建)

函数的调用过程:

我们知道每一次函数调用都是一个过程,这个过程我们通常称之为:函数的调用过程。

而这个过程要为函数开辟栈空间,用于本次函数调用中临时变量的保存,现场保护,这块栈空间我们称之为函数栈帧。

而函数栈帧的维护需要两个寄存器:esp和ebp,在调用过程中,这两个寄存器存放了维护这个栈的栈顶和栈底指针。

举个例子:

调用main函数,我们为main函数分配栈帧空间,那么栈帧的维护如下图:


注意:ebp永远指向栈底    esp永远指向栈顶

下面通过一个简单的代码详细了解下函数的调用过程:

#include <stdio.h>
int Add(int x, int y)
{
	int z = 0;
	z = x + y;
	return z;
}
int main()
{
	int a = 10;
	int b = 20;
	int ret = Add(a, b);
	printf("%d\n", ret);
	return 0;
}

注意:在栈空间的使用是由高地址向低地址;

1.我们先从main函数的地方开始,要展开main函数的调用就必须为main函数创建栈帧,下面看看main函数栈帧的创建:

注:在调用main函数之前会先调用一个__tmainCRTStartup()函数

首先将ebp(__tmainCRTStartup())压栈处理(方便函数返回之后的现场恢复),然后使esp的值赋给ebp,产生新的ebp(为main函数开辟空间做准备),给esp减去一个16进制数字4ch,产生新的esp,此时,已为main函数开辟好空间。在该栈空间的栈顶进行压栈,分别压进去三个寄存器,分别是ebx,esi,edi(esp向上移动,指向新的栈顶),把栈帧预开辟的空间全部初始化成0xcccccccc,接下来处理局部变量a,b的创建。

2.接下来是Add函数的调用:

先进行传参,参数压栈,先压b,再压a(利用eax寄存器),call指令的调用,先要压栈call指令下一条指令的地址(保护现场,函数需要返回),然后跳转到Add函数的地方。

将ebp(main函数)进行压栈,将esp的值赋值给ebp,给esp减去一个十六进制的数字44h,产生新的esp,此时为Add函数预开辟好栈空间,把预开辟的栈空间全部初始化成0xcccccccc,创建变量Z,获取形参a和b相加,将结果存储到z中,再次将结果存储到eax寄存器中,通过寄存器带回函数的返回值。

3.函数的返回部分:

三次pop出栈,使得esp(Add函数)向下移动,将ebp的值赋值给esp,出栈,将出栈的内容内容保存到ebp中,回到main函数的栈帧,接下来利用ret指令将程序跳转到call指令下一条指令的地址处(ret指令会使得出栈一次,并将出栈的内容当作地址,将程序执行跳转到该地址处)。

附:

1.把运算结果放到z中,但是z不能返回给main(),所以又将z的值存到eax中,由eax带回。

2.在函数调用时实参和形参并不在同一个空间,而函数调用会直接去拿形参,对实参不会有影响。

3.保存上一个函数的ebp,是因为在函数调用完之后它需要返回上一个函数的栈底

4.函数调用完之后,应返回call指令的下一条指令继续执行程序。



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值