C语言的函数调用过程(栈帧的创建与销毁)
從匯編的角度解析函數(shù)調用過程
看看下面這個簡單函數(shù)的調用過程:
1 int Add(int x,int y)2 {3 int sum = 0;4 sum = x + y;5 return sum;6 }7 8 int main ()9 { 10 int a = 10; 11 int b = 12; 12 int ret = 0; 13 ret = Add(a,b); 14 return 0; 15 }今天主要用匯編代碼去講述這個過程,首先介紹幾個寄存器和簡單的匯編指令的意思。?
先看幾個函數(shù)調用過程涉及到的寄存器:?
(1)esp:棧指針寄存器(extended stack pointer),其內存放著一個指針,該指針永遠指向系統(tǒng)棧最上面一個棧幀的棧頂。?
(2)ebp:基址指針寄存器(extended base pointer),其內存放著一個指針,該指針永遠指向系統(tǒng)棧最上面一個棧幀的底部。?
(3)eax 是”累加器”(accumulator), 它是很多加法乘法指令的缺省寄存器。?
(4)ebx 是”基地址”(base)寄存器, 在內存尋址時存放基地址。?
(5)ecx 是計數(shù)器(counter), 是重復(REP)前綴指令和LOOP指令的內定計數(shù)器。?
(6)edx 則總是被用來放整數(shù)除法產(chǎn)生的余數(shù)。?
(7)esi/edi分別叫做”源/目標索引寄存器”(source/destination index),因為在很多字符串操作指令中, DS:ESI指向源串,而ES:EDI指向目標串.?
在32位平臺上,ESP每次減少4字節(jié)。?
再看幾條簡單的匯編指令:?
mov :數(shù)據(jù)傳送指令,也是最基本的編程指令,用于將一個數(shù)據(jù)從源地址傳送到目標地址(寄存器間的數(shù)據(jù)傳送本質上也是一樣的)?
sub:減法指令?
lea:取偏移地址?
push:實現(xiàn)壓入操作的指令是PUSH指令?
pop:實現(xiàn)彈出操作的指令?
call:用于保存當前指令的下一條指令并跳轉到目標函數(shù)。?
這些指令當然能看懂最好,可以讓你很深刻的理解函數(shù)調用過程,不能看懂就只能通過我的描述去理解了。?
進行分析之前,先來了解下內存地址空間的分布:?
棧空間是向低地址增長的,主要是用來保存函數(shù)棧幀。 棧空間的大小很有限,僅有區(qū)區(qū)幾MB大小?
匯編代碼實現(xiàn):?
main函數(shù)匯編代碼:
Add函數(shù)匯編代碼:
int Add(int x,int y) { 011B26A0 push ebp 011B26A1 mov ebp,esp 011B26A3 sub esp,0CCh 011B26A9 push ebx 011B26AA push esi 011B26AB push edi 011B26AC lea edi,[ebp-0CCh] 011B26B2 mov ecx,33h 011B26B7 mov eax,0CCCCCCCCh 011B26BC rep stos dword ptr es:[edi] int sum = 0; 011B26BE mov dword ptr [sum],0 sum = x + y; 011B26C5 mov eax,dword ptr [x] 011B26C8 add eax,dword ptr [y] 011B26CB mov dword ptr [sum],eax return sum; 011B26CE mov eax,dword ptr [sum] } 011B26D1 pop edi 011B26D2 pop esi 011B26D3 pop ebx 011B26D4 mov esp,ebp 011B26D6 pop ebp 011B26D7 ret下面圖中詳細描述了調用過程地址變化(此處所有地址是取自32位windows系統(tǒng)vs編輯器下的調試過程。):?
過程描述:?
1、參數(shù)拷貝(參數(shù)實例化)。?
2、保存當前指令的下一條指令,并跳轉到被調函數(shù)。?
這些操作均在main函數(shù)中進行。
接下來是調用Add函數(shù)并執(zhí)行的一些操作,包括:?
1、移動ebp、esp形成新的棧幀結構。?
2、壓棧(push)形成臨時變量并執(zhí)行相關操作。?
3、return一個值。?
這些操作在Add函數(shù)中進行。
被調函數(shù)完成相關操作后需返回到原函數(shù)中執(zhí)行下一條指令,操作如下:?
1、出棧(pop)。?
2、回復main函數(shù)的棧幀結構。(pop )?
3、返回main函數(shù)?
這些操作也在Add函數(shù)中進行。?至此,在main函數(shù)中調用Add函數(shù)的整個過程已經(jīng)完成。?
總結起來整個過程就三步:?
1)根據(jù)調用的函數(shù)名找到函數(shù)入口;?
2)在棧中審請調用函數(shù)中的參數(shù)及函數(shù)體內定義的變量的內存空間?
3)函數(shù)執(zhí)行完后,釋放函數(shù)在棧中的審請的參數(shù)和變量的空間,最后返回值(如果有的話)?
如果你學了微機原理,你會想到cpu中斷處理過程,是的,函數(shù)調用過程和中斷處理過程一模一樣。
函數(shù)調用約定:?
這里再補充一下各種調用規(guī)定的基本內容。?
_stdcall調用約定
所有參數(shù)按照從右到左壓入堆棧,由被調用的子程序清理堆棧
_cdecl調用約定(The C default calling convention,C調用規(guī)定)
參數(shù)也是從右到左壓入堆棧,但由調用者清理堆棧。
_fastcall調用約定
顧名思義,_fastcall的目的主要是為了更快的調用函數(shù)。它主要依靠寄存器傳遞參數(shù),剩下的參數(shù)依然按照從右到左的順序壓入堆棧,并由被調用的子程序清理堆棧。
本篇博文是按調用約定__stdcall 調用函數(shù)。
?
csdn博客地址:http://blog.csdn.net/qq_38646470
總結
以上是生活随笔為你收集整理的C语言的函数调用过程(栈帧的创建与销毁)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 回调函数到底是怎么一回事呢
- 下一篇: C++知识回顾之__stdcall、__