棧與呼叫慣例

2021-07-29 17:04:40 字數 2238 閱讀 4540

每個程序分配的記憶體由很多部分組成,通常稱為「段」。

1.文字段:包含了程序執行的程式二進位制機器語言指令,唯讀,可共享,因為多個程序可同時執行同一程式;

2.初始化資料段:包含顯式初始化的全域性變數和靜態變數;

3.未初始化資料段:也稱為bss段,包含未進行顯示初始化的全域性變數和靜態變數。為什麼分開放呢?主要原因在於程式在磁碟上儲存時,沒有必要為未經初始化的變數分配儲存空間,相反可執行檔案只需記錄未初始化資料段的位置及所需大小,直到執行時再由程式載入器來分配這一空間。

4.棧段:動態增長和收縮的段,由棧幀組成,系統會為每個當前呼叫的函式分配乙個棧幀,儲存函式的區域性變數、實參和返回值。動態儲存函式之間的關係,以保證被呼叫函式在返回時恢復到母函式繼續執行

5.堆:可在執行時為變數動態進行記憶體分配的一塊區域,並在用完之後歸還給堆區。

32位計算機限制了虛擬位址空間為4gb的大小。

棧在記憶體中的存放是高位址是棧底,低位址是棧頂。

記憶體的棧區實際上指的就是系統棧,系統棧由系統自動維護,它用於實現高階語言中函式的呼叫。對於類似c語言這樣的高階語言,系統棧的push、pop等棧平衡細節是透明的。一般只有在使用組合語言開發程式的時候,才需要和它直接打交道。

每乙個函式都獨佔自己的棧幀空間。當前正在執行的函式的棧幀總是在棧頂。棧駐留在記憶體的高階並向下增長(朝堆的方向)。

(1)esp

棧指標暫存器,其內存放著乙個指標,該指標永遠指向系統棧最上面乙個棧幀的棧頂

(2)ebp

基址指標暫存器,esp就是一直指向棧頂的指標,而ebp只是存放某時刻的棧頂指標,以方便對棧的操作,如取區域性變數、函式引數等。

函式棧幀函式呼叫約定描述了函式傳遞引數方式和棧幀工作方式的技術細節。不同作業系統、不同語言、不同編譯器在實現函式呼叫原理雖然基本相同,但具體的呼叫約定還是有差別的,包括引數傳遞方式、引數入棧順序是從右向左還是從左向右、函式返回時恢復堆疊平衡的操作在子函式中進行還是在母函式中進行。

1.引數入棧:將引數從右向左依次壓入系統棧中。

2.返回位址入棧:將當前**區呼叫指令的下一條指令位址壓入棧中,供函式返回時繼續執行。

3.**區調**處理器從當前**區調轉到被呼叫函式的入口處。

4.棧幀調整:

<1>儲存當前棧幀狀態值,已備後面恢復本棧幀時使用(ebp入棧)。

<2>將當前棧幀切換到新棧幀(將esp值裝入ebp,更新棧幀底部)。

<3>給新棧幀分配空間(把esp減去所需空間的大小,抬高棧頂)。

<4>對於_stdcall呼叫約定,函式呼叫時用到的指令序列大致如下:

push 引數3 ;假設該函式有3個引數,將從右向做依次入棧

push 引數2

push 引數1

call 函式位址 ;call指令將同時完成兩項工作:a)向棧中壓入當前指令位址的下乙個指令位址,即儲存返回位址。 b)跳轉到所呼叫函式的入口處。

push ebp ;儲存舊棧幀的底部

mov ebp,esp ;設定新棧幀的底部 (棧幀切換)

sub esp,*** ;設定新棧幀的頂部 (抬高棧頂,為新棧幀開闢空間)

<1>儲存返回值,通常將函式的返回值儲存在暫存器eax中。

<2>彈出當前幀,恢復上乙個棧幀。具體包括:

(1)在堆疊平衡的基礎上,給esp加上棧幀的大小,降低棧頂,**當前棧幀的空間。

(2)將當前棧幀底部儲存的前棧幀ebp值彈入ebp暫存器,恢復出上乙個棧幀。

(3)將函式返回位址彈給eip暫存器。

<3>跳**按照函式返回位址跳回母函式中繼續執行。

還是以c語言和win32平台為例,函式返回時的相關的指令序列如下:

add esp,*** ;降低棧頂,**當前的棧幀

pop ebp ;將上乙個棧幀底部位置恢復到ebp

ret ;a)彈出當前棧頂元素,即彈出棧幀中的返回位址,至此,棧幀恢復到上乙個棧幀工作完成。b)讓處理器跳轉到彈出的返回位址,恢復呼叫前**區

乙個很常見的堆疊幀如下:

i386 下的函式是這樣呼叫的:

下面是返回

下面是乙個多級呼叫形成的堆疊格局:

函式呼叫慣例

函式的呼叫方與被呼叫方對於函式如何呼叫須要乙個明確的約定,這樣的約定就叫作呼叫慣例。乙個呼叫慣例一般會規定如下幾方面的內容 1.函式引數的傳遞順序及方式 函式引數的傳遞有多種方式,常見的是通過棧傳遞。函式的呼叫方將引數壓入棧中,函式自己再從棧中取出引數。對於有多個引數的函式,呼叫慣例需要約定函式呼叫...

呼叫約定呼叫慣例

呼叫約定是呼叫方和被呼叫方對於函式如何呼叫的乙個明確的約定,只有雙方都遵守同樣的約定函式才能被正確的呼叫。int foo int n,float m 如果函式的呼叫方在傳遞引數室先壓入引數n,再壓入引數m,而函式則認為呼叫方應該先先壓入引數m,再壓入引數n,那麼在內部中m與n的值將會被交換。再者,如...

VC 中的函式呼叫慣例

我們知道在進行函式呼叫時,有幾種呼叫方法,主要分為c式,pascal式.在c和c 中c式呼叫是預設的,類的成員函式預設呼叫為 stdcall。二者是有區別的,下面我們用例項說明一下 1.cdecl c和c 預設呼叫方式 例子 void input int m,int n 相當於void cdecl ...