當發生函式呼叫的時候,棧空間中存放的資料是這樣的:
1、呼叫者函式把被調函式所需要的引數按照與被調函式的形參順序相反的順序壓入棧中,即:從右向左依次把被調函式所需要的引數壓入棧;
2、呼叫者函式使用call指令呼叫被調函式,並把call指令的下一條指令的位址當成返回位址壓入棧中(這個壓棧操作隱含在call指令中);
3、在被調函式中,被調函式會先儲存呼叫者函式的棧底位址(push ebp)(從高內在位址–》低記憶體位址),然後再儲存呼叫者函式的棧頂位址,即:當前被調函式的棧底位址(mov ebp,esp);
4、在被調函式中,從ebp的位置處開始存放被調函式中的區域性變數和臨時變數,並且這些變數的位址按照定義時的順序依次減小,即:這些變數的位址是按照棧的延伸方向排列的,先定義的變數先入棧,後定義的變數後入棧;
所以,發生函式呼叫時,入棧的順序為:
引數n
引數n-1
引數n-2
…..
引數3
引數2
引數1
函式返回位址
上一層呼叫函式的ebp/bp
區域性變數1
區域性變數2
…. 區域性變數n
函式呼叫棧如下圖所示:
解釋:
首先,將呼叫者函式的ebp入棧(push ebp),
然後將呼叫者函式的棧頂指標esp賦值給被調函式的ebp(作為被調函式的棧底,mov ebp,esp),
此時,ebp暫存器處於乙個非常重要的位置,該暫存器中存放著乙個位址(原ebp入棧後的棧頂),
一般而言,ss:[ebp+4]處為被調函式的返回位址,
ss:[ebp+8]處為傳遞給被調函式的第乙個引數(最後乙個入棧的引數,此處假設其占用4位元組記憶體)的值,
ss:[ebp-4]處為被調函式中的第乙個區域性變數,
ss:[ebp]處為上一層ebp值;由於ebp中的位址處總是」上一層函式呼叫時的ebp值」,
而在每一層函式呼叫中,都能通過當時的ebp值」向上(棧底方向)能獲取返回位址、引數值,向下(棧頂方向)能獲取被調函式的區域性變數值」;
如此遞迴,就形成了函式呼叫棧;
函式內區域性變數布局示例:
#include
#include
struct c
;int test2(int
x, int
y, int z)
int test(int
x, int
y, int z)
int main(int argc, char** argv)
列印輸出如下:
addr x = 4288282272
addr y = 4288282276
addr z = 4288282280
addr a = 4288282260
addr b = 4288282256
addr c = 4288282252
addr st = 4288282240
addr st.a = 4288282240
addr st.b = 4288282244
addr st.c = 4288282248
a = 1; b = 2; c = 3;
a = 0; b = 0; c = 3;
示例效果圖:
該圖中的區域性變數都是在該示例中定義的;
這個中反映的是乙個典型的函式呼叫棧的記憶體布局;
函式呼叫棧
當程式進行函式呼叫的時候,系統會用到下面三種暫存器 3.ebp ebp暫存器裡儲存的是棧基址,是在函式呼叫之前,由esp賦值給ebp的。棧底方向,高位位址 call fun arg1,arg2,arg3 修改esp,棧向下增長,引數入棧,返回位址入棧 arg3 arg2 arg1 返回位址 上一層e...
棧 函式呼叫
編譯以下程式,分析此程式以得出棧的精髓 1 主函式被上層呼叫者呼叫後,執行push ebp,esp 4 因為ebp入棧 ebp值沒有改變,值得注意的是剛開始分配站的時候,第乙個入棧的是return,主函式的返回位址,然後是ebp 2 然後是mov ebp,esp,將esp的值賦給ebp,該語句未執行...
函式呼叫 函式棧
函式呼叫大家都不陌生,呼叫者向被呼叫者傳遞一些引數,然後執行被呼叫者的 最後被呼叫者向呼叫者返回結果,還有大家比較熟悉的一句話,就是函式呼叫是在棧上發生的,那麼在計算機內部到底是如何實現的呢?對於程式,編譯器會對其分配一段記憶體,在邏輯上可以分為 段,資料段,堆,棧 段 儲存程式文字,指令指標eip...