函式呼叫的三種方式 __cdecl、__stdcall、__fastcall
__cdecl、__stdcall、__fastcall是C/C++裡中經常見到的三種函式呼叫方式。
__cdecl是C/C++預設的呼叫方式
__stdcall是windows API函式的呼叫方式,只不過我們在標頭檔案裡檢視這些API的宣告的時候是用了WINAPI的巨集進行代替了,而這個巨集其實就是__stdcall了。
函式的呼叫過程是通過函式棧幀的不斷變化實現的:
函式的呼叫,涉及引數傳遞,返回值傳遞,呼叫後返回,這都是通過棧的變化來實現的,對於三種呼叫約定而言:
__cdecl:
C/C++預設方式,引數從右向左入棧,主調函式負責棧平衡。
__stdcall:
windows API預設方式,引數從右向左入棧,被調函式負責棧平衡。
__fastcall:
快速呼叫方式。所謂快速,這種方式選擇將引數優先從暫存器傳入(ECX和EDX),剩下的引數再從右向左從棧傳入。因為棧是位於記憶體的區域,而暫存器位於CPU內,故存取方式快於記憶體,故其名曰“__fastcall”。
#include <stdio.h>
class Point
{
public:
Point(int x, int y)
{
nx = x;
ny = y;
nTest = nx + ny;
}
~Point(){}
int nx;
int ny;
int nTest;
};
void __cdecl Fun1(int x, int y)
{
Point pt(0, 0);
pt.nx = x;
pt.ny = y;
}
void __fastcall Fun2(int x, int y)
{
Point pt(0, 0);
pt.nx = x;
pt.ny = y;
}
void __stdcall Fun3(int x, int y)
{
Point pt(0, 0);
pt.nx = x;
pt.ny = y;
}
int main()
{
Fun1(2, 5);//__cdecl
Fun2(2, 5);//__fastcall
Fun3(2, 5);//__stdcall
return 0;
}
先打斷點,進行反彙編
__cdecl按照引數從右向左的方式進入棧區,注意Fun1()和Fun3()的區別,Fun1()在call Fun1()之後執行了add esp,8。這一操作正是我們前面所說的進行棧的平衡。
呼叫函式之前連續進行了兩次push操作將函式所需的實參5和2先後壓入了棧區,呼叫完成後,我們需要恢復呼叫前的狀態,則需調整棧頂指標esp的位置,這一工作由誰來完成就決定了兩種函式呼叫方式__cdecl(主調函式完成)和__stdcall(被調函式完成)的區別。上圖我們看到了__cdecl中由主調函式完成了,那麼__stdcall呢,在被調函式Fun3()中,轉向被調函式結尾處的程式碼,我們看到了這一句:
fun1()結尾處
這個ret指令後面跟沒跟值就決定了函式返回是棧指標ESP需要增加的量。這樣,不需要主調函式再呼叫add指令為ESP操作平衡棧區,節約了程式的開銷,一條指令開銷小,如果十萬百萬個這樣的呼叫,這個開銷就明顯了。
__fastcall,如前面圖看到的呼叫時並未使用push指令向棧裡傳引數,而是使用了
兩條指令。這樣直接將引數傳入暫存器,被調函式在執行的時候直接從暫存器取值即可,省去了從棧裡取出來給暫存器,再從暫存器取出來放入記憶體。
ecx暫存器經常作為計數和C++裡this指標的傳遞媒介。ecx做計數器時,需要將ecx中儲存的實參先壓入棧區,計數操作完成後再pop出來。如此一來,這個fastcall倒顯得不那麼fast了。
相關文章
- JS的五種函式呼叫方式JS函式
- 函式的呼叫方式和引數函式
- go 陣列傳遞給函式三種方式Go陣列函式
- JS中建立函式的幾種方式JS函式
- 開窗函式的另外一種方式函式
- Python透過函式名呼叫函式的幾種場景Python函式
- 建構函式,拷貝賦值函式的N種呼叫情況函式賦值
- Android中js呼叫java本地方法的三種方式AndroidJSJava
- 一種WebService的呼叫方式Web
- axios為什麼可以使用物件和函式兩種方式呼叫?是如何實現的?iOS物件函式
- 外部函式的呼叫函式
- 018-html插入樣式的三種方式HTML
- html樣式表三種引入方式HTML
- Js基礎知識4-函式的三種建立、四種呼叫(及關於new function()的解釋)JS函式Function
- 函式你必須知道的三種角色?函式
- jmeter學習指南之Beanshell 呼叫 java 程式碼的三種方式JMeterBeanJava
- 三種方法實現strlen函式函式
- SpringCloud 三種服務呼叫方式,你學會了嗎?SpringGCCloud
- 三欄式佈局的幾種實現方式
- 子函式呼叫函式
- 函式呼叫棧函式
- PHP 三種方式實現鏈式操作PHP
- 分散式鎖初窺-分散式鎖的三種實現方式分散式
- 重構 - 用各種方式優化自己的函式庫優化函式
- python三種屬性管理魔法函式Python函式
- 如何使用函式指標呼叫類中的函式和普通函式函式指標
- 函式呼叫棧的問題函式
- 虛擬函式的呼叫原理函式
- gdb 如何呼叫函式?函式
- 徹底理解JavaScript函式的呼叫方式和傳參方式——結合經典面試題JavaScript函式面試題
- C程式函式呼叫&系統呼叫C程式函式
- JavaScript的三種引入方式JavaScript
- PostgreSQL函式裡呼叫函式(SETOF + RETURN QUERY)SQL函式
- 普通函式與函式模板呼叫規則函式
- MogDB/openGauss的三種函式穩定性關鍵字函式
- 三種常用的內建函式--必須牢記函式
- Go 函式的三種用法:方法、閉包、實參Go函式
- ton函式函式hash的兩種形式函式