
栈帧是什么
不知大家接触过 “栈帧” 这个词没有,我第一次听以为是函数战争,心里怎么想也想不明白,战争(栈帧)是什么鬼???? 然后我理解后才明白 栈(Stack)简单说, 是由于函数运行而临时占用的内存区域。而帧是函数开辟的会为它在栈上建立一个帧(frame) 我们了解后,那栈帧有什么用呢? 其实我们在学习C语言的时候会用很多的疑问,比如: 1.main函数肯定是要被调用的,那是被谁调用的呢? 2.函数里面的局部变量是怎么创建的? 3.局部变量又为什么是默认是随机值?而全局变量默认却是0? 4.函数是怎么传递参数的?参数的顺序又是怎么样的? 5.函数调用详细过程是怎么样的?调用后是怎么返回的? 让我们带着上面的疑问来一步步刨析吧。
大概轮廓
既然用到了函数,那我们就写一个简单的加法函数来看看过程是怎么样的吧。代码如下:
#include <stdio.h>
int Add(int x,int y)
{
int z=0;
z=x+y;
return z;
}
int main()
{
int a=2;
int b=3;
int c=0;
c=Add(a,b);
printf("%d ",c);
return 0;
}
先说明一下我平常用的环境是vs2019,但是vs2019封装细节太多了,不容易看到整个汇编过程,所以这里我用的是 vs2010版本,由于环境不同,下面讲解的一些细节方面略有差异,但大体逻辑是不变的。 我们先来回答第一个问题,main函数是被谁调用的,  
当调试的光标移动到这里,我们按F11返回调用main函数的函数,发现了调用main函数的是 __tmainCRTStartup函数 而这个函数又是被 mainCRTStartup函数调用的。 而函数是在栈上是靠寄存器 esp(栈顶指针)和ebp(栈底指针)来维护的,并且在栈区是是先使用高地址再使用底地址的,图片分析如下:  这就是main函数和Add函数在栈区上的大概轮廓,下面再让我们带着问题刨析main函数的具体创建过程。
函数创建过程
先让我们看看main函数的汇编是怎么样的?  调试到汇编: 
接下来就是一步步的分析了,话不多说上图:  所以就解释了函数里面局部变量的创建过程,以及为什么局部变量会是随机值。 当main函数创建好变量后就要开始调用Add函数了,我们再分析又是怎么调用的 图片如下: 
总之函数的创建是一步步的压栈过程,已经ebp 和esp 指针的变化过程。 所以函数的传参过程是先从后面开始时传的,先拷贝b再拷贝a, 形参存放在实参的那个函数当中的。 并且函数的形参只是实参的一份临时拷贝,改变形参不会影响实参。
函数销毁过程
函数创建执行相对于的功能后要销毁的,那是怎么样销毁呢? 其实函数的栈帧是靠esp 和ebp 指针来维护的,我们只要把ebp 赋值给 esp那么esp 和原来压进来的ebp 又维护能上一层的函数了。分析图片如下:

好了,分析到这里整个函数的过程就大概就是这样了,大家对开始的问题是不是已经有答案了呢?欢迎大家在评论区补充和留言。 
|