C語言函式呼叫及棧幀結構

2021-08-15 17:11:11 字數 3123 閱讀 9620

原文:

一、位址空間與物理記憶體 

(1)位址空間與物理記憶體是兩個完全不同的概念,真正的**及資料都存在物理記憶體中。 

物理儲存器是指實際存在的具體儲存器晶元,cpu在操縱物理儲存器的時候都把他們當做記憶體來對待,把他們看成由若干個儲存單元組成的邏輯儲存器,這個邏輯儲存器就是我們所說的位址空間。 

位址空間大小與邏輯儲存器大小不一定相等。 

(2)程序的位址空間分布 

程序的位址空間包括:棧區(heap)、共享區、堆區(stack)、未初始化靜態全域性區、已初始化靜態全域性區、靜態唯讀區、**段。如圖: 

二、棧幀的建立 

首先要明白幾個地方: 

每乙個函式都有自己的棧幀空間,並且獨佔自己的棧幀空間, 

當前正在執行的函式的棧幀總是在棧頂。win32系統提供兩個特殊的暫存器用於標識位於系統棧頂端的棧幀。

hljs axapta has-numbering" style="display:block; padding:0px; font-family:consolas,inconsolata,courier,monospace; line-height:22px; overflow-x:auto; word-wrap:normal">int fun(int a,int b)

int main()

這裡main是呼叫者(caller);fun是被呼叫者(callee)在函式呼叫前,main正在用esp和ebp暫存器指示它自己的棧幀。 

main把eax,ecx和edx壓棧。這是乙個可選的步驟,只在這三個暫存器內容需要保留的時候執行此步驟。 

接著,main把傳遞給fun的引數一一進棧,最後的引數最先進棧,這裡也就解釋了函式在壓棧過程中是從右往左,即先壓4,再壓3。 

(1)這裡首先main函式建立自己的棧幀結構;main()函式是由—__tcrtstartup()函式呼叫的,所以maincrtstratup()函式呼叫__tmaincrtstra()函式的時候就會從棧上為__tmaincrtstra()分配類似圖中這麼一塊空間,因為我們現在要呼叫main()函式了,所以當然要先把__tmaincrtstartup()函式的執行狀態儲存下來,這樣main()函式才能返回的時候才能找得到!。 

然後繼續執行下一條語句: mov ebp,esp 

即把esp的值賦給ebp,這樣,ebp也就指向了現在esp的位置 

然後sub esp 0c0h 這樣就為main函式開闢了一段空間然後將ebx、esi、edi暫存器壓棧就形成如圖所示: 

緊接著將區域性變數及實參壓棧,並執行call指令,main用call指令呼叫子函式: 

call fun 

當call指令執行的時候,eip指令指標暫存器的內容被壓入棧中。因為eip暫存器是指向main中的下一條指令,所以現在返回位址就在棧頂了。在call指令執行完之後,下乙個執行週期將從名為fun的標記處開始. 

當函式fun,也就是被呼叫者取得程式的控制權,它必須做3件事:建立它自己的棧幀,為區域性變數分配空間,最後,如果需要,儲存暫存器ebx,esi和edi的值。 

首先fun必須建立它自己的棧幀。ebp暫存器現在正指向main的棧幀中的某個位置,這個值必須被保留,因此,ebp進棧。然後esp的內容賦值給了 ebp。這使得函式的引數可以通過對ebp附加乙個偏移量得到,而棧暫存器esp便可以空出來做其他事情。如此一來,幾乎所有的c函式都由如下兩個指令開 始: 

push ebp 

mov ebp, esp 

下一步,fun必須為它的區域性變數分配空間,同時,也必須為它可能用到的一些臨時變數分配 空間。比如,foo中的一些c語句可能包括複雜的表示式,其子表示式的中間值就必須得有地方存放。這些存放中間值的地方同城被稱為臨時的,因為他們可以為 下乙個複雜表示式所復用 

現在,區域性變數和臨時儲存都可以通過基準指標ebp加偏移量找到了。 

最後,如果fun用到ebx,esi和edi暫存器,則它必須在棧裡儲存它們。

fun的函式體現在可以執行了。這其中也許有進棧、出棧的動作,棧指標esp也會上下移動,但ebp是保持不變的。這意味著我們可以一直用[ebp+…]找到第乙個引數,而不管在函式中有多少進出棧的動作。 

函式fun的執行也許還會呼叫別的函式,甚至遞迴地呼叫foo本身。然而,只要ebp暫存器在這些子呼叫返回時被恢復,就可以繼續用ebp加上偏移量的方式訪問實際引數,區域性變數和臨時儲存。 

緊接著當被呼叫者執行完畢時將消除棧幀結構,呼叫pop指令。

在把程式控制權返還給呼叫者前,被呼叫者foo必須先把返回值儲存在eax暫存器中。其次,foo必須恢復ebx,esi和edi暫存器的值。進棧和出棧操作的次數必須保持平衡。 

在程式控制權返回到呼叫者main)後,這時,傳遞給fun的引數通常已經不需要了。我們可以把引數一起彈出棧,這可以通過把棧指標實現: 

add esp, 8 

此時fun函式呼叫結束棧幀結構恢復至圖一。 

如果在函式呼叫前,eax,ecx和edx暫存器的值被儲存在棧中,呼叫者main函式現在可以把它們彈出。這個動作之後,棧頂就回到了我們開始整個函式呼叫過程前的位置。 

這樣整個函式的呼叫就結束了 

C語言 函式呼叫過程(棧幀)

首先舉個栗子 include int add int x,int y int main 在這個程式裡,函式被呼叫才會發揮函式的功能,而函式的呼叫其實是乙個過程,在這個過程計算機要為函式開闢棧空間,用於本次函式臨時變數的儲存和現場保護,這塊空間稱為函式的棧幀。現場保護的作用是為了在呼叫完另乙個函式,返...

C語言函式呼叫過程 棧幀

在學習過函式宣告和定義,了解了函式的引數 實參 形參 引數的設計 函式的使用等一些函式基礎知識之後,函式逐漸變為我們編寫 時重要工具。無論是編寫時引用的庫函式,還是實現程式部分功能時使用的自定義函式,都體現函式的重要性。函式特點 使 開發更高效 提高 復用性 使 邏輯更加清晰。函式所佔據的重要地位,...

C語言 深度理解函式的呼叫(棧幀)

本文講解的函式呼叫棧幀,需要用到彙編。又不知道同學可以自學一下。我們可以知道函式的呼叫是乙個過程,我們通常將這個過程稱之為函式的呼叫過程。從邏輯上講,棧幀就是乙個函式執行的環境 函式引數 函式的區域性變數 函式執行完後返回到 等等。這個過程要為函式開闢棧空間,於本次函式的呼叫中臨時變數 的儲存 現場...