1. 程式人生 > >函式棧幀圖解

函式棧幀圖解

函式棧幀圖解

2016年11月05日 11:55:09 stay_the_course 閱讀數:4715 標籤: 函式結構指標記憶體 更多

個人分類: 原創

本博文環境:Windows 7,64bit,visual studio 2015 
我們知道記憶體空間大致可以用下圖表示: 
記憶體空間: 
而函式在呼叫的時候都是在棧空間上開闢一段空間以供函式使用,所以下面來詳細談一談函式的棧幀結構。 
如圖示,棧是由高地址向地地址的方向生長的,而且棧有其棧頂和棧底,在x86系統的CPU中,暫存器ebp儲存的是棧底地址,稱為幀指標,暫存器esp儲存的是棧頂地址,稱為棧指標。而且還應該明確一點,棧指標和幀指標一次只能儲存一個地址,所以,任何時候,這一對指標指向的是同一個函式的棧幀結構。並且ebp一般由系統改變它的值,而esp會隨著資料的入棧和出棧而移動,也就是說esp始終指向棧頂。在明白了這些之後,下面我們來看一個具體的例子: 
`看這段簡單的程式碼:這裡有兩個函式:main、fun,其中,main函式呼叫fun函式來求兩個數的和

 
就像前面說的,函式在執行的時候,是在棧上開闢空間,也就是第一幅圖的綠色部分,而且我們知道,程式是逐句執行的,變數是逐個被定義和分配記憶體空間的,這也是棧指標是移動的的原因,下面我們逐句執行這段程式碼(VS2015環境下),來看main函式和fun函式的棧幀結構的變化(注:為方便理解,下面的圖都是可以按順序直接拼接的): 
這裡寫圖片描述

在這裡我們可以看到變數的定義分配記憶體順序便是先定義先分配,結合組合語言我們可以看得更清楚: 
這裡寫圖片描述 
然後,程式繼續執行進入fun函式,這裡我們來看組合語言: 
這裡寫圖片描述 
這裡寫圖片描述
這裡我們主要要明白兩點,(1)call指令的兩個功能,(2)上圖中紅色的圈1,圈2的目的。call指令的功能上圖描述的很清楚,這裡不再贅述,圈1目的是將main函式的pc壓入堆疊,因為pc指標裡面放的始終是下一條要執行指令的地址,這裡把pc壓入堆疊進行保護,是因為在執行fun函式的時候pc指標裡面放的將會是fun函式需要執行裡面的內容,同樣的道理,我們知道某一時刻只能有一個棧幀結構,當要去到fun函式的時候,暫存器ebp就要儲存fun函式的幀指標,所以main函式的幀指標就要儲存起來,檢視main函式的組合語言也可以清楚的看到這一點: 
這裡寫圖片描述

 
然後進入到fun函式:

總結一下就是這樣: 
畫在一起就是這樣的 
在fun函式執行完成之後,會返回到main函式,這裡要做的就是出棧,也就是就在形成fun函式之前壓入堆疊進行保護的pc指標,main函式的ebp彈出堆疊,重新形成或者說還原main函式的棧幀結構。