函式呼叫過程(棧幀)

2021-08-17 17:09:14 字數 2035 閱讀 8790

眾所周知,程式每呼叫乙個函式,系統都會為其開闢一塊空間,當它返回時,才收回這塊空間。(程式崩潰有一部分原因就是因為無限制的呼叫函式,卻沒有及時返回,導致記憶體空間不夠。)

為了更好的維護這一塊空間(通常稱為棧空間),我們需要了解兩個暫存器,乙個為 esp (指向棧頂的指標),乙個為 ebp (指向棧底的指標)。棧空間的位址是從高位址向低位址使用的,即先使用高位址的空間,再使用低位址的空間。

main()函式也是函式,它也有返回值,「return 0;」,所以main函式也被其他函式呼叫。用過呼叫堆疊,可以查到,呼叫main()函式的是 __tmaincrtstartup() 函式; __tmaincrtstartup() 函式也被 maincrtstartup() 函式呼叫。

每個程式開始執行的時候,都會先呼叫 maincrtstartup() 函式和 __tmaincrtstartup() 函式,然後才呼叫 main() 函式,執行程式所寫的**。當然這都是編譯器為我們做的事情,我們只需要了解一下,就可以了。

我們用一段比較簡單的**來進行演示:

#includeint sum(int x, int y)

int main()

該程式實現了用呼叫函式求兩個數相加之和。我們按f11執行程式,右鍵進入反彙編:

可以看到,在進入main()函式的時候,並不是先建立整形變數a,而是執行 push  ebp,即將現在ebp所指向的資料壓入棧頂。現在ebp所指的是 __tmaincrtstartup() 函式的棧底,所以這句執行的結果就是儲存 __tmaincrtstartup() 函式的棧底,這是為了當main()函式執行完之後返回用的。    

sub esp,0e4h:sub是彙編指令的減法,這條語句意思是esp指標減去0e4h所得結果重新賦給esp,即esp指標向低位址移動了0e4h大小的空間,也就是目前為止,系統為main()函式開闢了0e4h大小的空間。(並不是所有的main()函式開闢空間的大小都為0e4h)。

下面執行三次push壓棧指令,把三個暫存器壓入棧頂,這三個暫存器會分別記錄不同的資訊,以便更好地維護這片空間。

下面兩條mov指令,是給兩個暫存器賦值,把39h賦給ecx,把0cccccccch賦給eax。這兩條指令都是為下邊這條指令做鋪墊的。

rep stos:重複儲存,從edi開始,每次儲存四個位元組,賦值ecx次,賦值內容是eax。也就是把0e4h空間裡面全部初始化為隨機值。

到目前為止,上面所做的都是函式呼叫所產生的的系統開銷。

後面就是建立變數a,b,ret。之後在進入函式之前,就會把a,b的形參放到暫存器裡並且壓入棧頂。

進入sum函式後,前十條指令和main()函式並無太大區別,只是開闢的空間大小不同而已,都是執行相同的操作。然後再暫存器裡面進行+操作,並把結果送到變數z裡面。當函式結束,返回的時候,把需要返回的值,放到eax暫存器裡面,再把建立函式的,壓到棧頂的三個暫存器依次彈出。

pop ebp:把棧頂空間的東西彈出放到ebp裡面,此時棧頂存放的是原來main()函式的ebp,所以執行完這條指令,ebp指標指向的便是main()函式的底部。    

之後是銷毀形參,並且像sum()函式一樣,一步步出棧,返回空間。

以上為我對函式呼叫過程中,棧幀的分析,如果有理解錯誤的地方,望指正。

函式棧幀(呼叫過程)

函式棧幀就是在呼叫函式是為其在棧空間上開闢了一段空間,指向過程呼叫,乙個過程呼叫包括將資料 以過程引數和返回值的形式 和控制從 的一部分傳遞到另一部分。我們以以下 為例講解整個函式呼叫過程 int my add int x,int y int main 一 呼叫main 函式 我們從main 函式的...

函式呼叫過程 棧幀 呼叫約定

考慮函式呼叫 func 1,2 需要把1和2這兩個引數進行傳遞,這裡的引數傳遞可以通過兩種方式 1.引數入棧 記憶體 2.引數傳遞到暫存器 大多數情況下,也是c c 的預設形式是通過棧進行傳遞,因為雖然暫存器傳遞方式快但是暫存器數量有限 引數壓入棧中 記憶體 cs ip指向下一條指令位址需要進行跳轉...

函式的呼叫過程 棧幀

在談棧幀之前,我們必須要先知道c c 程式記憶體的分配情況。乙個由c c 編譯的程式占用的記憶體分為以下幾個部分 1 棧區 stack 由編譯器自動分配釋放 存放為執行函式而分配的局 部變數 函式引數 返回資料 返回位址等。其操作方式類似於資料結構中的 棧。2 堆區 heap 一般由程式設計師分配釋...