函式呼叫和名稱修飾

2021-06-17 16:04:55 字數 3810 閱讀 5899

呼叫約定:

__cdecl __fastcall與 __stdcall,三者都是呼叫約定(calling convention),它決定以下內容:1)函式引數的壓棧順序,2)由呼叫者還是被呼叫者把引數彈出棧,3)以及產生函式修飾名的方法。

1、__stdcall呼叫約定:函式的引數自右向左通過棧傳遞,被呼叫的函式在返回前清理傳送引數的記憶體棧,

2、_cdecl是c和c++程式的預設呼叫方式。每乙個呼叫它的函式都包含清空堆疊的**,所以產生的可執行檔案大小會比呼叫_stdcall函式的大。函式採用從右到左的壓棧方式。注意:對於可變引數的成員函式,始終使用__cdecl的轉換方式。

3、__fastcall呼叫約定:它是通過暫存器來傳送引數的(實際上,它用ecx和edx傳送前兩個雙字(dword)或更小的引數,剩下的引數仍舊自右向左壓棧傳送,被呼叫的函式在返回前清理傳送引數的記憶體棧)。

5、naked call採用1-4的呼叫約定時,如果必要的話,進入函式時編譯器會產生**來儲存esi,edi,ebx,ebp暫存器,退出函式時則產生**恢復這些暫存器的內容。naked call不產生這樣的**。naked call不是型別修飾符,故必須和_declspec共同使用。

呼叫約定可以通過工程設定:setting.../c/c++ /code generation項進行選擇,預設狀態為__cdecl。

名字修飾約定:

2、c編譯時函式名修飾約定規則:

__stdcall呼叫約定在輸出函式名前加上乙個下劃線字首,後面加上乙個"@"符號和其引數的位元組數,格式為_functionname@number,例如 :function(int a, int b),其修飾名為:_function@8

__cdecl呼叫約定僅在輸出函式名前加上乙個下劃線字首,格式為_functionname。

__fastcall呼叫約定在輸出函式名前加上乙個"@"符號,後面也是乙個"@"符號和其引數的位元組數,格式為@functionname@number。

3、c++編譯時函式名修飾約定規則:

__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的執行緒函式位址,_beginthreadex和createthread需要__stdcall的執行緒函式位址。

2、一般win32的函式都是__stdcall。而且在windef.h中有如下的定義:

#define callback __stdcall

#define winapi  __stdcall

3、extern "c" _declspec(dllexport) int __cdecl add(int a, int b);

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

修飾符的書寫順序如上。

4、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

5、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

6、dllmain負責初始化(initialization)和結束(termination)工作,每當乙個新的程序或者該程序的新的執行緒訪問dll時,或者訪問dll的每乙個程序或者執行緒不再使用dll或者結束時,都會呼叫dllmain。但是,使用terminateprocess或terminatethread結束程序或者執行緒,不會呼叫dllmain。

7、乙個dll在記憶體中只有乙個例項

dll程式和呼叫其輸出函式的程式的關係:

1)、dll與程序、執行緒之間的關係

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

dll使用的記憶體從呼叫程序的虛擬位址空間分配,只能被該程序的執行緒所訪問。

dll的控制代碼可以被呼叫程序使用;呼叫程序的控制代碼可以被dll使用。

dlldll可以有自己的資料段,但沒有自己的堆疊,使用呼叫程序的棧,與呼叫它的應用程式相同的堆疊模式。

2)、關於共享資料段

dll定義的全域性變數可以被呼叫程序訪問;dll可以訪問呼叫程序的全域性資料。使用同一dll的每乙個程序都有自己的dll全域性變數例項。如果多個執行緒併發訪問同一變數,則需要使用同步機制;對乙個dll的變數,如果希望每個使用dll的執行緒都有自己的值,則應該使用執行緒區域性儲存(tls,thread local strorage)。  

DLL 中呼叫約定和名稱修飾

呼叫約定 calling convention 是指在程式語言中為了實現函式呼叫而建立的一種協議。這種協議規定了該語言的函式中的引數傳送方式 引數是否可變和由誰來處理堆疊等問題。不同的語言定義了不同的呼叫約定。在c 中,為了允許操作符過載和函式過載,c 編譯器往往按照某種規則改寫每乙個入口點的符號名...

DLL中呼叫約定和名稱修飾

dll中呼叫約定和名稱修飾 一 呼叫約定 calling convention 是指在程式語言中為了實現函式呼叫而建立的一種協議。這種協議規定了該語言的函式中的引數傳送方式 引數是否可變和由誰來處理堆疊等問題。不同的語言定義了不同的呼叫約定。在c 中,為了允許操作符過載和函式過載,c 編譯器往往按照...

DLL中呼叫約定和名稱修飾(二)

thiscall thiscall 呼叫約定是 c 中的非靜態類成員函式的預設呼叫約定。thiscall 只能被編譯器使用,沒有相應的關鍵字,因此不能被程式設計師指定。採用 thiscall 約定時,函式引數按照從右到左的順序入棧,被呼叫的函式在返回前清理傳送引數的棧,只是另外通過 ecx暫存器傳送...