呼叫函式主要關注三個方面分別是函式名,返回值和引數列表,我接下來將會深入底層講解呼叫函式的過程。
呼叫函式的過程主要有四方面,①函式引數代入,②函式棧幀開闢,③函式返回值,④函式棧幀回退
首先來看一段簡單的c檔案**,和它的彙編碼,只需簡單瀏覽即可:
原始碼:
int fun1(int a, int b)
int main()
彙編碼:
main函式
int main()
00c3143f pop edi
00c31440 pop esi
00c31441 pop ebx
00c31442 add esp,0cch
00c31448 cmp ebp,esp
00c3144a call __rtc_checkesp (0c3113bh)
00c3144f mov esp,ebp
00c31451 pop ebp
00c31452 ret
fun1函式
int fun1(int a, int b)
009813fa pop edi
009813fb pop esi
009813fc pop ebx
009813fd mov esp,ebp
009813ff pop ebp
00981400 ret
首先知道ebp為棧底暫存器,esp為棧頂暫存器。push為操作,操作方式為在esp中的棧頂存放資料,棧頂上移。
在檢視fun函式引數如何代入之前,我們先看一下main的棧頂和棧底
(1)函式引數代入
兩次push後,引數就被放在了main函式的棧頂,且入棧為從右往左,效果如下:
(2)fun函式棧幀開闢
可以看到,call就相當於呼叫函式,這裡重新設定了棧底ebp和棧頂esp,過程如下:
檢視fun函式的開闢棧幀的過程會發現它與main函式的開闢及其類似,其實,main函式的下面也有主函式引數,也就是可以把main函式也看做為乙個普通函式。
(3)函式返回
利用暫存器帶回,將暫存器的值寫入接收返回值的常量
(4)棧幀回退
將fun的棧頂指向fun的棧底:
mian函式中:esp移動8位,消除引數
現在就回到了main函式的棧頂。
(5)其他問題
①因為引數大小不同,8位元組及以上的引數採用的是提前在棧頂開闢記憶體,以儲存大位元組的引數。
②返回8位元組及以上的返回值也是採用提前開闢記憶體的方法。
③有三種不同的約定呼叫方式,分別為__cedel、__stdcall、__fastcall,這三種方式有一些細節的不同,但是思想相同,本文講的是__cedel方式。
函式呼叫過程
每乙個未執行完的函式都對應著乙個棧幀,系統為單個函式分配的那部分棧空間就叫做棧幀,棧幀儲存了函式的資訊。以下面的 為例,通過彙編 的執行過程介紹棧幀建立和銷毀的過程 include int add int x,int y int main 從main函式建立自己的棧幀開始 其他內容先忽略 初始狀態 ...
函式呼叫過程
c語言種有三種迴圈 do.while while for 初始化 條件判斷 步進 主函式 main 庫函式自定義函式函式的發明,使得變成可以以函式為單位進行模組化,叫做面向過程。軟體工程中,有 高內聚,低耦合 的要求。函式就是為了實現以上要求發明的產物。函式是面向過程的 介面 其介面包含了 引數 返...
函式呼叫過程
引數代入順序 引數入棧的順序是從右向左入棧的。8位元組的引數代入 push入棧的方式將引數傳遞 8位元組的引數代入 先在main函式的棧頂向上移動12位元組,然後將引數的資料拷貝到main函式棧頂開闢的記憶體。int fun1 int a,int b int main 第一步進行函式引數入棧,如圖 ...