Visual C++名字修飾

Name mangling，或者Decorated Name，是指程序設計語言中具有存儲性質的對象的名字被編譯器改寫，以適合編譯器、鏈接器(linker)、彙編器(assembler)使用。所謂的具有存儲性質的對象，即lvalue對象，是指要實際佔用內存空間、有內存地址的那些實體對象，例如：變量(variables)、函數、函數指針等。C++中的純虛函數作為特例也屬於這一範疇。而數據類型(data type)就不屬於具有存儲性質的對象。Name mangling如何翻譯成中文，尚無廣泛接受的譯法。可翻譯作改名或名字修飾(name decorating)。
對於支持重載(overload)的程序設計語言，name mangling是必需的因而具有特別重要意義。C++允許函數重載
int foo(int i);
void foo(char c);
C++也允許變量名稱在不同限定（qualifier)下同名：
std::cout;
:cout;
因此C++的編譯器必須給這些函數及變量不同的名字以供程序編譯鏈接、載入時的內部辨別使用。
C++的編譯器可大略分為Windows平臺上的Microsoft Visual C++與類Unix平臺上的GNU CC/g++兩大類，分別成了各自操作系統環境下的業界工業標準。例如，Windows平臺上的Intel C++ Compiler（ICC）與Digital Mars C++，都與Visual C++保持了二進制兼容(Application Binary Interface, ABI)。而Linux平臺上的Intel C++ Compiler（ICC）與HP aC++，都與GCC 3.x/4.x做到二進制兼容。GCC是開源產品，它的內部實現機制是公開的；而Visual C++不公開它內部實現細節，因此在name mangling上並無詳盡的正式文檔，Visual C++ name mangling的細節屬於hacker行為。
一般情況下，編程者不需要知道C/C++函數的修飾名字。但是，如果在彙編源程序或者內聯彙編中引用了C/C++函數，就必須使用其正確的修飾名字。

**C語言的name mangling**
C語言並不支持重載，因此C程序中禁止函數與函數同名，也就沒有必要做name mangling。但C語言的函數調用協議(calling conventions)五花八門，用某一種調用協議編譯的靜態庫或動態庫的函數，如果用另外的調用協議去調用將會導致錯誤甚至系統崩潰。因此C語言編譯器對函數的名字做少量的修飾，用於區別該函數支持哪種調用協議，這可以給編譯、鏈接、特別是庫函數的載入提供額外的檢查信息。Microsoft C編譯器在八十年代最先引入這種mangling模式，隨後各家編譯器如Digital Mars, Borland, GNU gcc等紛紛效仿。
目前，C語言常用的調用協議有三種：cdecl, stdcall與fastcall，其它眾多調用協議如__pascal, __fortran, __syscall, __far等基本上算是過時了(obsolote)，因此無需考慮。cdecl的函數被改名為_name；stdcall的函數被改名為_name@X；fastcall的函數被改名為@name@X。其中X是函數形參所佔用的字節長度(包括那些用寄存器傳遞的參數, 如fastcall協議). 例如：
int __cdecl foo(int i); // mangled name is _foo;
int __stdcall bar(int j); // mangled name is _bar@4  
int __fastcall qux(int i) ; // mangled name is @qux@4 
注意在64位Windows平臺上，由於ABI有正式標準可依，只存在一種子程序調用協議，所以該平臺上的C語言的函數不在名字前加上下劃線(leading underscore). 這會導致一些老的程序(legacy program), 例如使用'alias'去鏈接C語言的函數的Fortran程序不能正常工作。

**C++語言name mangling概述**
C++語言由於含有大量複雜的語言特性，如classes, templates, namespaces, operator overloading等，這使得對象的名字在不同使用上下文中具有不同的意義。所以C++的name mangling非常複雜。
這些名字被mangled的對象，實際上都是具有全局屬性的存儲對象(storage object)，其名字將綁定到所佔用內存空間的地址，都有lvalue。存儲對象具體可分為數據變量(data variable)與函數(function)。為什麼不考慮函數的非靜態局部變量、類的非靜態數據成員呢？因為編譯器把函數的非靜態局部變量翻譯為sp+固定的偏移量；把類的非靜態數據成員翻譯為this+固定的偏移量。
下文使用了巴科斯-瑙爾範式(BNF)來表述一些name mangling的語法定義。方括號表示該項出現0次或1次，除非在方括號後用上下角標給出該項出現的上下限。下文使用類，一般包含了class、struct、union等複合數據類型。

