前期学习时,我们可能有很多困惑,
比如:局部变量是怎么创建的?函数是怎么传参的?传参的顺序是怎样的?形参和实参是什么关系?函数调用时怎么做的?函数调用时结束后怎么返回的?
这些问题在了解了函数栈帧的创建和销毁就能全部解决了。
在不同的编译器下,函数调用过程中栈帧的创建是略有差异的。
本篇文章参考了这本书。
[Wil06] Rob Williams. Computer Systems Architecture: A Networking Approach. Prentice Hall, 2nd edition.
1. 什么是栈帧(Stack frame)
- 栈中存放与一个子程序调用有关的所有数据的区域被称为栈帧。
- 这些数据包括
- 子程序的参数。
- 返回地址。
- 局部变量。
2. 寄存器
首先要了解两个寄存器EBP
, ESP
, 这两个寄存器中存放的是地址,这两个地址是用来维护函数栈帧的。
-
每调用一个函数,都要在栈区开辟一个空间。
-
由于嵌套调用,几个栈
帧可能同时存在。 -
EBP
: 栈低指针。- 用于指示当前栈帧的底部。
-
ESP
: 栈顶指针。- 用来保存堆栈顶部的地址。
3. 栈帧是如何工作的
ESP
总是指向堆栈的顶部。
EBP
最初包含一个堆栈底层的地址。
栈底的地址。
-
在调用一个子程序之前和期间,会发生以下情况
发生以下情况:-
参数被压到栈上。
-
返回地址被压到栈上。
-
存储在
EBP
中的地址被压到栈上。 -
一个新的栈帧被创建。
-
新的栈帧顶部的地址被保存在EBP中。
-
局部变量被传到新的栈上。
-
-
一旦子程序完成其工作
- 将所有局部变量从栈中弹出。
- 从栈顶部弹出先前的EBP的地址,并将其恢复到EBP中。
- 清理堆栈中的参数。
- 弹出返回地址并保存在EIP中。
-
注意:弹出顺序是至关重要的。
可以用vs反编译试试看,参考下面文章
[想要自己操作一遍的可以看看这篇博客函数栈帧的创建与销毁(超详解)