热门问题
时间线
聊天
视角

呼叫堆叠

用于存储运行程序子程序的栈数据结构 来自维基百科,自由的百科全书

Remove ads

调用堆栈(英语:Call stack,台湾称呼叫堆叠)别称有:执行栈(execution stack)、控制栈(control stack)、运行时栈(run-time stack)与机器栈(machine stack),是电脑科学中存储有关正在执行的子程式的讯息的堆叠。英文有时直接简称“”(the stack),但堆叠中不一定仅存储子程式讯息。几乎所有电脑程式都依赖于呼叫堆叠,而高阶语言一般将呼叫堆叠的细节隐藏至后台。

呼叫堆叠最经常被用于存放子程式的返回位址。在呼叫任何子程式时,主程式都必须暂存子程式执行完毕后应该返回到的位址。因此,如果被呼叫的子程式还要呼叫其他的子程式,其自身的返回位址就必须存入呼叫堆叠,在其自身执行完毕后再行取回。在递回程式中,每一层次递回都必须在呼叫堆叠上增加一条位址,因此如果程式出现无限递回(或仅仅是过多的递回层次),呼叫堆叠就会产生堆叠溢位

Remove ads

功能

呼叫堆叠的主要功能是存放返回位址。除此之外,呼叫堆叠还用于存放:

  • 局部数据存储:子程式的本地变数可以存入呼叫堆叠,这样可以达到不同子程式间变数分离开的作用。
  • 参数传递:如果暂存器不足以容纳子程式的参数,可以在呼叫堆叠上存入参数。
  • 包围子程式上下文:有些语言(如PascalAda)支援嵌套子程式英语Nested function,即子程式中可以利用包围子程式的本地变数,亦称其为非本地变数英语Non-local variable。这些变数可以通过呼叫堆叠链接入子程式。

结构

Thumb

调用栈由栈帧(stack frame)组成,它们也叫做“活动记录”或“活动帧”。它们是机器依赖英语Machine-dependent software并且ABI依赖的数据结构,包含了子例程状态信息。每个栈帧对应于一个子例程的仍未通过返回来完成的一次调用[1]。在堆栈顶部的栈帧对应当前执行例程。例如,一个叫做DrawLine的子例程当前正在运行,它被子例程DrawSquare所调用,右侧图示中给出了调用栈的顶部状况。

栈解绕

Thumb
面条式堆栈,高亮显示了“活跃”栈帧。

从被调用函数返回会弹出堆栈的顶部帧,并可能留下返回的一个值。更一般性的动作是从堆栈弹出一个或多个帧,从而恢复在程序中其它某处的执行,这叫做栈解绕(unwind),并且在使用了非局部控制结构的时候必须进行,比如例外处理。在这种情况下,一个函数的栈帧包含了指定例外处理器的一个或多个入口(entry)。在一个例外被抛出的时候,堆栈被解绕直到找到了准备处理(接住)这个抛出例外类型的一个处理器。

一些语言有要求一般性解绕的其他控制结构。Pascal允许使用全局性的goto语句,将控制从嵌套的函数传送出来并进入此前调用的外面的函数。这个操作要求堆栈被解绕,为了恢复正确的上下文,从而将控制传送给包围它的外面函数中的目标语句,需要移除多少栈帧就移除多少。与之类似,C语言有setjmplongjmp函数充当非局部跳转,它们分别保存和恢复:栈指针程序计数器、被调用者保存的寄存器ABI要求的任何其他内部状态。

Common Lisp允许通过使用unwind-protect特殊算子,控制在堆栈被解绕时都要做些什么。在支持续体的编程语言比如Scheme新泽西Standard ML中,在应用续体的时候,堆栈(在逻辑上)被解绕并重绕(rewind)上这个续体的堆栈,接着盘绕(wind)上要传递的那一个值。Scheme语言提供了对续体调用进行“保护”的dynamic-wind,只要控制离开或进入它所界定范围,包括通过应用续体这种非局部跳转方式,在分别对控制栈“解绕”或“重绕”的特定点上,都会执行任意的thunk英语thunk

解绕并重饶不是实现续体的唯一方式,例如通过使用多个显式的堆栈,应用一个续体可以简单的停用或舍弃当前堆栈并激活这个续体的堆栈。在支持续体的编程语言和执行栈可以在运行时检查并修改的编程语言比如SmalltalkCilk中,由这些堆栈链接起来而实现的实际运行栈被称为面条式堆栈英语Parent pointer tree[2],它包含了变量绑定和其他环境特征。在必须支持续体的时候,一个函数的局部变量在这个函数返回时不能被销毁:一个保存了的续体可以在往后时重入(re-enter)这个函数,并且不仅期望其中的变量不变动,而且期望整个栈都存在使得这个函数能再次返回。要解决这个问题,栈帧可以在面条式堆栈结构中动态分配,并在不再有续体引用的时候,将其留作垃圾回收。这种类型的结构还解决了上行和下行的函数参数问题英语funarg problem,因为在这种基底上很容易实现头等词法闭包

安全性

在较底层语言(如组合语言C语言中),程式控制讯息与资料可能一同被存入呼叫堆叠中,因此造成安全隐患,可能允许恶意程式通过栈缓冲区溢出(stack buffer overflow)来获取程式的控制权。

参见

引用

延伸阅读

Loading related searches...

Wikiwand - on

Seamless Wikipedia browsing. On steroids.

Remove ads