函数调用堆栈的过程

程序在执行一个函数之前需要做一些准备工作,要将形参、局部变量、返回地址以及若干寄存器都压入栈中,然后才能执行函数体中的代码;函数体中的代码执行完毕后还要清理现场,将之前压入栈中的数据都出栈,才能接着执行函数调用位置以后的代码。

 

通过下面一个简单的例子来进入话题:

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

先从main函数开始,查看main()函数的反汇编代码

 从main函数开始,

  1. 将调用main的栈底地址入栈。
  2. 让原本指向调用方栈底的ebp(栈底指针)指向当前函数main的栈底。
  3. 给当前函数main开辟栈帧。
  4. 对开辟的栈帧进行初始化。
  5. 将三个局部变量放入栈中。
  6. 开辟sum函数的形参内存,从右往左,依次把b的值,a的值入栈。
  7. 将main函数的下一行指令入栈,然后调用函数sum

sum函数:

  1. 将main的栈底地址入栈。
  2. 让原本指向main函数栈底的ebp(栈底指针)指向当前函数sum的栈底。
  3. 给当前函数sum开辟栈帧。
  4. 对开辟的栈帧进行初始化。
  5. 将形参变量放在eax寄存器中。
  6. 将eax寄存器中的值赋值给了局部变量tmp。
  7. 局部变量的值,通过eax寄存器返回。
  8. 出栈,并把出栈元素赋值给ebp(栈底指针),因为调用sum函数刚开始时,把main函数的栈底地址放入栈中,出栈操作,让ebp(栈底指针)指向了main函数的栈底地址。
  9. 最后,函数结束ret指令干了两件事:先出栈;再将出栈的值放到CPU的PC寄存器中。因为PC寄存器中永远放的是下一次执行指令的地址,所以就顺理成章的在函数调用完之后依旧接着原来的代码继续执行。

补充知识点:

C语言中,每个栈帧对应着一个未运行完的函数。栈帧中保存了该函数的返回地址和局部变量。

总结:

1、函数的运行都是在栈上开辟内存。

2、栈是通过esp(栈顶指针)、ebp(栈底指针)两个指针来标识的。

3、对于栈上的访问都是通过ebp(栈底指针)的偏移来访问的。

4、在调用一个函数时,有两件事情要做:先将调用函数的下一行指令的地址压入栈中;再进行跳转。

5、在函数调用时检查函数是否申明、函数名是否相同、函数的参数列表是否匹配、函数的返回值多大。

①如果  【函数的返回值<=4个字节】,则返回值通过寄存器eax带回。

②如果  【4<函数的返回值<=8个字节】,则返回值通过两个寄存器eax和edx带回。

③如果  【函数的返回值>8个字节】,则返回值通过产生的临时量带回。

6、函数结束ret指令干了两件事:先出栈;再将出栈的值放到CPU的PC寄存器中。因为PC寄存器中永远放的是下一次执行指令的地址,所以就顺理成章的在函数调用完之后依旧接着原来的代码继续执行。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值