**基本結構**
::= ? 
::= @ @∞0 @
C++中被mangled的名字都使用問號(?)開始，因為這與用字母數字(alphanumeric)、下劃線(_)或@開頭的C語言程序中的被mangled的名字能完全區分開。
C++中的變量與函數，可定義於名字空間或類中。所以變量與函數受到名字空間或類的限定(qualification)。而名字空間、類又可以嵌套(nest)。
表示變量與函數的名字及所定義的名字空間（或類）的嵌套情況。並採用與C++程序中作用域嵌套相反的順序編碼。例如，namespace1::nestedClass::something編碼為something@nestedClass@namespace1@@。
Name mangling時，名字的字符串用@符號作為結束標誌。例如@，表示這個字符串以@符號作為結束標誌。因為名字的長度不是事先確定的。如果一個詞法單元的長度是確定的，這些詞法單元就不用@作為結尾標誌，例如下文中只需用單個字母表示，則無需額外的結束標誌。
是變量與函數的類型信息的編碼表示。對於數據對象，就是它的數據類型，見數據對象的name mangling；對於函數，類型信息就是它的返回值類型、參數類型列表、調用協議等情況，見函數的name mangling。

**數據對象的name mangling**
這裡所說的數據對象，包括全局變量(global variables)、類的靜態數據成員變量(static member variables of classes)。
::= ?@@∞0@

用於表示數據對象的類別，其編碼為：
是數據對象的訪問屬性的編碼表示，一般常用的值有：A表示default對象、B表示const對象、C表示volatile對象、D表示const volatile對象。詳見小節:。需要注意的是對於指針、數組、引用類型的對象，是對所指向的基類型的內存空間的訪問屬性。
例如：
int alpha;    // mangled as ?alpha@@3HA 其中3表示全局變量，H表示整形，A表示非const非volatile
char beta6 = "Hello"; // mangled as ?beta@@3PADA 
//其中P表示為指針(或一維數組)且指針自身為非const非volatile，
//D表示char類型，兩次出現的A都表示基類型為非const非volatile 
class myC{
	static int s_v;       // mangled as ?s_v@myC@@0HA 其中0表示私有靜態數據成員
};


**函數的name mangling**
函數需要分配內存空間以容納函數的代碼，函數的名字實際上都是lvalue，即指向一塊可執行內存空間的起始地址。而函數模板的實例化(function template instantiation)，也是lvalue，需要分配內存空間存儲實例化後的代碼，其name mangling在模板實例化的名字編碼中詳述。
::= ?@ @∞0 @  
∞1  
::= ?@ @∞0 @  

∞1  
其中，給出了函數是near或far、是否為靜態函數、是類成員函數還是全局函數、是否為虛函數、類成員函數的訪問級別等基本信息。需要注意，far屬性僅適用於Windows 16位環境，32位或64位環境下使用扁平(flat)內存地址模型，函數只能具有near屬性。
類成員函數的是指是否為只讀成員函數(constant member function). 如果不是const，則編碼為A；如果是const，則編碼為B；如果是類的靜態成員函數，則省略該項，因為靜態成員函數沒有this指針，無法修改類對象的數據。
是指函數的調用協議。詳見調用協議的編碼。常見的調用協議的編碼為：__cdecl是A, __pascal是C, __thiscall是E, __stdcall是G, __fastcall是I。在Windows 64位編譯環境中唯一允許的調用協議的編碼是A（詳見64位程序的調用約定）。
是指函數的返回值的是否有const或volatile屬性：
::= ？   
如果函數的返回值不具有const或volatile性質，那麼該項在編碼中被省略；但是如果函數的返回類型是class、struct或union等複合數據類型，此項在編碼中是必需的，不能省略。
是函數的返回值的數據類型，詳見類型的編碼表示。
是函數的形參列表(parameter list)的數據類型的編碼。按照形參從左到右順序給每個參數的數據類型編碼，詳見類型的編碼表示。參數類型列表的編碼為：- X (即函數沒有參數，或者說參數為void，該編碼也是列表的結束標誌)- type1 type2 ... typeN @ (正常N個形參. 以@作為列表的結束標誌)- type1 type2 ... Z (形參表最後一項為...，即ellipsis，其編碼Z也標誌著列表的結束)
是函數拋出異常的說明，即異常規範(exception specification)。截至Visual C++ 2010，仍是接受但沒有實現異常規範。因此這一項編碼仍然保持為字符Z。
舉例：
void Function1 (int a, int * b); /*mangled as ?Function1@@YAXHPAH@Z 
其中 Y: 全局函數
A: cdecl調用協議
X: 返回類型為void
H: 第一個形參類型為int
PAH：第二個形參類型為整形指針
@： 形參表結束標誌
Z: 缺省的異常規範 */
int Class1::MemberFunction(int a, int * b); /* mangled in 32-bit mode as 
?MemberFunction@Class1@@QAEHHPAH@Z
其中 Q: 類的public function
A: 成員函數不是const member function
E: thiscall調用協議
H: 返回值為整形
H: 第一個形參類型為整形
PAH: 第二個形參為整形指針 */

