呼叫堆疊

呼叫堆疊(英語:Call stack,中國大陸稱調用堆棧,台灣稱呼叫堆疊)別稱有:執行堆疊(execution stack)、控制堆疊(control stack)、執行時堆疊(run-time stack)與機器堆疊(machine stack),是電腦科學中儲存有關正在執行的子程式的訊息的堆疊。英文有時直接簡稱「堆疊」(the stack),但堆疊中不一定僅儲存子程式訊息。幾乎所有電腦程式都依賴於呼叫堆疊,而高階語言一般將呼叫堆疊的細節隱藏至後台。

呼叫堆疊最經常被用於存放子程式的返回地址。在呼叫任何子程式時,主程式都必須暫存子程式執行完畢後應該返回到的地址。因此,如果被呼叫的子程式還要呼叫其他的子程式,其自身的返回地址就必須存入呼叫堆疊,在其自身執行完畢後再行取回。在遞歸程式中,每一層次遞歸都必須在呼叫堆疊上增加一條地址,因此如果程式出現無限遞歸(或僅僅是過多的遞歸層次),呼叫堆疊就會產生堆疊溢位

功能

呼叫堆疊的主要功能是存放返回地址。除此之外,呼叫堆疊還用於存放:

  • 本地變數:子程式的變數可以存入呼叫堆疊,這樣可以達到不同子程式間變數分離開的作用。
  • 參數傳遞:如果暫存器不足以容納子程式的參數,可以在呼叫堆疊上存入參數。
  • 環境傳遞:有些語言(如PascalAda)支援「多層子程式」,即子程式中可以利用主程式的本地變數。這些變數可以通過呼叫堆疊傳入子程式。

實例

匯編語言

以下MIPS匯編語言程式計算 ,並將結果存至暫存器s0

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再從堆疊中取回其自身的返回地址。

安全性

在較底層語言(如匯編語言C語言中),程式控制訊息與資料可能一同被存入呼叫堆疊中,因此造成安全隱患,可能允許惡意程式通過堆疊緩衝區溢位(stack buffer overflow)來獲取程式的控制權。

參見