函数栈帧的创建与销毁


前言

大家在学习的时候一定有以下困惑: 局部变量是怎么创建的?为什么局部变量的值是随机值?函数是怎么传参?传参的顺序是怎样的?形参与实参是什么关系?函数调用是怎么做到的?函数调用完成不是销毁了吗,如何带回的返回值?
以上这些都可以通过了解函数栈帧的创建与销毁来理解。接下来我就带大家来了解函数栈帧的创建与销毁。
本次使用的编辑器是VS2013,因为越先进的编辑器,越不容易观察到底层。好了,我们回到正题。


一、函数栈帧是什么?

1.寄存器

寄存器里有eax,ebx,ecx,edx,ebp,esp等,我们今天要重点了解的
是ebp与esp这两个寄存器。

2.ebp与esp

这两个寄存器中存放的是地址,整两个地址使用来维护函数栈帧的。每一个函数调用,都要在栈区创建一个空间,这块空间叫做函数栈帧,用esp与ebp来维护。

二、函数栈帧的创建

1.代码块

#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("ret=%d\n", ret);
	return 0;
}

这个代码只是一个很简单的实现了加法函数,不用多说。

2.调用堆栈

接下来开始调试程序来看底层代码如何实现,首先打开调用堆栈
在这里插入图片描述
从这里可以清楚地知道,main函数之前还有两个函数在调用,所以大家平时认为的main函数就是最初的开始是错误的哦。

3.esp与ebp如何维护栈帧

在栈的使用中,是从高地址开始的。其中ebp指向栈底,esp指向栈顶。
在这里插入图片描述

main函数被调用后,程序会为main函数分配栈空间。接下来调试并右击鼠标转到反汇编。可以看到
在这里插入图片描述
这些代码的意思是
将ebp的值压栈,然后将esp赋给ebp,给esp减去0E4h的大小,之后分别将ebx,esi,edi压栈
压栈的意思是将元素放到栈顶。在上面调用堆栈可以看到main函数由__tmainCRTStartup调用。图解如下:
在这里插入图片描述
值得一提的是,在栈顶放元素是esp会自动指向新放元素的上方。
在这里插入图片描述在这里插入图片描述
上图中我们很容易的看出在调用main函数是为main函数开辟的栈空间即栈帧,并且将esp的值给ebp,ebp和esp指向同一块空间,然后esp变小指向上面的区域,接下来将ebx,esi,edi压栈。

在这里插入图片描述
在这里插入图片描述

接下来这四句,lea是加载有效地址,从上图中,我们知道ebp指向的地址,那么edi存放的就是ebp-0E4h的地址也就是③esp处的地址,最后一句rep stos dword ptr es:[edi] 意思是从edi里面重复拷贝ecx次eax的内容。一次拷贝四个字节,dword是四个字节的意思。
在这里插入图片描述
接下来这三个汇编代码的意思就是将值放入相应的空间。至于为什么是为什么是ebp-8和ebp-20,这个和编译器有关,就不过多叙述。
在这里插入图片描述
在这里插入图片描述

由汇编代码可知,接下来就开始创建形式参数,将要传递的参数值存入eax与ecx这两个寄存器中并压入栈顶,所以创建的形式参数并不在add函数的函数栈帧哦,并且我们之前常说的形参是实参的一份临时拷贝无疑是非常正确的。
在这里插入图片描述
在这里插入图片描述

接下来开始add函数调用。call是调用的意思,后面是add函数的地址用来找到调用函数。这里值得一提的是call指令下一条指令的地址被存储来方便执行完add函数可以跳回来。
Add函数创建栈帧的过程其实和main函数一样的,先将ebp压栈方便找到指向main函数栈底的空间,再将esp的地址存放到ebp里面,此时,esp和ebp指向同一位置,再将esp上移0CCh个位置,然后就是ebx,esi,edi压栈,这里不多作说明,由下图可清晰理解:
在这里插入图片描述
在这里插入图片描述
接下来初始化z与执行加法也没什么要说的,z=x=y是找到之前创建的形参来进行加法。并将结果移动到z里。
在这里插入图片描述
返回z的操作是将z的值存放到eax这个寄存器中,所以函数销毁变量与返回的值这个操作并不冲突。
在这里插入图片描述
最后销毁add函数栈帧,pop就是弹出元素然后把元素放入后面的寄存器,每次pop都会销毁一个栈顶元素然后esp自动++往下挪移。三下pop之后要回收空间了,操作是把ebp的值传给esp这样esp指向的空间是main函数的栈顶,然后popebp这个操作会将之前栈底存放的之前存放的main函数的栈底指针传给ebp,这样ebp就指向了main函数的栈底。如下图:在这里插入图片描述
接下来销毁main函数的操作也与之前一样,就不细说了。


总结

以上就是今天要讲的内容,当然以上是我个人的理解,如有纰漏和错误的地方,还请各方大神指正,感激不尽!
  • 7
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值