热门问题
时间线
聊天
视角
呼叫堆疊
来自维基百科,自由的百科全书
Remove ads
呼叫堆疊(英語:Call stack,中國大陸稱調用堆棧,台灣稱呼叫堆疊)別稱有:執行堆疊(execution stack)、控制堆疊(control stack)、執行時堆疊(run-time stack)與機器堆疊(machine stack),是電腦科學中儲存有關正在執行的子程式的訊息的堆疊。英文有時直接簡稱「堆疊」(the stack),但堆疊中不一定僅儲存子程式訊息。幾乎所有電腦程式都依賴於呼叫堆疊,而高階語言一般將呼叫堆疊的細節隱藏至後台。
呼叫堆疊最經常被用於存放子程式的返回地址。在呼叫任何子程式時,主程式都必須暫存子程式執行完畢後應該返回到的地址。因此,如果被呼叫的子程式還要呼叫其他的子程式,其自身的返回地址就必須存入呼叫堆疊,在其自身執行完畢後再行取回。在遞歸程式中,每一層次遞歸都必須在呼叫堆疊上增加一條地址,因此如果程式出現無限遞歸(或僅僅是過多的遞歸層次),呼叫堆疊就會產生堆疊溢位。
Remove ads
功能
呼叫堆疊的主要功能是存放返回地址。除此之外,呼叫堆疊還用於存放:
堆疊解繞
從被呼叫函數返回會彈出堆疊的頂部幀,並可能留下一個返回值。更一般性的動作是從堆疊彈出一個或多個幀,從而恢復在程式中其它某處的執行,這叫做堆疊解繞(unwind),並且在使用了非局部控制結構的時候必須進行,比如例外處理。在這種情況下,一個函數的堆疊幀包含了指定例外處理器的一個或多個入口(entry)。在一個例外被投擲的時候,堆疊被解繞直到找到了準備處理(接住)這個投擲例外類型的一個處理器。
一些語言有要求通用解繞的其他控制結構。Pascal允許使用全域的goto陳述式,將控制從巢狀的函數傳送出來並進入此前呼叫的外面的函數。這個操作要求堆疊被解繞,恢復正確的上下文,來將控制傳送給包圍它的外面的函數中目標陳述式,需要移除多少堆疊幀就移除多少。類似的C語言有setjmp與longjmp函數充當非局部跳轉。Common Lisp允許通過使用unwind-protect特殊算子控制在堆疊被解繞時都要做些什麼。
在應用續體的時候,堆疊(在邏輯上)被解繞並重繞(rewind)上這個續體的堆疊。這不是實現續體的唯一方式,例如可以使用多個顯式的堆疊,應用一個續體可以簡單的啟用它的堆疊並盤繞(wind)上一個要傳遞的值。Scheme語言允許通過dynamic-wind在呼叫一個續體的時候,在控制堆疊「解繞」或「重繞」的特定點上,執行任意的thunk。
Remove ads
實例
main:
li $a0, 3
li $a1, 4
jal sumsq
move $s0, $v0
j mainend
sumsq:
addi $sp, $sp, -4 # 在堆疊上分配空間
sw $ra, 0($sp) # 將sumsq的返回位址存入堆疊中
jal square
move $t0, $v0
move $a0, $a1
jal square
add $v0, $v0, $t0
lw $ra, 0($sp) # 從堆疊中取回sumsq的返回位址
addi $sp, $sp, 4 # 釋出堆疊上分配的空間
jr $ra
square:
mult $a0, $a0
mflo $v0
jr $ra
mainend:
這裏,主程式(main)呼叫「sumsq」子程式並將返回地址存入暫存器ra,但是「sumsq」子程式需要呼叫「square」子程式。為保證sumsq的返回地址不被重寫,這個地址被儲存在堆疊中。在square子程式返回後,sumsq再從堆疊中取回其自身的返回地址。
Remove ads
安全性
在較底層語言(如匯編語言與C語言中),程式控制訊息與資料可能一同被存入呼叫堆疊中,因此造成安全隱患,可能允許惡意程式通過堆疊緩衝區溢位(stack buffer overflow)來獲取程式的控制權。
參見
Wikiwand - on
Seamless Wikipedia browsing. On steroids.
Remove ads