**C++語言name mangling細節**

**名字的編碼**
C++程序中，需要考慮的具有全局存儲屬性的變量名字及函數的名字。這些名字受namespace、class等作用域(scope)的限定。因此，帶完整限定信息的名字定義為：
::=  ∞0 @
::=  

::=  



其中，是組成名字標識符的ASCII碼串，規定必須以@作為結尾後綴。是指構造函數、析構函數、運算符函數(operator function)、虛表(vtable)等內部數據結構等，詳見特殊名字的編碼。
與就是指C++程序中的名字空間與類。是指實例化後的函數模板或類模板，詳見模板實例化的名字編碼。是對一個函數內部用花括號{ ... }給出的不同的作用域(scope)的編號表示，詳見編號名字空間。是對一個mangled name的ASCII碼串中重複出現的類型或名字的簡寫表示方法，詳見重複出現的名字與類型的簡寫表示。是對靜態局部變量所在的函數名的表示方法，詳見嵌套的名字。

**數的編碼**
Visual C++的name mangling，有時會用到數(number)，例如多維數組的維數等。數的編碼使用一套獨特的方法：- 1-10 編碼為 0-9，這節省了最常用的數的編碼長度；- 大於10的數編碼為十六進制，原來的十六進制數字0-F用A-P代替，不使用前綴0或0x，使用後綴@作為結束標誌；- 0編碼為A@；- 負數編碼為前綴?後跟相應的絕對值(absolute value)的編碼。
例如，8編碼為7。29110編碼為BCD@。-1510編碼為?P@。

**特殊名字的編碼**
特殊名字(special names)是指類的構造函數、析構函數、運算符函數、類的內部數據結構等的名字，表示為前綴?後跟編碼。已知的編碼：
虛表的mangled name是::= ??_7  ∞0 @6B@,
前綴_P用在?_PX之中. 其含義未知。
下表是RTTI相關的編碼，都是在_R後跟一個數字. 有些編碼還後跟參數.


**模板實例化的名字編碼**
函數模板實例化後，就是一個具體的函數。類模板實例化後，就是一個具體的類數據類型。
::= ?$    
::= ?$   
::= ?$   
模板的名字以前綴?$開始。?$  可以代替，?$  可以代替。
模板實參(template argument)，可以分為類型名字(typename)與非類型(non-type)的常量兩類。如果是類型名字或類作為模板實參，那麼其編碼格式詳見類型的編碼表示。如果模板實參是常量(constant)，則已知的編碼格式列為下表：
上表中，用a, b, c表示有符號整數，而x, y, z表示無符號整數. 這些有符號整數或無符號整數的編碼格式，詳見數的編碼。上表中，實數值的編碼表示$2ab，a、b都是有符號整數，但計算實數的值時，實際上規範到以10為基數的科學計數法的表示形式。
例如：
template class one{
	int i;
};
one one1; // mangled as ?one1@@3V?$one@H@@A 
//3V...@A表示這是一個全局的類對象，其中的@是類的編碼的結束標誌；
//類名為one，編碼是?$one@H@，其中one@是模板的名字，
//H是模板實參int，其後的@是模板實參表結束標誌


class Ce{};
one another; /* mangled as ?another@@3V?$one@VCe@@@@A */
//注意，倒數第1個@表示整個(模板實例)類的結束；
//     倒數第2個@表示模板實參表的結束；
//     倒數第3個@表示類Ce的限定情況的結束(此處限定為空，即Ce的作用域是全局)；
//    倒數第4個@表示類名碼串的結束 

**編號名字空間**
編號名字空間(numbered namespace)用於指出函數靜態局部變量包含在函數的哪個內部作用域中。之所以需要引入編號名字空間，是為了區分函數內部的不同作用域，從而可以區分包含在不同作用域中但同名的變量，詳見下例。其編碼格式為前綴?後跟一個無符號數，無符號數的編碼參見數的編碼小節。

特例情況是, 以?A開始的編號名字空間, 是('anonymous namespace').
例如：
int func()
{
static int i;	// mangled as ?i@?1??func@@YAHXZ@4HA  內部表示為`func'::`2'::i
	     // ?1表示第2號名字空間；?func@@YAHXZ@是函數的mangled名字；4表示靜態局部變量
{
static int i; // mangled as ?i@?2??func@@YAHXZ@4HA  內部表示為`func'::`3'::i
	              // ?3表示第3號名字空間
	}
	return 0;
}

