程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> 函數調用棧和棧幀

函數調用棧和棧幀

編輯:關於C語言
 

在計算機科學中,Callstack 是指存放某個程序的正在運行的函數的信息的棧。Call stack 由 stack frames 組成,每個 stack frame 對應於一個未完成運行的函數。

在當今流行的計算機體系架構中,大部分計算機的參數傳遞,局部變量的分配和釋放都是通過操縱程序棧來實現的。棧用來傳遞函數參數,存儲返回值信息,保存寄存器以供恢復調用前處理機狀態。每次調用一個函數,都要為該次調用的函數實例分配棧空間。為單個函數分配的那部分棧空間就叫做 stack frame,也就是說,stack frame 這個說法主要是為了描述函數調用關系的。

Stack frame 組織方式的重要性和作用體現在兩個方面:第一,它使調用者和被調用者達成某種約定。這個約定定義了函數調用時函數參數的傳遞方式,函數返回值的返回方式,寄存器如何在調用者和被調用者之間進行共享;第二,它定義了被調用者如何使用它自己的 stack frame 來完成局部變量的存儲和使用。


|------------------|<--high address
| ...... | Previous
| ...... | frames
|------------------|<-----------
| registers and |
| local variables |
|------------------| Caller's
| argument 6 |
| argument 5 | frame
| argument 4 |
| argument 3 |
| argument 2 |
| argument 1 |
|------------------|<--previous sp
| return address |(n-4)sp
|------------------|(n-8)sp
| saved registers |
| ...... | Current
|------------------| frame
| local variables |(Callee's
| ...... | frame )
|------------------|
| next calling |
| arguments list |
|------------------|<--sp
| ...... |
|__________________|<--low address


圖片鏈接:http://solrex.googlepages.com/frame.jpg

上圖描述的是一種典型的(MIPS O32)嵌入式芯片的 stack frame 組織方式。在這張圖中,計算機的棧空間采用的是向下增長的方式,SP(stack pointer) 就是當前函數的棧指針,它指向的是棧底的位置。Current Frame 所示即為當前函數(被調用者)的 frame ,Caller’s Frame 是當前函數的調用者的 frame 。每個 frame 中所存放的內容和存放順序,則由目標體系架構的調用約定(calling convention)定義。如圖所示,MIPS O32調用約定規定了所占空間不大於4 個字節的參數應該放在從 $4到 $8 的寄存器中,剩下的參數應該依次放到調用者 stack frame 的參數域中,並且在參數域中需要為前四個參數保留棧空間;如果被調用者需要使用 $16 到 $23 這些保留寄存器(saved register),就必須先將這些保留寄存器的值保存在被調用者 stack frame 的保留寄存器域中,當被調用者返回時恢復這些寄存器值;當被調用者不是葉子函數時,即被調用者中存在對其它函數的調用,需要將 RA(return address) 寄存器 ($31) 值保存到被調用者 stack frame 的返回值域中;被調用者所需要使用的局部變量,應保存在被調用者 stack frame 的本地變量域中。

在沒有 BP(base pointer) 寄存器的目標架構中,進入一個函數時需要將當前棧指針向下移動 n 字節,這個大小為 n 字節的存儲空間就是此函數的 stack frame 的存儲區域。此後棧指針便不再移動,只能在函數返回時再將棧指針加上這個偏移量恢復棧現場。由於不能隨便移動棧指針,所以寄存器壓棧和出棧都必須指定偏移量,這與 x86 架構的計算機對棧的使用方式有著明顯的不同。

在 RISC 計算機中主要參與計算的是寄存器,saved registers 就是指在進入一個函數後,如果某個保存原函數信息的寄存器會在當前函數中被使用,就應該將此寄存器保存到堆棧上,當函數返回時恢復此寄存器值。而且由於 RISC 計算機大部分采用定長指令或者定變長指令,一般指令長度不會超過32個位。而現代計算機的內存地址范圍已經擴展到 32 位,這樣在一條指令裡就不足以包含有效的內存地址,所以RISC計算機一般借助於一個返回地址寄存器 RA(return address) 來實現函數的返回。幾乎在每個函數調用中都會使用到這個寄存器,所以在很多情況下 RA 寄存器會被保存在堆棧上以避免被後面的函數調用修改,當函數需要返回時,從堆棧上取回 RA 然後跳轉。移動 SP 和保存寄存器的動作一般處在函數的開頭,叫做 function prologue;恢復這些寄存器狀態的動作一般放在函數的最後,叫做 function epilogue。

 
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved