首頁 > 軟體

C語言函數棧幀的建立與銷燬詳解

2022-02-21 13:06:05

前言

大家在學習的時候一定有以下困惑: 區域性變數是怎麼建立的?為什麼區域性變數的值是隨機值?函數是怎麼傳參?傳參的順序是怎樣的?形參與實參是什麼關係?函數呼叫是怎麼做到的?函數呼叫完成不是銷燬了嗎,如何帶回的返回值?

以上這些都可以通過了解函數棧幀的建立與銷燬來理解。接下來我就帶大家來了解函數棧幀的建立與銷燬。

本次使用的編輯器是VS2013,因為越先進的編輯器,越不容易觀察到底層。好了,我們回到正題。

一、函數棧幀是什麼?

1.暫存器

暫存器裡有eax,ebx,ecx,edx,ebp,esp等,我們今天要重點了解的是ebp與esp這兩個暫存器。

2.ebp與esp

這兩個暫存器中存放的是地址,整兩個地址使用來維護函數棧幀的。每一個函數呼叫,都要在棧區建立一個空間,這塊空間叫做函數棧幀,用esp與ebp來維護。

二、函數棧幀的建立

1.程式碼塊

#include<stdio.h>
int Add(int x, int y)
{
	int z = 0;
	z = x + y;
	return z;
}
int main()
{
	int a = 10;
	int b = 20;
	int ret = Add(a, b);
	printf("ret=%dn", ret);
	return 0;
}

這個程式碼只是一個很簡單的實現了加法函數,不用多說。

2.呼叫堆疊

接下來開始偵錯程式來看底層程式碼如何實現,首先開啟呼叫堆疊

從這裡可以清楚地知道,main函數之前還有兩個函數在呼叫,所以大家平時認為的main函數就是最初的開始是錯誤的哦。

3.esp與ebp如何維護棧幀

在棧的使用中,是從高地址開始的。其中ebp指向棧底,esp指向棧頂。

main函數被呼叫後,程式會為main函數分配棧空間。接下來偵錯並右擊滑鼠轉到反組合。可以看到

這些程式碼的意思是

將ebp的值壓棧,然後將esp賦給ebp,給esp減去0E4h的大小,之後分別將ebx,esi,edi壓棧

壓棧的意思是將元素放到棧頂。在上面呼叫堆疊可以看到main函數由__tmainCRTStartup呼叫。圖解如下:

值得一提的是,在棧頂放元素是esp會自動指向新放元素的上方。


上圖中我們很容易的看出在呼叫main函數是為main函數開闢的棧空間即棧幀,並且將esp的值給ebp,ebp和esp指向同一塊空間,然後esp變小指向上面的區域,接下來將ebx,esi,edi壓棧。

接下來這四句,lea是載入有效地址,從上圖中,我們知道ebp指向的地址,那麼edi存放的就是ebp-0E4h的地址也就是③esp處的地址,最後一句rep stos dword ptr es:[edi] 意思是從edi裡面重複拷貝ecx次eax的內容。一次拷貝四個位元組,dword是四個位元組的意思。

接下來這三個組合程式碼的意思就是將值放入相應的空間。至於為什麼是為什麼是ebp-8和ebp-20,這個和編譯器有關,就不過多敘述。

由組合程式碼可知,接下來就開始建立形式引數,將要傳遞的引數值存入eax與ecx這兩個暫存器中並壓入棧頂,所以建立的形式引數並不在add函數的函數棧幀哦,並且我們之前常說的形參是實參的一份臨時拷貝無疑是非常正確的。

接下來開始add函數呼叫。call是呼叫的意思,後面是add函數的地址用來找到呼叫函數。這裡值得一提的是call指令下一條指令的地址被儲存來方便執行完add函數可以跳回來。

Add函數建立棧幀的過程其實和main函數一樣的,先將ebp壓棧方便找到指向main函數棧底的空間,再將esp的地址存放到ebp裡面,此時,esp和ebp指向同一位置,再將esp上移0CCh個位置,然後就是ebx,esi,edi壓棧,這裡不多作說明,由下圖可清晰理解:

接下來初始化z與執行加法也沒什麼要說的,z=x=y是找到之前建立的形參來進行加法。並將結果移動到z裡。

返回z的操作是將z的值存放到eax這個暫存器中,所以函數銷燬變數與返回的值這個操作並不衝突。

最後銷燬add函數棧幀,pop就是彈出元素然後把元素放入後面的暫存器,每次pop都會銷燬一個棧頂元素然後esp自動++往下挪移。三下pop之後要回收空間了,操作是把ebp的值傳給esp這樣esp指向的空間是main函數的棧頂,然後popebp這個操作會將之前棧底存放的之前存放的main函數的棧底指標傳給ebp,這樣ebp就指向了main函數的棧底。如下圖:

接下來銷燬main函數的操作也與之前一樣,就不細說了。

總結 

本篇文章就到這裡了,希望能夠給你帶來幫助,也希望您能夠多多關注it145.com的更多內容!        


IT145.com E-mail:sddin#qq.com