天天看點

__cdecl,__fastcall, __stdcall

__cdecl,__fastcall, __stdcall 什麼差別?

函數參數入棧的方式

---------------------------------------------------------------

在函數調用過程中,會使用堆棧,這三個表示不同的堆棧調用方式和釋放方式。

比如說__cdecl,它是标準的c方法的堆棧調用方式,就是在函數調用時的參數壓入堆棧是與函數的聲明順序相反的,其它兩個可以看MSDN,不過這個對我們程式設計沒有太大的作用

---------------------------------------------------------------

調用約定

調用約定(Calling convention)決定以下内容:函數參數的壓棧順序,由調用者還是被調用者把參數彈出棧,以及産生函數修飾名的方法。MFC支援以下調用約定:

_cdecl

按從右至左的順序壓參數入棧,由調用者把參數彈出棧。對于“C”函數或者變量,修飾名是在函數名前加下劃線。對于“C++”函數,有所不同。

如函數void test(void)的修飾名是_test;對于不屬于一個類的“C++”全局函數,修飾名是?test@@ZAXXZ。

這是MFC預設調用約定。由于是調用者負責把參數彈出棧,是以可以給函數定義個數不定的參數,如printf函數。

_stdcall

按從右至左的順序壓參數入棧,由被調用者把參數彈出棧。對于“C”函數或者變量,修飾名以下劃線為字首,然後是函數名,然後是符号“@”及參數的位元組數,如函數int func(int a, double b)的修飾名是[email protected]。對于“C++”函數,則有所不同。

所有的Win32 API函數都遵循該約定。

_fastcall

頭兩個DWORD類型或者占更少位元組的參數被放入ECX和EDX寄存器,其他剩下的參數按從右到左的順序壓入棧。由被調用者把參數彈出棧,對于“C”函數或者變量,修飾名以“@”為字首,然後是函數名,接着是符号“@”及參數的位元組數,如函數int func(int a, double b)的修飾名是@[email protected]。對于“C++”函數,有所不同。

未來的編譯器可能使用不同的寄存器來存放參數。

thiscall

僅僅應用于“C++”成員函數。this指針存放于CX寄存器,參數從右到左壓棧。thiscall不是關鍵詞,是以不能被程式員指定。

naked call

采用1-4的調用約定時,如果必要的話,進入函數時編譯器會産生代碼來儲存ESI,EDI,EBX,EBP寄存器,退出函數時則産生代碼恢複這些寄存器的内容。naked call不産生這樣的代碼。

naked call不是類型修飾符,故必須和_declspec共同使用,如下:

__declspec( naked ) int func( formal_parameters )

{

// Function body

}

過時的調用約定

原來的一些調用約定可以不再使用。它們被定義成調用約定_stdcall或者_cdecl。例如:

#define CALLBACK __stdcall

#define WINAPI __stdcall

#define WINAPIV __cdecl

#define APIENTRY WINAPI

#define APIPRIVATE __stdcall

#define PASCAL __stdcall

繼續閱讀