**重複出現的名字與類型的簡寫**
在對一個名字做mangling時，用簡寫方法表示非首次出現的同一個名字或同一個類型。整個簡寫過程需要對ASCII碼串做3次掃描處理：
# 對函數或函數指針的形參表中的重複出現的參數數據類型的編碼簡寫；
# 把第一步獲得結果中重複出現的名字的編碼簡寫；
# 把第二步獲得的結果中的模板實例化(模板名字+模板實參表)內部重複出現的名字的編碼簡寫。

**=重複出現的類型的簡寫=**
這適用於函數與函數指針的形參列表。只有編碼超過一個字符的類型參與簡寫，包括指針、函數指針、引用、數組、bool、__int64、class、實例化的模板類、union、struct、enum等數據類型。形參表中前10種多字符編碼的類型按照出現次序依次編號為0,1,...,9。用單個字符編碼的類型不參加編號。對不是該數據類型首次出現的形參，用該類型的單個數字的編號代替該數據類型的多個字符的編碼來簡寫表示。排在前十名之後的多字符編碼的數據類型，不再簡寫。函數的返回值類型不參與此編號及簡寫。
如果函數的返回類型或者形參是函數指針型，那麼函數指針型的形參也參與類型排序編號與簡寫，但函數指針型的返回值類型不參與類型排序編號與簡寫。在對類型排序編號時，先編號函數指針型內部的形參的數據類型，再編號函數指針型本身。例如，假如函數的第一個形參是void (__cdecl*)(class alpha, class beta)，那麼class alpha編號為0, class beta編號為1, 最後整個函數指針編號為2.
例如：
bool ExampleFunction (int*a, int b, int c, int*d, bool e, bool f, bool*g);  
// mangled as ?ExampleFunction@@YA_NPAHHH0_N1PA_N@Z
//  其中，_N為返回類型bool，不參與類型簡寫，不參與編號排序；
//  類型的排序編號：int*為0，bool為1，bool*為2。
//  int的編碼為單字符H，因此不參與編號
//  第3個形參不簡寫，仍為H；第四個形參簡寫為0，第五個形參簡寫為1 */    
//



typedef int* (*FP)(int*); /* 該函數指針類型編碼為 P6APAHPAH@Z */
//
FP	funcfp (int *, FP) /* mangled as ?funcfp@@YAP6APAHPAH@Z0P6APAH0@Z@Z */
//  其中P6A為函數指針類型的編碼前綴
//  其中共出現了5次int* (編碼為PAH)
//  第1次為funcfp的返回值類型FP的返回值類型，不參與排序編號與簡寫；
//  第2次為funcfp的返回值類型FP的形參，參與排序編號，編號為0，
//       因為是該類型int*的首次作為形參出現，不簡寫，仍編碼為PAH
//  第3次為funcfp的第一個形參，簡寫為0；
//  第4次為funcfp的第二個形參的返回值類型，不參與排序編號與簡寫；
//  第5次為funcfp的第二個形參的參數，簡寫為0 
{return 0;}

**=重複出現的名字的簡寫=**
在對碼串完成重複出現的類型的簡寫後，再對結果中所有不同的名字排序編號，從0編號到9。排在前10個之後的名字不再編號、簡寫。這裡的名字是指函數、class、struct、union、enum、實例化的帶實參的模板等等的以@作為結尾後綴的名字。例如，在alpha@?1beta@@(即beta::'2'::alpha)中, 0指代alpha@, 1指代beta@，?1是編號名字空間‘2’的編碼. 特殊名字、編號名字空間的名字都不參加此輪名字的排序編號與簡寫。
例如：
class C1{
	class C2{};
};
union C2{};
void func(C2,C1::C2) /* mangled as ?func@@YAXTC2@@V1C1@@@Z */
//其中，func的形參表是TC2@@V1C1@@@
//TC2@@是第一個形參union C2；
//V1C1@@是第二個形參C1::C2，其中V表示這是class，
//首個1表示是編號為1的名字(即C2@)重複出現，隨後的C1@表示C2的限定域為C1
//隨後的@表示限定域的嵌套結束。注意，該字串中編號為“0”的名字是func@  
{}
對於實例化模板，模板名字後跟模板實參作為一個整體視作一個名字，參加此輪排序編號與簡寫。而模板實參表中的參數序列，單獨處理它的編號及簡寫。例如：
template class tc{
public: void __stdcall func(tc){};
};
int main(int argc, char *argv)
{
	tc ins;
	ins.func(ins); /* tc::func(tc) mangled as ?func@?$tc@H@@QAEXV1@@Z */
	                  //其中，?$tc@H@表示實例化的類模板tc，裡面的H表示模板實例化的實參是整型
	                  //Q表示public的成員函數；A表示非只讀成員函數；E表示thiscall
	                  //X表示函數返回類型void；
	                  //V1@表示形參為一個class，class的名字為1號名字，本例中即tc的編碼?$tc@H@；
	                  //注意，本例中0號名字是函數名，即func@
	                  //最後一個@表示函數的形參表結束；Z表示缺省的exception specification
	return 0;
}

