函式的呼叫堆疊

2021-08-10 10:36:24 字數 1929 閱讀 8614

在學習c++的過程中,有面向過程和物件導向兩種程式設計方式。對於面向過程來說,函式的書寫是最基本的,所以了解函式的呼叫過程和函式呼叫的底層原理也是必須要會的事情。

那麼函式棧幀的開闢和回退是怎麼進行的呢?

下面我們先用乙個簡單的例子,通過乙個模擬模型和反彙編來了解一下函式堆疊的呼叫。

題外話:

1、說到彙編我們要知道,彙編的**分為兩種:

一種是inter的x86彙編(從右向左看),

一種是unix上的at&t彙編(從左向右看)

2、棧上的兩個指標:在棧上開闢記憶體後,在棧上訪問變數和資料都是通過訪問ebp指標的偏移量來實現。乙個棧用兩個指標來表示,其中ebp為棧底指標,esp為棧頂指標。從下往上看,棧底是高位址,棧頂是低位址。

3、在看反彙編的**之前先介紹幾個指令的含義

(1)mov 是用來移動值的,將值壓棧

之前我們已經介紹過,在x86系統上反彙編的**要從右向左看,下面我們用乙個小例子來簡單解釋一下,之後就不再過多贅述了。

例如:mov   dword  ptr[ebp-4]  0ah    

注:dword意思是double  word 乙個字表示2個位元組,所以dword表示4個位元組

這個語句的意思是將  oah(立即數10) 移動到 ebp-4 這個位址表示的4個位元組的記憶體中

(2)lea 是用來將指標壓棧的

(3)call 是近址相對位移呼叫指令。

(4)push 入棧

(5)sub  減操作

(6)rep stos  迴圈拷貝指令

(7)pop  出棧

例子:int sum(inta,int b)

接下來我們通過結合上圖和**來總結一下函式堆疊呼叫過程

1.壓棧

呼叫方:(每次壓棧esp都向上移動)

2.將實參壓棧:從反彙編中可以看出先將 b的值  20  放到eax暫存器中,然後eax入棧,再將a的值 10 放到ecx暫存器中,然後ecx入棧。所以形參是在呼叫方開闢記憶體。從中我們可以看出,在呼叫函式時,實參是從右向左入棧

那麼為什麼會從右向左入棧呢?

我們暫且可以這樣簡單的理解,如果從左向右入棧的話,我們不知道有多少個引數,不能計算,所以這種做法不現實。

被呼叫方:

回退到呼叫方棧底。將esp 賦給 ebp 。從上圖中我們可以看到,紫色的框是呼叫方函式的棧幀。ebp壓棧的記憶體就是被呼叫方函式的棧底。

2.被呼叫函式棧幀的開闢:sub  esp  44h,對esp進行減等操作,開闢記憶體。

3.將開闢的記憶體初始化:將esp 和ebp 之間的記憶體全部初始化成0cccccccch

4.返回temp:將a = 10 和 b = 20 相加賦給temp。通過eax暫存器將temp的值帶回

2.清棧:

1.將ebp賦值給esp,我們可以看到,在清棧時並沒有將被呼叫方開闢棧幀中的資料清零,所以如果之後棧中的資料沒有被覆蓋,我們再訪問資料的話還可以訪問到

2.pop  ebp,將ebp出棧,並將出棧的元素賦給ebp,esp下移。這時ebp又回到了呼叫方的棧底。

3. 將形參變數記憶體清棧,add  esp,8

3.將eax暫存器的值賦給ret

函式的堆疊呼叫

型參在 開闢記憶體?型參的入棧順序?函式返回值怎麼帶出來?函式的返回值為什麼會回退到棧裡?函式呼叫結束為什麼會沿著呼叫點繼續執行?我們先來了解一下堆與棧是怎樣的一種存在 什麼是棧?棧用於維護函式呼叫的上下文,離開棧,函式就沒有辦法實現。棧通常在使用者空間的最高位址處分配,通常有數兆位元組大小。棧在程...

函式的堆疊呼叫

今天我們來介紹一下函式的堆疊呼叫過程,首先,我們以乙個簡單的例子進行說明 includeint sum int a,int b int main 將上面 轉到反彙編 sum函式 int sum int a,int b 00e21401 pop edi 出棧 00e21402 pop esi 00e2...

函式呼叫堆疊

一 函式呼叫堆疊 認真體會每一行指令位址!include intsum int a,int b mov ebp,esp 讓esp回退到ebp的位置,回退棧幀的過程中,沒有對棧幀中的值進行清0的操作 pop ebp 出棧並把出棧的值賦給ebp int main 下圖為上面示例函式,程式在sum函式中,...