int fun(int a, int b)
int main()
int main()
int fun(int a, int b)
0093824b pop edi
0093824c pop esi
0093824d pop ebx
0093824e mov esp,ebp
00938250
pop ebp
00938251
ret
記憶體棧基礎
函式呼叫一定是 call指令
009382
a4 call fun (93710
dh)
注意到call指令之前的幾個指令
0093829c mov eax,dword ptr [b]
0093829f push eax
009382a0 mov ecx,dword ptr [a]
009382a3 push ecx
這裡是引數入棧,而且是從右到左的順序
call指令(「呼叫」指令)的功能,就是以下兩點:
1. 將下一條指令的所在位址(即當時程式計數器pc的內容)入棧,
2. 並將子程式的起始位址送入pc(於是cpu的下一條指令就會轉去執行子程式)。
call之後,vs2010下按f11,debug進入fun函式裡面
push ebp
儲存上乙個函式棧的ebp, 也就是儲存main函式棧,可以發現main函式開頭也是這一句。(所以,每個函式都有自己的函式棧,但都是在整個記憶體棧中)
mov ebp,esp
sub esp,0cch
上一句push指令後,esp是自動往下移動了的,這乙個是把當前的esp賦值給ebp(所以,main函式的ebp,儲存了的,這裡ebp變化了,也就形成了fun函式自己的函式棧了)
sub應當是給當前函式分配棧大小的初始空間
接下來棧空間如下(灰色部分為fun函式的函式棧)
return語句,可以看到最後的返回結果存到了eax中
fun函式最後的三條指令
011c824e mov esp,ebp
011c8250 pop ebp
011c8251 ret
mov esp, ebp 就是清除fun的函式棧,現在esp指向了ebp(ebp就是原來fun函式的棧底位址)
pop ebp,就是 彈出main函式的ebp
即如下的1,2操作,接著函式棧變成了,如下圖紅色框裡面的內容
ret指令
ret指令用棧中的資料,修改ip的內容,從而實現近轉移;
cpu執行ret指令時,進行下面的兩步操作:
(ip) = ((ss)*16 +(sp))
(sp) = (sp)+2
經過上面的兩條語句後,esp指向了 上圖中[call指令下乙個指令的所在位址] 那裡, ip剛好取得位址,然後回到原來,main函式的地方。
接著 esp 增加,也就是出棧,所以 main函式棧如下
回到main函式中
011c82a4 call fun (11c710dh)
011c82a9 add esp,8
011c82ac mov dword ptr [c],eax
printf("%d\n", c);
011c82af mov eax,dword ptr [c]
011c82b2 push eax
011c82b3 push offset string "%d" (1213ee8h)
011c82b8 call @ilt+3880(_printf) (11c6f2dh)
011c82bd add esp,8
call指令之後是 add esp 8
因為有兩個引數,所以 add esp 8 ,彈出兩個int
011c82ac
movdword
ptr[c],eax
即把fun函式返回的結果 儲存到變數c中, 函式呼叫完後,一切恢復正常(fun函式棧,早就被清除掉了)
C語言小函式 棧
include include include typedef struct stack void stacknew stack s,int elemsize void stackfree stack s static void stackgrw stack s void stackpush sta...
C語言彙編 函式呼叫棧
函式呼叫大家都不陌生,呼叫者向被呼叫者傳遞一些引數,然後執行被呼叫者的 最後被呼叫者向呼叫者返回結果,還有大家比較熟悉的一句話,就是函式呼叫是在棧上發生的,那麼在計算機內部到底是如何實現的呢?對於程式,編譯器會對其分配一段記憶體,在邏輯上可以分為 段,資料段,堆,棧 段 儲存程式文字,指令指標eip...
C語言函式棧幀解析
目錄 eax,ebx,ecx ebp 存放了指向函式棧幀棧底的位址 esp 存放了指向函式棧幀棧頂的位址 函式被呼叫時,系統會在棧區為該函式開闢一塊棧空間,這個棧空間就是該函式的函式棧幀。以main函式的呼叫為例 棧幀也叫過程活動記錄,是編譯器用來實現函式呼叫過程的一種資料結構。從邏輯上講,棧幀為乙...