C 名字修飾規則

2023-01-17 06:33:05 字數 4518 閱讀 6693

c++ 編譯器的函式名修飾規則

函式名字修飾(decorated name)方式

函式的名字修飾(decorated name)就是編譯器在編譯期間建立的乙個字串,用來指明函式的定義或原型。link程式或其他工具有時需要指定函式的名字修飾來定位函式的正確位置。 多數情況下程式設計師並不需要知道函式的名字修飾,link程式或其他工具會自動區分他們。

當然,在某些情況下需要指定函式的名字修飾,例如在c++程式中, 為了讓link程式或其他工具能夠匹配到正確的函式名字,就必須為過載函式和一些特殊的函式(如建構函式和析構函式)指定名字裝飾。另一種需要指定函式的名字修飾的情況是在匯程式設計序中呼叫c或c++的函式。如果函式名字,呼叫約定,返回值型別或函式引數有任何改變,原來的名字修飾就不再有效,必須指定新的名字修飾。

c和c++程式的函式在內部使用不同的名字修飾方式,下面將分別介紹這兩種方式。

1. c編譯器的函式名修飾規則

對於__stdcall呼叫約定,編譯器和鏈結器會在輸出函式名前加上乙個下劃線字首,函式名後面加上乙個「@」符號和其引數的位元組數,例如 _functionname@number。__cdecl呼叫約定僅在輸出函式名前加上乙個下劃線字首,例如_functionname。 __fastcall呼叫約定在輸出函式名前加上乙個「@」符號,後面也是乙個「@」符號和其引數的位元組數,例如 @functionname@number

2. c++編譯器的函式名修飾規則

c++的函式名修飾規則有些複雜,但是資訊更充分,通過分析修飾名不僅能夠知道函式的呼叫方式,返回值型別,引數個數甚至引數型別。不管 __cdecl,__fastcall還是__stdcall呼叫方式,函式修飾都是以乙個「?」開始,後面緊跟函式的名字,再後面是參數列的開始標識和按照引數型別代號拼出的參數列。

對於__stdcall方式,參數列的開始標識是「@@yg」,對於__cdecl方式則是「@@ya」,對於 __fastcall方式則是「@@yi」。參數列的拼寫代號如下所示:

x--void

d--char

e--unsigned char

f--short

h--int

i--unsigned int

j--long

k--unsigned long(dword)

m--float

n--double

_n--bool

u--struct

....

指標的方式有些特別,用pa表示指標,用pb表示const型別的指標。後面的代號表明指標型別,如果相同型別的指標連續出現,以「0」代替,乙個「0」代表一次重複。u表示結構型別,通常後跟結構體的型別名,用「@@」表示結構型別名的結束。

函式的返回值不作特殊處理,它的描述方式和函式引數一樣,緊跟著參數列的開始標誌,也就是說,函式引數表的第一項實際上是表示函式的返回值型別。參數列後以「@z」標識整個名字的結束,如果該函式無引數,則以「z」標識結束。下面舉兩個例子,假如有以下函式宣告:

int function1 (char *var1,unsigned long);

其函式修飾名為「?function1@@yghpadk@z」,而對於函式宣告:

void function2();

其函式修飾名則為「?function2@@ygxxz」 。

對於c++的類成員函式(其呼叫方式是thiscall),函式的名字修飾與非成員的c++函式稍有不同,首先就是在函式名字和參數列之間插入以「@」字元引導的類名;其次是參數列的開始標識不同,公有(public)成員函式的標識是「@@qae」,保護(protected)成員函式的標識是 「@@iae」,私有(private)成員函式的標識是「@@aae」,如果函式宣告使用了const關鍵字,則相應的標識應分別為 「@@qbe」,「@@ibe」和「@@abe」。如果引數型別是類例項的引用,則使用「a**1」,對於const型別的引用,則使用「abv1」。下面就以類ctest為例說明c++成員函式的名字修飾規則:

class ctest

; 對於成員函式function,其函式修飾名為「?function@ctest@@aaexh@z」,字串「@@aae」表示這是乙個私有函式。成員函式copyinfo只有乙個引數,是對類ctest的const引用引數,其函式修飾名為 「?

copyinfo@ctest@@iaexabv1@@z」。drawtext是乙個比較複雜的函式宣告,不僅有字串引數,還有結構體引數和hdc 控制代碼引數,需要指出的是hdc實際上是乙個hdc__結構型別的指標,這個引數的表示就是「pauhdc__@@」,其完整的函式修飾名為 「?drawtext@ctest@@qaejpauhdc__@@jpbdutagrgbquad@@e_n@z」。

insightclass是乙個共有的const函式,它的成員函式標識是「@@qbe」,完整的修飾名就是「?insightclass@ctest@@qbejk@z」。

無論是c函式名修飾方式還是c++函式名修飾方式均不改變輸出函式名中的字元大小寫,這和pascal呼叫約定不同,pascal約定輸出的函式名無任何修飾且全部大寫。