**=模板實例化時重複出現的實參的簡寫=**
模板實例化的名字編碼，基本上就是用模板的名字與模板實參作為一個整體，當作或使用。因此模板實例化的名字在參與完成重複出現的類型簡寫與重複出現的名字簡寫兩步處理之後，再單獨處理模板實例化的模板實參表，對其內部重複出現的名字的編號與簡化。其方法與重複出現的名字簡寫的處理相同。
例1：
template class tc{
	int i;
public: void __stdcall func(tc p1,tc p2){};
};
class Ce{};
int main(int argc, char *argv)
{
tc ins;
	  ins.func(ins,ins);  /* void tc::func(tc,tc) 
	                  mangled as ?func@?$tc@VCe@@V1@@@QAGXV1@0@Z */
	     //?$tc@VCe@@V1@@表示實例化的類模板tc，其中的V1@是類模板的實參的重複名字簡寫；
	     //函數func的作用域是tc,即tc::func編碼為func@?$tc@VCe@@V1@@@  ；
//V1@表示成員函數func的第一個形參為一個類，類名為1號名字，本例中即tc，這是重複名字的簡寫；
	     //0表示該函數func的第二個形參與形參表中的0號形參相同，即tc，這是重複類型的簡寫   

return 0;
}
例2：
class class1{
public:  class class2{} ee2;
};

union class2{};
template  void func( T1,T2,T3 ){}
int main(int argc, char *argv)
{
	class1 a;
	class2 b;
	func(b,a.ee2,b); 
	   /* func(class2,class1::class2,class2)
	    mangled as ??$func@Tclass2@@V1class1@@T1@@@YAXTclass2@@V0class1@@0@Z  */
//如果不簡寫: ??$func@Tclass2@@Vclass2@class1@@Tclass2@@@@YAXTclass2@@Vclass2@class1@@Tclass2@@@Z
//    首先對函數的形參表中重複的類型簡寫，第3個參數與第1個參數相同，以類型編號0簡寫；
//    然後對整個字串中重複的名字編號、簡寫，第1個形參中的名字‘class2@’編號為0，
//        所以函數第2個形參中的‘class2@’被簡寫為0；
//        這也說明“函數模板名+模板實參”，不作為名字參與編號及簡寫(但普通函數的名字卻是參與名字編號!)，
//        而類模板名+模板實參”卻算作單獨一個名字而參與編號及簡寫；
//      之後，對模板實例化名字，即“函數模板名+模板實參”，單獨執行重複出現的名字編號及簡寫，
//          其中的‘class2@’出現3次，後2次被簡寫為1，這也說明在模板實例化名字內部，
//          僅執行重複名字的簡寫，不執行重複類型的簡寫 
	return 0;
}
例3：
class class1{
public: class name9{} ee;
};
template  void name9 ( T p1){}
int main(int argc, char *argv)
{
	class1 vv;
	name9(vv.ee); /* void name9(class1::name9) 
	      mangled as ??$name9@V0class1@@@@YAXVname9@class1@@@Z */
// 	      此例說明模板實例化在做重複名字簡化時，
// 	      模板實參中的name9與模板名字name9相同，因而簡化為編號0 
return 0;
}

**數據類型的編碼表示**
這裡所說的類型，包括數據類型、函數指針的類型、函數模板、類模板等不需要分配內存空間的一些概念屬性。類型是數據對象與函數這兩類實體的屬性。
對於簡單的數據類型，其編碼往往就是一個字母。如int類型編碼為X。對各種衍生的數據類型（如指針）、複合的數據類型（如類）、函數指針、模板等，在下文中分述。
X表示void 僅當用於表示函數的返回類型、形參表的終止或指針的基類型, 否則該編碼表示cointerface. 代碼 Z (表示ellipsis)僅用於表示不定長度的形參列表(varargs).

