调用堆栈

调用堆栈(英语: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)来获取程序的控制权。

参见