热门问题
时间线
聊天
视角
呼叫堆疊
来自维基百科,自由的百科全书
Remove ads
调用堆栈(英語:Call stack,台湾稱呼叫堆疊)别称有:执行栈(execution stack)、控制栈(control stack)、运行时栈(run-time stack)与机器栈(machine stack),是電腦科學中存儲有關正在執行的子程式的訊息的堆疊。英文有時直接简称“栈”(the stack),但堆疊中不一定僅存儲子程式訊息。幾乎所有電腦程式都依賴於呼叫堆疊,而高階語言一般將呼叫堆疊的細節隱藏至後台。
呼叫堆疊最經常被用於存放子程式的返回位址。在呼叫任何子程式時,主程式都必須暫存子程式執行完畢後應該返回到的位址。因此,如果被呼叫的子程式還要呼叫其他的子程式,其自身的返回位址就必須存入呼叫堆疊,在其自身執行完畢後再行取回。在遞迴程式中,每一層次遞迴都必須在呼叫堆疊上增加一條位址,因此如果程式出現無限遞迴(或僅僅是過多的遞迴層次),呼叫堆疊就會產生堆疊溢位。
Remove ads
结构

调用栈由栈帧(stack frame)组成,它们也叫做“活动记录”或“活动帧”。它们是机器依赖并且ABI依赖的数据结构,包含了子例程状态信息。每个栈帧对应于一个子例程的仍未通过返回来完成的一次调用[1]。在堆栈顶部的栈帧对应当前执行例程。例如,一个叫做DrawLine的子例程当前正在运行,它被子例程DrawSquare所调用,右侧图示中给出了调用栈的顶部状况。
呼叫堆疊的主要功能是存放返回位址。除此之外,呼叫堆疊還用於存放:
栈解绕
从被调用函数返回会弹出堆栈的顶部帧,并可能留下一个返回值。更一般性的动作是从堆栈弹出一个或多个帧,从而恢复在程序中其它某处的执行,这叫做栈解绕(unwind),并且在使用了非局部控制结构的时候必须进行,比如例外处理。在这种情况下,一个函数的栈帧包含了指定例外处理器的一个或多个入口(entry)。在一个例外被抛出的时候,堆栈被解绕直到找到了准备处理(接住)这个抛出例外类型的一个处理器。
一些语言有要求通用解绕的其他控制结构。Pascal允许使用全局性的goto语句,将控制从嵌套的函数传送出来并进入此前调用的外面的函数。这个操作要求堆栈被解绕,为了恢复正确的上下文,从而将控制传送给包围它的外面函数中的目标语句,需要移除多少栈帧就移除多少。与之类似,C语言有setjmp与longjmp函数充当非局部跳转。Common Lisp允许通过使用unwind-protect特殊算子控制在堆栈被解绕时都要做些什么。
在应用续体的时候,堆栈(在逻辑上)被解绕并重绕(rewind)上这个续体的堆栈。这不是实现续体的唯一方式,例如可以使用多个显式的堆栈,应用一个续体可以简单的激活它的堆栈并盘绕(wind)上一个要传递的值。Scheme语言允许通过dynamic-wind在调用一个续体的时候,在控制栈“解绕”或“重绕”的特定点上,分别执行任意的thunk。
Remove ads
安全性
在較底層語言(如組合語言與C語言中),程式控制訊息與資料可能一同被存入呼叫堆疊中,因此造成安全隱患,可能允許惡意程式通過栈缓冲区溢出(stack buffer overflow)來獲取程式的控制權。
參見
引用
延伸阅读
Wikiwand - on
Seamless Wikipedia browsing. On steroids.
Remove ads