栈帧
复习
- 调用约定:函数之间如何传参与返回
- 栈:后进先出
- 寄存器着色与溢出:部分值要放到内存
TL;DR
- 栈帧是函数调用时在栈上开辟的一块空间
- 它保存局部变量、被保护的寄存器、返回地址等
- 每次调用压入一个栈帧,返回时弹出
- 溢出到内存的变量,也放在栈帧里
正文
函数调用时,需要一块地方来安置“这次调用特有的东西”。这块空间,就是栈帧(stack frame)。它建在栈上,正好契合函数调用的“嵌套”特性。
一帧里装什么
一个栈帧通常包含:
- 返回地址:函数执行完,该回到调用处的哪里
- 保存的寄存器:按调用约定,被调函数要保护的寄存器旧值
- 局部变量:这个函数自己的局部数据
- 溢出的值:放不进寄存器、只能暂存内存的那些变量
所以,栈帧不仅装“数据”,还装“回来的路”和“现场的备份”。
调用压入,返回弹出
栈帧和函数调用是同步的:
- 调用一个函数:为它压入一个新的栈帧
- 函数执行:用的是自己这一帧里的数据
- 返回:弹出这一帧,控制权交还调用者,调用者继续用它的帧
因为函数调用是“一层套一层”的,栈帧也一层压一层——这正好是栈(后进先出)的用武之地。一个函数调用了另一个,新帧就压在旧帧上面;后者返回,就把它弹掉。
和前面的呼应
栈帧把前面好几个概念串了起来:
- 它体现调用约定(谁保存哪些寄存器)
- 它是函数调用运行栈在机器层面的具体样子
- 它收留溢出的值
到这里,“值放在哪”这件事就彻底落实了:能放寄存器的放寄存器,放不下的放栈帧。位置都定了,就可以真正动手写汇编了。
思考题 1
栈帧里通常保存哪些东西?
思考题 2
栈帧和函数调用的“嵌套”有什么关系?
小结
知识点
- 栈帧是函数调用时在栈上开辟的空间
- 内含返回地址、保存的寄存器、局部变量、溢出的值
- 调用时压入,返回时弹出
- 栈帧契合函数调用的嵌套结构
参考资料
- Wikipedia(zh):调用栈:函数调用信息的栈式存储
- Wikipedia(zh):栈帧:一次函数调用占用的栈空间
思考题答案(仅供参考)
思考题 1
通常包括:返回地址、按约定需要保护的寄存器旧值、函数的局部变量,以及无法放进寄存器、被溢出到内存的那些值。
思考题 2
函数调用是一层套一层的:调用一个函数会压入一个新栈帧,返回时再弹出。这种“后进先出”的嵌套关系正好对应栈结构,所以栈帧用栈来组织最自然。
协议
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
封面图
设计师 | 南国微雪