函數(shù)的名字修飾(Decorated Name)就是編譯器在編譯期間創(chuàng)建的一個(gè)字符串,,用來指明函數(shù)的定義或原型,。LINK程序或其他工具有時(shí)需要指定函數(shù)的名字修飾來定位函數(shù)的正確位置。多數(shù)情況下程序員并不需要知道函數(shù)的名字修飾,,LINK程序或其他工具會(huì)自動(dòng)區(qū)分他們,。當(dāng)然,在某些情況下需要指定函數(shù)的名字修飾,,例如在C++程序中,,為了讓LINK程序或其他工具能夠匹配到正確的函數(shù)名字,就必須為重載函數(shù)和一些特殊的函數(shù)(如構(gòu)造函數(shù)和析構(gòu)函數(shù))指定名字裝飾,。另一種需要指定函數(shù)的名字修飾的情況是在匯編程序中調(diào)用C或C++的函數(shù),。如果函數(shù)名字,調(diào)用約定,,返回值類型或函數(shù)參數(shù)有任何改變,,原來的名字修飾就不再有效,必須指定新的名字修飾,。C和C++程序的函數(shù)在內(nèi)部使用不同的名字修飾方式,,下面將分別介紹這兩種方式。
1. C編譯器的函數(shù)名修飾規(guī)則
對(duì)于__stdcall調(diào)用約定,,編譯器和鏈接器會(huì)在輸出函數(shù)名前加上一個(gè)下劃線前綴,,函數(shù)名后面加上一個(gè)“@”符號(hào)和其參數(shù)的字節(jié)數(shù),例如_functionname@number,。__cdecl調(diào)用約定僅在輸出函數(shù)名前加上一個(gè)下劃線前綴,,例如_functionname,。__fastcall調(diào)用約定在輸出函數(shù)名前加上一個(gè)“@”符號(hào),后面也是一個(gè)“@”符號(hào)和其參數(shù)的字節(jié)數(shù),,例如@functionname@number
2. C++編譯器的函數(shù)名修飾規(guī)則
C++的函數(shù)名修飾規(guī)則有些復(fù)雜,,但是信息更充分,通過分析修飾名不僅能夠知道函數(shù)的調(diào)用方式,,返回值類型,,參數(shù)個(gè)數(shù)甚至參數(shù)類型。不管__cdecl,,__fastcall還是__stdcall調(diào)用方式,,函數(shù)修飾都是以一個(gè)“?”開始,后面緊跟函數(shù)的名字,,再后面是參數(shù)表的開始標(biāo)識(shí)和按照參數(shù)類型代號(hào)拼出的參數(shù)表。對(duì)于__stdcall方式,,參數(shù)表的開始標(biāo)識(shí)是“@@YG”,,對(duì)于__cdecl方式則是“@@YA”,對(duì)于__fastcall方式則是“@@YI”,。參數(shù)表的拼寫代號(hào)如下所示: 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類型的指針,。后面的代號(hào)表明指針類型,,如果相同類型的指針連續(xù)出現(xiàn),以“0”代替,,一個(gè)“0”代表一次重復(fù),。U表示結(jié)構(gòu)類型,通常后跟結(jié)構(gòu)體的類型名,,用“@@”表示結(jié)構(gòu)類型名的結(jié)束,。函數(shù)的返回值不作特殊處理,它的描述方式和函數(shù)參數(shù)一樣,,緊跟著參數(shù)表的開始標(biāo)志,,也就是說,函數(shù)參數(shù)表的第一項(xiàng)實(shí)際上是表示函數(shù)的返回值類型,。參數(shù)表后以“@Z”標(biāo)識(shí)整個(gè)名字的結(jié)束,,如果該函數(shù)無參數(shù),則以“Z”標(biāo)識(shí)結(jié)束,。下面舉兩個(gè)例子,,假如有以下函數(shù)聲明:
int Function1 (char *var1,unsigned long); 其函數(shù)修飾名為“?Function1@@YGHPADK@Z”,而對(duì)于函數(shù)聲明: void Function2(); 其函數(shù)修飾名則為“?Function2@@YGXXZ” ,。
對(duì)于C++的類成員函數(shù)(其調(diào)用方式是thiscall),,函數(shù)的名字修飾與非成員的C++函數(shù)稍有不同,,首先就是在函數(shù)名字和參數(shù)表之間插入以“@”字符引導(dǎo)的類名;其次是參數(shù)表的開始標(biāo)識(shí)不同,,公有(public)成員函數(shù)的標(biāo)識(shí)是“@@QAE”,保護(hù)(protected)成員函數(shù)的標(biāo)識(shí)是“@@IAE”,私有(private)成員函數(shù)的標(biāo)識(shí)是“@@AAE”,,如果函數(shù)聲明使用了const關(guān)鍵字,則相應(yīng)的標(biāo)識(shí)應(yīng)分別為“@@QBE”,,“@@IBE”和“@@ABE”,。如果參數(shù)類型是類實(shí)例的引用,則使用“AAV1”,,對(duì)于const類型的引用,,則使用“ABV1”。下面就以類CTest為例說明C++成員函數(shù)的名字修飾規(guī)則: class CTest
{
......
private:
void Function(int);
protected:
void CopyInfo(const CTest &src);
public:
long DrawText(HDC hdc, long pos, const TCHAR* text, RGBQUAD color, BYTE bUnder, bool bSet);
long InsightClass(DWORD dwClass) const;
......
}; 對(duì)于成員函數(shù)Function,,其函數(shù)修飾名為“?Function@CTest@@AAEXH@Z”,,字符串“@@AAE”表示這是一個(gè)私有函數(shù)。成員函數(shù)CopyInfo只有一個(gè)參數(shù),,是對(duì)類CTest的const引用參數(shù),,其函數(shù)修飾名為“?CopyInfo@CTest@@IAEXABV1@@Z”。DrawText是一個(gè)比較復(fù)雜的函數(shù)聲明,,不僅有字符串參數(shù),,還有結(jié)構(gòu)體參數(shù)和HDC句柄參數(shù),需要指出的是HDC實(shí)際上是一個(gè)HDC__結(jié)構(gòu)類型的指針,,這個(gè)參數(shù)的表示就是“PAUHDC__@@”,,其完整的函數(shù)修飾名為“?DrawText@CTest@@QAEJPAUHDC__@@JPBDUtagRGBQUAD@@E_N@Z”。InsightClass是一個(gè)共有的const函數(shù),,它的成員函數(shù)標(biāo)識(shí)是“@@QBE”,,完整的修飾名就是“?InsightClass@CTest@@QBEJK@Z”。
無論是C函數(shù)名修飾方式還是C++函數(shù)名修飾方式均不改變輸出函數(shù)名中的字符大小寫,,這和PASCAL調(diào)用約定不同,,PASCAL約定輸出的函數(shù)名無任何修飾且全部大寫。
3.查看函數(shù)的名字修飾
有兩種方式可以檢查你的程序中的函數(shù)的名字修飾:使用編譯輸出列表或使用Dumpbin工具,。使用/FAc,,/FAs或/FAcs命令行參數(shù)可以讓編譯器輸出函數(shù)或變量名字列表。使用dumpbin.exe /SYMBOLS命令也可以獲得obj文件或lib文件中的函數(shù)或變量名字列表,。此外,,還可以使用 undname.exe 將修飾名轉(zhuǎn)換為未修飾形式。
|