**指針、引用、數組的類型編碼**
::=   
::=   
::= $$Q  
::= 
::=   
其中作為前綴，用於區分各種情況的指針、引用、數組。指針自身是const還是volatile等訪問屬性，由確定。共有八種情況：
表示所指向的基類型(Referred type)是否具有const或volatile等訪問屬性，詳見小節：。
表示指針或引用的基類型(Referred type)，或數組的成員類型(element type)。其編碼詳見數據類型的編碼表示。
表示多維數組的基礎維度信息，其格式為：Y...。注意，這裡使用的數字，要用Visual C++ name mangling特有的數字編碼方法，詳見數的編碼。可見，C++語言的數組是對連續存儲數據的內存的直接隨機訪問(random access)的手段；因此一維數組視作指針，數組訪問是否越界，完全由編程者負責；而對多維數組，必須知道除了第一維之外其它各維的長度，才能做到直接隨機訪問，所以多維數組作為函數形參時，必須已知其除了第一維之外其它各維的長度（以及總的維數），這些信息都被編入了數組的mangled name中。
對於函數指針類型的編碼，其為函數調用接口信息，包括使用的調用協議、返回值類型、形參類型、允許拋出的異常等，詳見函數指針類型的編碼。類成員指針的類型編碼，詳見類成員指針的類型編碼。類成員函數指針的類型編碼，詳見類成員函數指針的類型編碼。
2003年x86-64位處理器問世後，第一批64位Windows平臺的C++編譯器曾經使用_O作為數組類型的前綴修飾符(type modifier). 但不久就改回了32位Windows平臺C++編譯器使用的P前綴. 
需注意的是，全局數組類型被編碼為P(指針型)，同時作為函數形參的數組類型被編碼為Q(常量指針). 這與其本來含義恰恰相反——全局數組型的變量名字表示某塊內存地址，該名字不能再改為指向其它內存地址；而作為函數形參的數組型變量的名字所表示的內存地址是可以修改的。但數組類型這種編碼方法已經被各種C++編譯器廣泛接受。顯然，這是為了與老的代碼保持向後兼容。例如：
int ia10;                                 //ia是數組類型的非函數形參的變量
//
int main(int argc, char *argv) //argv是數組類型的形參, 其類型為 (char *)
{
int j=*(ia++);                              //編譯錯誤！ia是隻讀的lvalue，不能完成地址的++操作
char *c= *(argv++);                         //編譯正確！argv是可以修改的lvalue
return 0;
}
例如：
typedef int * p1; // coded as PAH 其中P表示default訪問屬性的指針，
//A表示對基類型的default訪問屬性，H表示基類型為int
typedef const int * p2; //coded as PBH 其中B表示基類型的const訪問屬性
typedef volatile int *p3; //coded as PCH 其中C表示基類型的volatile訪問屬性
typedef volatile const int *p4; //coded as PDH 其中D表示基類型的const volatile訪問屬性
typedef int * const p5; //coded as QAH 其中Q表示是const pointer
typedef int * volatile p6; //coded as RAH 其中R表示是volatile pointer
typedef int * const volatile p7; //coded as SAH 其中S表示是const volatile pointer
typedef volatile int * const p6; //coded as QCH 例如這是一個外部IO設備輸入數據的內存地址
typedef int &r1; // coded as AAH 其中第一個A表示左值引用類型(l-value reference type)
typedef int&& r2; //coded as $$QAH 其中$$Q表示是右值引用類型(r-value reference type)
typedef const int&& r3; //coded as $$QBH 其中B表示基類型是const屬性
typedef int8 a1; // global array coded as PAH
typedef int108 a2; //global array coded as PAY07H 其中Y標誌多維數組，0是(維數-1)即1的編碼
// 7是第二維長度8的編碼
typedef int4165 a3; //global array coded as PAY1BA@4H 其中1為(維數-1)即2的編碼
//BA@是第二維長度16的編碼(16進制的10)，4是第3維長度5的編碼
int76 // 作為函數形參時，該數據類型編碼為 QAY05H

**函數指針類型的編碼**
函數指針的類型信息，包括函數返回類型，函數形參類型，調用協議等。以前綴P6開始。各項具體定義可參見函數的類型信息編碼：
::=    
∞1

一般地，取值為P，意為指針；取值為6，意為指針的基類型為near屬性的非成員函數。

例如：
typedef const  int (__stdcall *FP) (int i); /* coded as P6G?BHH@Z */
//                                         其中?B表示為const 

**類成員指針的類型編碼**
指向類成員的指針，其編碼為
** 

其中各項的定義詳見指針、引用、數組的類型編碼。注意，是指基類型的屬性，常用的值為：Q for default， R for const; S for volatile; T for const volatile。
例如：
class C1{
	int i;
};
typedef int C1::*p; // coded as PQC1@@H

**類成員指針變量的mangled name**
類成員指針(pointer to member)的名字mangling的最末尾處對基類型訪問屬性的編碼不同於普通的指針，要在最後加上所指向類的帶完整限定信息的名字：
::= ? 

