__stdcall,__cdecl,__fastcall的區別
一、三者區別一覽表
__stdcall | __cdecl | __fastcall |
---|---|---|
引數傳遞方式 | 右->左 壓棧 | 右->左 壓棧 |
清理棧方式 | 被呼叫函式清理(即函式自己清理),多資料情況使用這個 | 呼叫者清理 |
棧適用場合 | Win API | c/C++ MFC預設方式可變引數的時候使用 |
C編譯修飾約定(它們均不改變輸出函式名中的字元大小寫) | 約定在輸出函式名前加上一個下劃線字首,後面加上一個“@”符號和其引數的位元組數,格式為_functionname@number | 約定僅在輸出函式名前加上一個下劃線字首,格式為_functionname |
C++修飾 | 見下面的”四、名字修飾約定” |
二、thiscall
thiscall僅僅應用於”C++”成員函式。this指標存放於CX暫存器,引數從右到左壓。thiscall不是關鍵詞,因此不能被程式設計師指定。
naked call:
採用1-4的呼叫約定時,如果必要的話,進入函式時編譯器會產生程式碼來儲存ESI,EDI,EBX,EBP暫存器,退出函式時則產生程式碼恢復這些暫存器的內容。
naked call不產生這樣的程式碼。naked call不是型別修飾符,故必須和_declspec共同使用。
三、編譯器選項
關鍵字 __stdcall
、__cdecl
和__fastcall
可以直接加在要輸出的函式前,也可以在編譯環境的Setting…/C/C++ /Code Generation項選擇。當加在輸出函式前的關鍵字與編譯環境中的選擇不同時,直接加在輸出函式前的關鍵字有效。它們對應的命令列引數分別為/Gz、 /Gd和/Gr。預設狀態為/Gd,即__cdecl。
四、名字修飾約定
1、修飾名(Decoration name)
“C” 或者“C++”函式在內部(編譯和連結)通過修飾名識別。修飾名是編譯器在編譯函式定義或者原型時生成的字串。有些情況下使用函式的修飾名是必要的,如在模組定義檔案裡頭指定輸出“C++”過載函式、建構函式、解構函式,又如在彙編程式碼裡呼叫“C””或“C++”函式等。
修飾名由函式名、類名、呼叫約定、返回型別、引數等共同決定。
2、名字修飾約定
隨呼叫約定和編譯種類(C或C++)的不同而變化。函式名修飾約定隨編譯種類和呼叫約定的不同而不同,下面分別說明。
a、C編譯時函式名修飾約定規則:
__stdcall呼叫約定在輸出函式名前加上一個下劃線字首,後面加上一個“@”符號和其引數的位元組數,格式為_functionname@number。
__cdecl呼叫約定僅在輸出函式名前加上一個下劃線字首,格式為_functionname。
__fastcall呼叫約定在輸出函式名前加上一個“@”符號,後面也是一個“@”符號和其引數的位元組數,格式為@functionname@number。
它們均不改變輸出函式名中的字元大小寫,這和PASCAL呼叫約定不同,PASCAL約定輸出的函式名無任何修飾且全部大寫。
b、C++編譯時函式名修飾約定規則:
__stdcall呼叫約定:
- 以“?”標識函式名的開始,後跟函式名;
- 函式名後面以“@@YG”標識參數列的開始,後跟參數列;
參數列以代號表示:
X–void ,
D–char,
E–unsigned char,
F–short,
H–int,
I–unsigned int,
J–long,
K–unsigned long,
M–float,
N–double,
_N–bool,
….
PA–表示指標,後面的代號表明指標型別,如果相同型別的指標連續出現,以“0”代替,一個“0”代表一次重複;參數列的第一項為該函式的返回值型別,其後依次為引數的資料型別,指標標識在其所指資料型別前;
- 參數列後以“@Z”標識整個名字的結束,如果該函式無引數,則以“Z”標識結束。
其格式為“?functionname@@YG*****@Z”或“?functionname@@YG*XZ”,例如
int Test1(char *var1,unsigned long)—–“?Test1@@YGHPADK@Z”
void Test2() —–“?Test2@@YGXXZ”
__cdecl呼叫約定:
規則同上面的_stdcall呼叫約定,只是參數列的開始標識由上面的“@@YG”變為“@@YA”。
__fastcall呼叫約定:
規則同上面的_stdcall呼叫約定,只是參數列的開始標識由上面的“@@YG”變為“@@YI”。
VC++對函式的省缺宣告是“__cedcl“,將只能被C/C++呼叫.
注意:
- _beginthread需要__cdecl的執行緒函式地址,_beginthreadex和CreateThread需要__stdcall的執行緒函式地址。
- 一般WIN32的函式都是__stdcall。而且在Windef.h中有如下的定義:
#define CALLBACK __stdcall
#define WINAPI __stdcall - extern “C” _declspec(dllexport) int __cdecl Add(int a, int b);
typedef int (__cdecl*FunPointer)(int a, int b);
修飾符的書寫順序如上。 extern “C”的作用:如果Add(int a, int b)是在c語言編譯器編譯,而在c++檔案使用,則需要在c++檔案中宣告:extern “C” Add(int a, int b),因為c編譯器和c++編譯器對函式名的解釋不一樣(c++編譯器解釋函式名的時候要考慮函式引數,這樣是了方便函式過載,而在c語言中不存在函式過載的問題),使用extern “C”,實質就是告訴c++編譯器,該函式是c庫裡面的函式。如果不使用extern “C”則會出現連結錯誤。
一般象如下使用:#ifdef _cplusplus #define EXTERN_C extern "C" #else #define EXTERN_C extern #endif #ifdef _cplusplus extern "C"{ #endif EXTERN_C int func(int a, int b); #ifdef _cplusplus } #endif
MFC提供了一些巨集,可以使用AFX_EXT_CLASS來代替__declspec(DLLexport),並修飾類名,從而匯出類,AFX_API_EXPORT來修飾函式,AFX_DATA_EXPORT來修飾變數
AFX_CLASS_IMPORT:__declspec(DLLexport)
AFX_API_IMPORT:__declspec(DLLexport)
AFX_DATA_IMPORT:__declspec(DLLexport)
AFX_CLASS_EXPORT:__declspec(DLLexport)
AFX_API_EXPORT:__declspec(DLLexport)
AFX_DATA_EXPORT:__declspec(DLLexport)
AFX_EXT_CLASS:#ifdef _AFXEXT
AFX_CLASS_EXPORT
#else
AFX_CLASS_IMPORT- DLLMain負責初始化(Initialization)和結束 (Termination)工作,每當一個新的程式或者該程式的新的執行緒訪問DLL時,或者訪問DLL的每一個程式或者執行緒不再使用DLL或者結束時,都會呼叫DLLMain。但是,使用TerminateProcess或TerminateThread結束程式或者執行緒,不會呼叫DLLMain。
- 一個DLL在記憶體中只有一個例項
DLL程式和呼叫其輸出函式的程式的關係:
1)、DLL與程式、執行緒之間的關係
DLL模組被對映到呼叫它的程式的虛擬地址空間。
DLL使用的記憶體從呼叫程式的虛擬地址空間分配,只能被該程式的執行緒所訪問。
DLL的控制程式碼可以被呼叫程式使用;呼叫程式的控制程式碼可以被DLL使用。
DLLDLL可以有自己的資料段,但沒有自己的堆疊,使用呼叫程式的棧,與呼叫它的應用程式相同的堆疊模式。
2)、關於共享資料段
DLL定義的全域性變數可以被呼叫程式訪問;DLL可以訪問呼叫程式的全域性資料。使用同一 DLL的每一個程式都有自己的DLL全域性變數例項。如果多個執行緒併發訪問同一變數,則需要使用同步機制;對一個DLL的變數,如果希望每個使用DLL的執行緒都有自己的值,則應該使用執行緒區域性儲存(TLS,Thread Local Strorage)。
相關文章
- 函式呼叫的三種方式 __cdecl、__stdcall、__fastcall函式AST
- WIN32程式設計必知:__stdcall,__cdecl,__fastcall,thiscall,naked callWin32程式設計AST
- 函式呼叫方式 FAR PASCAL,__cdecl,_stdcall,__pasc函式
- stdcall詳解
- ??與?:的區別
- 蜂蜜的區別
- 和 的區別
- as 和 with的區別
- ||和??的區別
- /*和/**的區別
- LinkedList和ArrayList的區別、Vector和ArrayList的區別
- http和https的區別/get和post的區別HTTP
- UIModalPresentationStyle 各種型別的區別UI型別
- @synthesize @dynamic 的區別
- ../和./和/的區別
- CMM/CMMI 的區別
- scala中:: , +:, :+, :::, +++的區別
- ./ 和sh 的區別
- JQuery this和$(this)的區別jQuery
- jquery $(this) 和this的區別jQuery
- T和?的區別
- JavaScript中的“=、==、===”區別JavaScript
- ++a和a++的區別
- MySQL的@與@@區別MySql
- mybatis #與$的區別MyBatis
- BIO、NIO的區別
- PHP版本的區別PHP
- Null 與 “” 的區別Null
- restore,recover的區別REST
- truncate delete 的區別delete
- makefile =和:=的區別
- 值型別與引用型別的區別型別
- Golang的值型別和引用型別的範圍、儲存區域、區別Golang型別
- Mybatis中#{}和${}傳參的區別及#和$的區別小結MyBatis
- JS 的型別(null 和 undefined 的區別)JS型別NullUndefined
- 互斥量與臨界區的區別
- Android中 @和?區別以及?attr/**與@style/**等的區別Android
- 自增長列和序列的區別(identity與sequence的區別)IDE