3.檢視函式的名字修飾

有兩種方式可以檢查你的程式中的函式的名字修飾:使用編譯輸出列表或使用dumpbin工具。使用/fac,/fas或/facs命令列引數可以讓編譯器輸出函式或變數名字列表。

使用 /symbols命令也可以獲得obj檔案或lib檔案中的函式或變數名字列表。此外,還可以使用 將修飾名轉換為未修飾形式。

函式呼叫約定和名字修飾規則不匹配引起的常見問題

函式呼叫時如果出現堆疊異常,十有**是由於函式呼叫約定不匹配引起的。比如動態鏈結庫a有以下匯出函式:long makefun(long lfun);

動態庫生成的時候採用的函式呼叫約定是__stdcall,所以編譯生成的中函式makefun的呼叫約定是_stdcall,也就是函式呼叫時引數從右向左入棧,函式返回時自己還原堆疊。現在某個程式模組b要引用a中的makefun,b和a一樣使用c++方式編譯,只是b模組的函式呼叫方式是__cdecl,由於b包含了a提供的標頭檔案中makefun函式宣告,所以makefun在b模組中被其它呼叫makefun的函式認為是 __cdecl呼叫方式,b模組中的這些函式在呼叫完makefun當然要幫著恢復堆疊啦,可是makefun已經在結束時自己恢復了堆疊,b模組中的函式這樣多此一舉就引起了棧指標錯誤,從而引發堆疊異常。巨集觀上的現象就是函式呼叫沒有問題(因為引數傳遞順序是一樣的),makefun也完成了自己的功能,只是函式返回後引發錯誤。

解決的方法也很簡單,只要保證兩個模組的在編譯時設定相同的函式呼叫約定就行了。

在了解了函式呼叫約定和函式的名修飾規則之後,再來看在c++程式中使用c語言編譯的庫時經常出現的lnk 2001錯誤就很簡單了。還以上面例子的兩個模組為例,這一次兩個模組在編譯的時候都採用__stdcall呼叫約定,但是使用c語言的語法編譯的(c語言方式),所以的載入庫中makefun函式的名字修飾就是「_makefun@4」。b包含了a提供的標頭檔案中 makefun函式宣告,但是由於b採用的是c++語言編譯,所以makefun在b模組中被按照c++的名字修飾規則命名為 「?

makefun@@ygjj@z」,編譯過程相安無事,鏈結程式時c++的鏈結器就到中去找「?makefun@@ygjj@z」,但是 中只有「_makefun@4」,沒有「?makefun@@ygjj@z」,於是鏈結器就報告:

error lnk2001: unresolved external symbol ?makefun@@ygjj@z

解決的方法和簡單,就是要讓b模組知道這個函式是c語言編譯的,extern "c"可以做到這一點。乙個採用c語言編譯的庫應該考慮到使用這個庫的程式可能是c++程式(使用c++編譯器),所以在設計標頭檔案時應該注意這一點。通常應該這樣宣告標頭檔案:

#ifdef _cplusplus

extern "c"

#endif

這樣c++的編譯器就知道makefun的修飾名是「_makefun@4」,就不會有鏈結錯誤了。

許多人不明白,為什麼我使用的編譯器都是vc的編譯器還會產生「error lnk2001」錯誤?其實,vc的編譯器會根據原始檔的副檔名選擇編譯方式,如果檔案的副檔名是「.c」,編譯器會採用c的語法編譯,如果副檔名是 「.

cpp」,編譯器會使用c++的語法編譯程式,所以,最好的方法就是使用extern "c"。

1.__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 test2test2@@ygxxz」

C異常捕獲和修飾符

c 異常的使用 c 中的異常其實挺簡單,只不過剛學有些想不明白,首先要先了解異常的概念,定義 c 語言的異常處理功能可幫助您處理程式執行時出現的任何意外或異常情況。其次對異常如何使用的呢?1 異常處理使用 try catch 和 finally 關鍵字嘗試某些操作,以處理失敗情況,儘管這些操作有可能...

桌球雙打比賽規則c

一 賽制 五局三勝制,局分十一分,賽前猜拳勝者選場地,輸者先發球。二 接 發球規則 第一局 a隊先發球 a1 b2 a2 b1 即 如果a1先發,b1接髮 兩球後換發 原接發球者 b1 換為發球者,即 b1發球,a2接髮 兩球後換發 原接發球者 a2 換為發球者,即 a2發球,b2接髮 兩球後換發 ...

c1科目三考試規則

1.起步前準備 開車門打指紋 考試開始 逆時針繞車一圈 檢查車況 上車三模,第一摸座位下邊的金屬棒即調整好座位到適應自己的位置,第二模是繫好安全帶,第三模是摸內視鏡的支架和左門縫。2.起步 簡稱一踩 踩剎車和離合 二掛 掛一檔 三打燈 打左轉向燈 四鳴笛五鬆手剎,然後眼睛看左後視鏡,確認安全後,起步...