在《从汇编层看64位程序运行——程序中的栈(Stack)结构及其产生的历史原因》一文中,我们讲解了X86体系架构下,程序的栈结构的特点。本文将介绍另外一个非常重要的结构——栈帧。
A stack frame, often just called a ‘frame,’ is a section of the stack dedicated to a particular function call. The stack is a region of memory that supports push and pop operations and is used to store data in a Last-In-First-Out (LIFO) manner.
栈帧是栈上一个抽象的结构,即栈上一个起始地址和截止地址中间的一段内存块。
在我们学习一些底层语言时,往往会讲到一些局部变量是分配在栈上的。一个函数所有的局部变量以及一些辅助信息,构成了这个函数的栈帧。
我们先通过一个例子来熟悉下栈帧。
下面代码非常简单,main函数调用了foo函数。
void foo() {
int a = 10;
a = a + 5;
}
int main() {
foo();
return 0;
}
启动程序时,代码还没执行,所以我们通过info stack指令看到没有栈帧。
然后我们给main函数下断点,让其断在main的起始代码处。此时foo函数没有被调用,于是只有main函数的栈帧——即只有一个栈帧。
然后给foo函数下断点,让程序运行到这个函数的入口处,然后查看栈帧的变化
可以看到此时有两个栈帧,因为main函数和foo函数都在运行态。
然后我们让foo函数执行完毕,返回到main函数中。再观察栈帧的变化
此时我们看到,栈帧又变成1个。
通过上面这个例子,我们可以看到:
- 栈帧是一个虚拟概念,它存在于栈上。
- 一次函数调用会产生一个栈帧。
- 函数返回后,对应的栈帧会消失。
- 不管栈帧创建还是消失,变化的只是这个概念性的逻辑单元。其对应栈上的值,不会因为这些变化而自动变化,需要函数逻辑进行值覆盖。