函式呼叫約定與名字修飾約定

2021-07-05 09:44:57 字數 2959 閱讀 4041

**:

在windows下,由於很多語言支援動態鏈結庫技術,因此動態鏈結庫是一種很好的混合程式設計方法。語言對函式的約定有兩種:函式呼叫約定和名字修飾約定。不同語言預設的呼叫呼叫約定和函式的命名方式是不同的,要想不同的語言開發的動態鏈結庫能夠相互呼叫,那麼開發動態鏈結庫的語言和呼叫鏈結庫的語言的函式約定必須相同,同時在編譯時函式的修飾名也必須一樣。

呼叫約定決定了函式引數傳送時入棧和出棧的順序,以及堆疊平衡的方式,即是由呼叫函式還是被呼叫函式複製清除堆疊的引數,還原堆疊。常見的呼叫約定有__cdecl,__fastcall,__stdcall,__thiscall,__nakedcall。

1.1__cdecl

__cdecl 是c declaration的縮寫,表示c語言預設的函式呼叫方法。_cdecl 約定所有引數從右到左依次入棧,這些引數由呼叫者清除,稱為手動清棧。被呼叫函式不會要求呼叫者傳遞多少引數,呼叫者傳遞過多或者過少的引數,甚至完全不同的引數都不會產生編譯階段的錯誤。對引數可變的函式需要用__cdecl的方式進行宣告,因為如果引數不固定的話,在被呼叫函式內就無法知道有多少個引數入棧,所以為了實現可變引數,必須要在被調函式執行之後才知道引數使用的位元組數,所以要呼叫者來進行堆疊平衡操作。

1.2__fastcall

從字面的意思看,__fastcall的特點就是快。_fastcall函式呼叫約定在可能的情況下使用暫存器傳遞引數,其餘引數按照從右到左的順序入棧,被呼叫函式在返回之前負責清除棧中的引數。不同編譯器編譯的程式規定的暫存器不同。vc編譯器使用ecx和edx暫存器傳遞。使用__fastcall方式無法用作跨編譯器的介面。另外,在使用內嵌彙編的時候,還要注意不能和編譯器使用的暫存器有衝突。

1.3__stdcall

__stdcall 是standardcall的縮寫,絕大多數的windows的api是__stdcall呼叫約定。__stdcall呼叫約定的引數入棧方式是從右向左入棧,由被呼叫函式負責清除棧中的引數。像wsprintf這樣的api是採用__cdecl呼叫約定的。

1.4__thiscall

__thiscall只用在c++成員函式的呼叫,函式引數按照從右向左的順序入棧,類例項的this指標通過ecx暫存器傳遞。需要注意的是__thiscall不是c++的關鍵字,不能使用__thiscall宣告函式,它只能由編譯器使用。

1.5__nakedcall

採用前面幾種函式呼叫約定的函式,編譯器會在必要的時候自動在函式開始新增儲存esi,edi,ebx,ebp暫存器的**,在退出函式時會新增**恢復這些暫存器 的內容,使用__nakedcall方式宣告的函式則不會新增這樣的**。__nakedcall不是型別修飾符,故必須和_declspec共同使用。

名字修飾約定指編譯器在編譯階段如何定義函式的修飾名,各種編譯器在編譯函式的時候,會根據函式原型生成包含諸如函式名稱、引數和返回值等資訊的標識字串,這個字串稱為函式的修飾名。由於函式的修飾名僅在編譯和鏈結階段使用,所以該名稱字串存在於導入庫.lib檔案中,而在最終生成的dll檔案中,匯出的函式名和原始檔中定義的函式名是一致的。 

如果呼叫程式開發時和開發dll使用的名字修飾約定不一致,那麼在使用dll對應的lib檔案時,即使使用了正確的函式名,但是因為編譯在lib檔案中找的是修飾名而不是函式名,所以編譯器仍然會報」找不到函式」的錯誤。

2.1 vc和win32彙編編譯器的函式名修飾約定

在vc和win32彙編編譯器的函式名修飾約定如下:__stdcall呼叫約定在輸出函式名前加上乙個下劃線字首,後面加上乙個」@」符號和其引數的位元組數,格式為_functionname@number。__cdecl呼叫約定僅在輸出函式名前加上乙個下劃線字首,格式為_functionname。 __fastcall呼叫約定在輸出函式名前加上乙個」@」符號,後面也是乙個」@」符號和其引數的位元組數,格式為@functionname@number。

2.2 c++編譯器函式名修飾約定

c++編譯器使用的函式名稱修飾方式比較複雜,對於__stdcall方式的約定,編譯器在函式名前面加」?」作為開始,然後後面跟」@@yg」表示參數列開始,參數列用以下代號表示:代號x

defh

ijkm

n_n…型別

void

char

unsigned char

short

intunsigned int

long

unsigned long

float

double

bool…

pa表示指標,後面的代號表明指標型別,如果相同型別的指標連續出現,以」0」代替,乙個」0」代表一次重複。參數列的第一項為該函式的返回值型別,其後依次為引數化的資料型別,參數列後以」@z」標識整個名字的結束,如果該引數無返回值,則以」z」標識結束。 

對於__cdecl呼叫約定: 規則同上面的__stdcall呼叫約定,只是參數列的開始標識由上面的「@@yg」變為「@@ya」。 

對於__fastcall呼叫約定: 規則同上面的__stdcall呼叫約定,只是參數列的開始標識由上面的「@@yg」變為「@@yi」。 

vc++對函式的省缺宣告是」__cedcl」,將只能被c/c++呼叫. 

c++使用這麼複雜的名稱修飾方式的好處是可以支援函式過載,但這樣就造成了c++的名稱修飾方式無法和其他語言相容,造成用c++修飾方式命名的dll函式無法被其他語言使用。不過使用c++提供的extern 「c」關鍵字,新增在函式名和函式宣告前,c++就會對該函式強制使用標準c的函式名稱修飾方式,這樣在dll中用這種方式輸出的函式就可以被其他語言使用了,同時其他語言編寫的dll函式在c++的標頭檔案中宣告時,前面也必須加extern 「c」關鍵字,這樣c++才會在lib檔案中找到正確的函式修飾名。 

為了使標頭檔案可以同時在c和c++中使用,可以利用c編譯器的內部變數__cplusplus進行條件編譯,當使用c++方式編譯時,該變數被設定為true,否則被設定為false。這樣在c++中使用時,函式宣告前就會被自動加上extern 「c」關鍵字。

#ifdef __cplusplus

extern "c"

#endif

名字修飾約定和函式呼叫約定

所謂名字修飾約定,就是指變數名 函式名等經過編譯後重新輸出名稱的規則。比如源 中函式名稱為int func int a,int b 經過編譯後名稱可能為?func yahhh z func yghhh z func 8,也有可能與源 中名稱相同為func。影響編譯後輸出的名稱通常與名字修飾約定 ex...

C 呼叫約定和名字約定

c 呼叫約定和名字約定 呼叫約定 cdecl fastcall與 stdcall,三者都是呼叫約定 calling convention 它決定以下內容 1 函式引數的壓棧順序,2 由呼叫者還是被呼叫者把引數彈出棧,3 以及產生函式修飾名的方法。1 stdcall呼叫約定 函式的引數自右向左通過棧傳...

C 呼叫約定和名字約定

呼叫約定是指程式在函式呼叫時傳遞引數和獲取返回值所採用的方法 通過暫存器 或通過棧 或者是兩者的混合。用於指定calling convention的修飾符主要有 cdecl,stdcall,fastcall等。呼叫約定可以通過工程設定 setting.c c advanced callingconv...