【C语言】函数栈帧的创建与销毁
目录
一、两个特别的寄存器
二、在函数调用时发生的事
先开辟空间
再进行实参与形参的传递
然后执行函数的调用
之后将返回值存储在寄存器中(eax)带回去
最后通过留下的锚点返回调用函数的位置
在不同的编译器下,函数调用过程中栈帧的创建是略有差异的
栈区使用时是从高地址向低地址使用
堆区使用时是从低地址向高地址使用
一、两个特别的寄存器
每一个函数的调用都要在栈区开辟一块空间,栈区的使用习惯是从高地址往低地址使用
栈底指针寄存器ebp和栈顶指针寄存器esp这两个寄存器中存放的是地址,这两个地址是用来指明当前函数所在的内存空间的底和顶,当前程序正在执行哪个函数,ebp和esp就维护哪个函数的栈帧
- ebp:栈底指针寄存器
- esp:栈顶指针寄存器,在正常使用时每压入一个数据,指针指向就会往低地址移动一位
二、在函数调用时发生的事
先开辟空间
- 栈底指针ebp压入上一个函数所开辟的栈,此时栈顶指针esp指向栈底指针ebp
- 将栈底指针ebp指向当前的栈顶指针esp,此时出现了新的空间的栈底
- 栈顶指针esp减去一个值(由编译器决定),即栈顶指针往低地址移动,此时出现了新的空间的栈顶
由此为当前调用的函数开辟出了一块新的内存空间
再将所开辟的空间初始化(这也是为什么一个变量如果不初始化直接拿来使用可能会出问题的原因)
然后在所开辟的空间内为变量分配空间,之后该空间可以自由存储值(如果在创建变量时不初始化,那么这个变量的默认值就是内存空间初始化后存放的值,比如VS里的0xcccccccc)
如果创建多个变量,那么变量会从栈底(高地址)依次向栈顶(高地址)使用先前所开辟的空间
再进行实参与形参的传递
实参变量创建好后,将实参地址下的值再创建一份压入为主函数开辟的栈区成为形参空间,然后继续压入程序目前的栈底地址和创建形参变量之前的栈顶地址(为了方便后续函数的返回和形参空间的释放)
然后执行函数的调用
先重复空间的开辟和初始化,再将main函数栈顶的备份实参值(即形参)读取出来(例如:ebp+8,读本次函数空间栈底再往高的地址,即前一个调用该函数的空间下的值)进行运算
之后将返回值存储在寄存器中(eax)带回去
因为函数变量的空间离开函数就销毁了,所以借用寄存器存储返回值
最后通过留下的锚点返回调用函数的位置
并且将被调用函数的空间释放掉,将eax存储的返回值赋给main函数用于接收的变量