一、說起函式呼叫,我們可能很快就想到:程式從main函式走起,遇到呼叫函式的語句,就跳轉到此函式所在的語句塊執行此函式,執行完之後再返回main函式繼續執行程式。但是這只是籠統的描述,其實在函式內部,函式呼叫要經過一系列的複雜的過程,下面為大家一一詳細敘述。
1.說到函式呼叫,我們不可避免的要說到棧幀的建立和銷毀。函式呼叫過程要為函式開闢空間,用於本次函式的調⽤用中臨時變數的儲存、現場保護。這塊棧空間,我們稱之為函式棧幀。首先應該明白,棧是從高位址向低位址延伸的。而說到函式棧幀,我們不可避免的要說到兩個暫存器:暫存器ebp和暫存器esp。暫存器ebp指向當前的棧幀的底部(高位址),暫存器esp指向當前的棧幀的頂部(低位址)。這兩個暫存器用於維護函式棧幀。
2.介紹完基本概念,我們使用乙個具體的例子來看看函式的呼叫過程及棧幀的建立和銷毀具體是怎麼實現的。首先看**:
#include
#include
int add(int
x, int
y)int main()
這個**時main函式呼叫了乙個簡單的add函式,但足以說明問題,下面我們來分析一下。首先看下它的彙編**。
00401060
push ebp
00401061
mov ebp,esp
00401063
sub esp,4ch
00401066
push ebx
00401067
push esi
00401068
push edi
00401069 lea edi,[ebp-4ch]
0040106c mov ecx,13h
00401071
mov eax,0cccccccch
00401076 rep stos dword ptr [edi]
12: int a = 10
;00401078
mov dword ptr [ebp-4],0ah
13: int b = 20
;0040107f mov dword ptr [ebp-8],14h
14: int ret = add(a, b);
00401086
mov eax,dword ptr [ebp-8]
00401089
push eax
0040108a mov ecx,dword ptr [ebp-4]
0040108d push ecx
0040108e call @ilt+0(_add) (00401005)
00401093
add esp,8
00401096
mov dword ptr [ebp-0ch],eax
15: printf("ret = %d\n", ret);
00401099
mov edx,dword ptr [ebp-0ch]
0040109c push edx
0040109d push offset string "ret = %d\n" (00424024)
004010a2 call printf (00401200)
004010a7 add esp,8
16: system("pause");
004010aa push offset string "pause" (0042401c)
004010af call system (004010f0)
004010b4 add esp,4
17: return 0
;004010b7 xor eax,eax
18: }
004010b9 pop edi
004010ba pop esi
004010bb pop ebx
004010bc add esp,4ch
004010bf cmp ebp,esp
004010c1 call __chkesp (00401280)
004010c6 mov esp,ebp
004010c8 pop ebp
004010c9 ret
我們來文字具體分析一下它的步驟
一、main函式執行之前首先要執行maincrtstart函式,為其開闢一塊記憶體空間。
二、執行main函式
1.push ebp 將ebp壓入棧
2.將esp移向棧頂
3.mov ebp esp 將esp賦給ebp,即將ebp移到esp所指向的位置,這時esp,ebp指向同一塊記憶體空間。
4.esp向上移動,為main函式開闢空間,此時esp和ebp維護一塊新的空間,即main函式的空間。
5.向main函式的棧幀裡壓入ebx,esi,edi三個暫存器,同時esp移向棧頂。
6.為main函式開闢的空間進行初始化。
7.將棧底指標減4,向上移動4個位元組,為變數a開闢空間。即建立變數a,並將a的值放入記憶體。
8.將棧底指標再向上移動4個位元組,為變數b開闢空間。即建立變數b, 並將b的值放入記憶體。
9.將棧底指標再向上移動4個位元組,為變數ret開闢空間,即建立變數ret,並將ret的值放入記憶體。
10.將ebp-8,即將變數b的值放入eax暫存器,這一塊空間即是實參b的臨時拷貝。之後將esp移向棧頂。
11.將ebp-4,即將變數a的值放入eax暫存器,這一塊空間即是實參a的臨時拷貝。之後將esp移向棧頂。
三、呼叫add函式
1.push ebp 將ebp壓入棧中
2.將esp移向棧頂
3.mov ebp,esp.將esp賦給ebp,即將ebp移動到esp所指向空間。此時esp和ebp指向同一塊空間。
4.esp向上移動,為add函式欲開闢空間,此時esp和ebp維護一塊新的空間,即add函式的空間。
5.為add函式開闢的空間進行初始化。
6.將棧底指標ebp-4,即棧底向上移動4個位元組,為變數z開闢空間,建立變數z.
7.將棧底指標ebp+8,即ebp向下移動8個位元組,此時ebp指向exa暫存器,即剛才所存放的變數a的值的空間。
8.將棧底指標ebp+12,即ebp向下移動12個位元組,此時ebp指向exa暫存器即剛才所存放的變數b的值的空間。
9.將形參x和形參y進行相加。
10.將相加的結果放入exa暫存器。
11.將暫存器edi,esi,ebx彈出棧,esp向下移動。
12.將ebp賦給esp,彈出ebp,esp與ebp回到原來維護main函式的空間。
add函式棧幀被銷毀。
13.返回main函式。
14.esp+8,銷毀剛才建立的存放變數a和變數b的值的空間,即銷毀剛才建立的形參。
15.將剛才存放結果的暫存器放入變數ret所指向的空間,即將函式所返回的結果放入變數ret的空間。
至此,add函式呼叫過程即相關函式棧幀的建立於銷毀完成。
下面是函式呼叫過程的簡單步驟圖:
C語言 函式呼叫過程(棧幀)
首先舉個栗子 include int add int x,int y int main 在這個程式裡,函式被呼叫才會發揮函式的功能,而函式的呼叫其實是乙個過程,在這個過程計算機要為函式開闢棧空間,用於本次函式臨時變數的儲存和現場保護,這塊空間稱為函式的棧幀。現場保護的作用是為了在呼叫完另乙個函式,返...
C語言函式呼叫過程 棧幀
在學習過函式宣告和定義,了解了函式的引數 實參 形參 引數的設計 函式的使用等一些函式基礎知識之後,函式逐漸變為我們編寫 時重要工具。無論是編寫時引用的庫函式,還是實現程式部分功能時使用的自定義函式,都體現函式的重要性。函式特點 使 開發更高效 提高 復用性 使 邏輯更加清晰。函式所佔據的重要地位,...
函式的呼叫過程 棧幀
在談棧幀之前,我們必須要先知道c c 程式記憶體的分配情況。乙個由c c 編譯的程式占用的記憶體分為以下幾個部分 1 棧區 stack 由編譯器自動分配釋放 存放為執行函式而分配的局 部變數 函式引數 返回資料 返回位址等。其操作方式類似於資料結構中的 棧。2 堆區 heap 一般由程式設計師分配釋...