__cdecl __fastcall__stdcall




呼叫約定:


__cdecl __fastcall__stdcall,三者都是呼叫約定(Calling convention),它決定以下內容:1)函數參數的壓堆疊順序,2)由呼叫者還是被呼叫者把參數即現堆疊,3)以及產生函數修飾名的方法。




1__stdcall呼叫約定:函數的參數自右向左通過堆疊傳遞,被呼叫的函數在傳回前清理傳輸參數的記憶體堆疊,




2_cdeclCC++程式的預設呼叫方式。每一個呼叫它的函數都包含清空堆疊的程式碼,所以產生的可執行檔案大小會比呼叫_stdcall函數的大。函數釆用從右到左的壓堆疊方式。注意:對于可變參數的成員函數,始終使用__cdecl的轉換方式。




3__fastcall呼叫約定:它是通過暫存器來傳輸參數的(實際上,它用ECXEDX傳輸前兩個雙字(DWORD)或更小的參數,剩餘的參數仍舊自右向左壓堆疊傳輸,被呼叫的函數在傳回前清理傳輸參數的記憶體堆疊)。




4thiscall僅僅應用于"C++"成員函數。this指標存放于CX暫存器,參數從右到左壓。thiscall不是關鍵詞,因此無法被式設計師指定。




5naked call釆用1-4的呼叫約定時,如果必要的話,進入函數時編譯器會產生程式碼來儲存ESIEDIEBXEBP暫存器,結束函數時則產生程式碼回復這些暫存器的內容。naked call不產生這樣的程式碼。naked call不是類別修飾符,故必須和_declspec共同使用。




呼叫約定可以通過工程設定:Setting...\C/C++ \Code Generation項進行選取,預設狀態為__cdecl




名字修飾約定:




1、修飾名(Decoration name)"C"或者"C++"函數在內部(編譯和鏈結)通過修飾名識別


2C編譯時函數名修飾約定規則:


__stdcall呼叫約定在匯出函數名前加上一個底線前置,后面加上一個"@"象徵式和其參數的位元組數,格式為_functionname@number,例如:function(int a, int b),其修飾名為:_function@8


__cdecl呼叫約定僅在匯出函數名前加上一個底線前置,格式為_functionname


__fastcall呼叫約定在匯出函數名前加上一個"@"象徵式,后面也是一個"@"象徵式和其參數的位元組數,格式為@functionname@number




3C++編譯時函數名修飾約定規則:


__stdcall呼叫約定:


1)、以"?"識別函數名的開始,后跟函數名;


2)、函數名后面以"@@YG"識別參數表的開始,后跟參數表;


3)、參數表以代號表示:


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"代表一次重複;


4)、參數表的第一項為該函數的傳回值類別,其后依次為參數的資料類別,指標識別在其所指資料類別前;


5)、參數表后以"@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++呼叫.






注意:


1_beginthread需要__cdecl的執行緒函數位址,_beginthreadexCreateThread需要__stdcall的執行緒函數位址。




2、一般WIN32的函數都是__stdcall。而且在Windef.h中有如下的定義:


#define CALLBACK __stdcall


#define WINAPI  __stdcall




3extern "C" _declspec(dllexport) int __cdecl Add(int a, int b);


typedef int (__cdecl*FunPointer)(int a, int b);


修飾符的書寫順序如上。




4extern "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




5MFC提供了一些巨集,可以使用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




6DLLMain負責起始化(Initialization)和結束(Termination)工作,每當一個新的程式或者該程式的新的執行緒存取DLL時,或者存取DLL的每一個程式或者執行緒不再使用DLL或者結束時,都會呼叫DLLMain。但是,使用TerminateProcessTerminateThread結束程式或者執行緒,不會呼叫DLLMain




7、一個DLL在記憶體中只有一個實例


DLL程式和呼叫其匯出函數的程式的關係:


1)DLL與程式、執行緒之間的關係


DLL模組被對映到呼叫它的程式的虛擬位址空間。


DLL使用的記憶體從呼叫程式的虛擬位址空間配置,只能被該程式的執行緒所存取。


DLL的控點可以被呼叫程式使用;呼叫程式的控點可以被DLL使用。


DLLDLL可以有自己的資料段,但沒有自己的堆疊,使用呼叫程式的堆疊,與呼叫它的應用程式相同的堆疊型態。




2)、關于共用資料段


DLL定義的全域變數可以被呼叫程式存取;DLL可以存取呼叫程式的全域資料。使用同一DLL的每一個程式都有自己的DLL全域變數實例。如果多個執行緒并發存取同一變數,則需要使用同步機制;對一個DLL的變數,如果希望每個使用DLL的執行緒都有自己的值,則應該使用執行緒局部儲存(TLSThread Local Strorage)






原文位址 http://tb.blog.csdn.net/TrackBack.aspx?PostId=706590

arrow
arrow
    全站熱搜

    立你斯 發表在 痞客邦 留言(0) 人氣()