有的文獻稱，類成員指針、類成員函數指針的name mangling都必須以Q1@作為結尾，以替代。從下述幾例可以看出，這種說法是錯誤的。Q是對所指向的成員類型使用default訪問屬性，這是最常見的情況。1是該指針所指向類的名字簡寫，因為在此位置之前該類的名字必然已經出現在該數據類型的編碼中，所以此處名字的簡寫是必然的。但不一定總是簡寫作1
下例中，成員指針變量的mangled name以S12@結尾：
class outer{
public:
	class cde{
public: volatile int i;
};
};
volatile int outer::cde::* p; // mangled as ?p@@3PScde@outer@@HS12@ 
// 其中兩個S都是表示基類型為volatile屬性
// 1是cde@的簡寫
// 2是outer@的簡寫
例2：
class C1{
public: int i;
};
C1 const *pi; // ?pi@@3PBVC1@@B 一個簡單的指針變量。作為對比
typedef  int C1::*  TP; // coded as PQC1@@H
void func(TP){
static TP ppp=0; // `func'::`2'::ppp mangled as ?ppp@?1??func@@YAXPQC1@@H@Z@4PQ2@HQ2@ 
//其中，?ppp@?1??func@@YAXPQC1@@H@Z@表示帶作用域限定信息的名字`func'::`2'::ppp
//4表示靜態局部變量；PQ2@H表示成員指針類型，其中的“2”是C1@的簡寫。
//注意ppp@編號為0，func@編號為1
//最後三個字符Q2@表示對基類型“2”(C1@的簡寫)的訪問屬性為Q(缺省屬性，即非const非volatile)                  
}

**類成員函數指針的類型編碼**
類成員函數的指針(pointer to member function)，遵從指針類型編碼的一般規則。但與函數指針類型的編碼相比，多了一項，表示所指的函數是否為只讀成員函數(constant member function)。
::= 


∞1  
一般地，取值為P，意為指針；取值為8，意為指針的基類型為near屬性的類成員函數。其它各項取值參見函數的name mangling。
例如：
class C1{
public: void foo(int) const
{};
};
typedef  void (C1::*TP)(int) const; /* coded as P8C1@@BEXH@Z 
其中B表示const member function; E表示thiscall */

**類成員函數的指針變量的mangled name**
類成員函數指針(pointer to member function)的名字mangling，對基類型訪問屬性的編碼不同於普通的指針，要在最後加上所指向類的帶完整限定信息的名字。
::= ? 

上述定義中，取值一般是Q
例如：
class xyz{
public: void foo(int) {};
};
void (xyz::*pfunc)(int) ; /* mangled as ?pfunc@@3P8xyz@@AEXH@ZQ1@ */
//                    其中Q表示對基類型的訪問屬性為default；1表示被簡寫的編號為‘1’的名字，即‘xyz@’；
//                  注意，編號為‘0’的名字是‘pfunc@’ 

**複合類型(union, struct, class, coclass, cointerface)的編碼**
::= 
其中複合類型的種類作為前綴，union編碼為T, struct編碼為U, class編碼為V, coclass編碼為X, cointerface編碼為Y。複合類型的帶限定的名字，是指按照名字所在的名字空間、所屬的類，逐級列出限定情況(qualifier)，詳見名字的編碼。
經常可以看到複合類型的編碼以@@兩個字符結尾，這是因為第一個@表示複合類型名字的字串結束，第二個@表示限定情況的結束(即作用域為全局，限定情況為空)。
編寫代碼時，經常要用到類的前向聲明(forward declaration)，即提前聲明這個名字是個類，但類的成員尚未給出。例如:
class myClassName; //mangled type name is VmyClassName@@
class myClassName::embedClassName; //mangled type name is VembedClassName@VmyClassName@@

**枚舉類型(enum)的編碼**
::= W  
::= W  @ 
其中，W為枚舉類型前綴詞。為枚舉類型的帶限定的名字，是指按照名字所在的名字空間、所屬的類，逐級列出限定情況(qualifier)，詳見名字的編碼。枚舉實際使用的數據類型，
編碼如下：
例如：
enum namex:unsigned char {Sunday, Monday}; // enum-type coded as W4namex@@
看起來Visual C++已經把所有枚舉類型用int型實現，因此枚舉的基類型(The underlying type of the enumeration identifiers)的編碼總是為4

****
用於普通的數據對象，表示其是否具有const、volatile等訪問屬性；用於指針、數組、引用類型，則表明對基類型的訪問屬性，而指針自身是否為const、volatile等屬性，則專由編碼表示。
用於函數指針時，表示該指針所指向的基類型是函數。但與指向數據對象的普通指針不同——函數指針指向的基類型（即函數）也有自己的內存空間，只是這塊內存空間必定是隻讀的、可執行的，因此函數指針所指向的基類型內存空間不存在const、volatile等訪問屬性。
的取值情況：
可以有0個或多個前綴:

**=__based()屬性的變量=**
指針變量的__based()屬性是Microsoft的C++語言擴展. 這一屬性編碼為:- 0 (意味著__based(void))- 2 (意味著__based())- 5 (意味著沒有__based())
例如：
int *pBased;     // mangled name:  ?pBased@@3PAHA 
int __based(pBased) * pBasedPtr;    // 需要注意Visual C++編譯器把這個指針變量的聲明解釋為：
//         (int __based(pBased) * __based(pBased) pBasedPtr)
//         因此其mangled name: ?pBasedPtr@@3PM2pBased@@HM21@ 
//       其中PM2pBased@@表示這是基於的指針；HM21表示是基於“1”的整型指針，
//       “1”是重複出現的名字的編號簡寫，這裡就是指pBased@ 
//
int __based(void) *pbc; // mangled name: ?pbc@@3PM0HM0 其中的0表示這是__based(void). 
//        編譯器把該變量聲明解釋為(int __based(void) * __based(void) pbc) 


**函數的類型信息編碼**
函數的類型信息，是指調用函數時必須考慮的ABI(Application Binary Interface)，包括調用協議、返回類型、函數形參表、函數拋出異常的說明(exception specification)等，參見函數的name mangling。

****
給出了函數是near或far（但far屬性僅適用於Windows 16位環境，32位或64位環境下只能函數具有near屬性）、是否為靜態函數、是否為虛函數、類成員函數的訪問級別等信息：
上表中的thunk函數，是指在多繼承時，由編譯器生成的包裝函數(warpper function)，用於多態調用實際已被子類對應函數覆蓋(overrided)的父類虛函數，並把指向父類的this指針調整到指向子類的起始地址。

**調用協議的編碼**
64位編程時，唯一可用的調用協議的編碼是A

**查看Visual C++的函數的修飾後的名字**
有多種方法，可以方便地查看一個函數在編譯後的修飾名字：
# 直接用工具軟件(如微軟開發環境提供的dumpbin)查看obj、exe等二進制文件。使用dumplib查看.obj或.lib文件時，使用"/SYMBOLS"命令行選項。
# 編譯時使用"/FAu"編譯選項，生成帶有豐富註釋信息的彙編源程序，其文件擴展名是.cod或者.asm，可以查看每個C/C++函數的修飾名字。
# 在源程序中使用微軟提供的預定義宏(Microsoft-Specific Predefined Macros)—— __FUNCDNAME__，例如：
void exampleFunction()
{
printf("Function name: %s\n", __FUNCTION__);
printf("Decorated function name: %s\n", __FUNCDNAME__);
printf("Function signature: %s\n", __FUNCSIG__);

// 輸出為：  
// -------------------------------------------------
// Function name: exampleFunction
// Decorated function name: ?exampleFunction@@YAXXZ
// Function signature: void __cdecl exampleFunction(void)

}

**由修飾名字反查其未修飾時的原名**
 使用微軟Visual C++中的解析修飾名字的工具軟件undname.exe。例如：
C:\>undname.exe ??$name9@V0class1@@@@YAXVname9@class1@@@Z
Microsoft (R) C++ Name Undecorator
Copyright (C) Microsoft Corporation. All rights reserved.
Undecoration of :- "??$name9@V0class1@@@@YAXVname9@class1@@@Z"
is :- "void __cdecl name9(class class1::name9)"- 使用Windows提供的系統調用UnDecorateSymbolName()把修飾名字翻譯為未修飾名字。UnDecorateSymbolName在DbgHelp.h或imagehlp.h中聲明，在DbgHelp.dll中實現，需要使用導入庫DbgHelp.lib。Windows SDK中包含了DbgHelp.h與DbgHelp.lib。示例程序：
//UnDecorate.cpp
#include  //如果不包含此頭文件，編譯DbgHelp.h時會產生大量語法錯誤
#include 
#include 
#include 
#pragma comment(lib,"dbghelp.lib") //告訴鏈接器使用這個輸入庫
int _tmain(int argc, _TCHAR* argv)  
{  
	TCHAR szUndecorateName256;  
	memset(szUndecorateName,0,256);  
	if (2==argc)  
	{  
		::UnDecorateSymbolName(argv1,szUndecorateName,256,0);  
		std::cout
編譯後，執行上述程序：
C:\>UnDecorate.exe   ?apiname@@YA_NEEPAD@Z   
bool __cdecl apiname(unsigned char,unsigned char,char *)

**C++修飾名字的用途**

**DLL輸出的C++函數**
在Windows平臺上，使用dllexport關鍵字直接輸出C++函數時，DLL的用戶看到的是修飾後的函數名字. 如果不希望使用複雜的C++修飾後的函數名，替代辦法是在DLL的.def文件中定義輸出函數的別名，或者把函數聲明為extern "C". 

**在彙編源程序或者內聯彙編中引用C/C++函數**
在彙編源程序或者內聯彙編中引用了C/C++函數，就必須引用該函數的修飾名字。

**參考文獻==