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與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@4int __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)。為什麼不考慮函式的非靜態區域性變數、類的非靜態資料成員呢？因為編譯器把函式的非靜態區域性變數翻譯為codice_1；把類的非靜態資料成員翻譯為codice_2。下文使用了巴科斯-瑙爾正規化(BNF)來表述一些name mangling的語法定義。方括號[]表示該項出現0次或1次，除非在方括號後用上下角標給出該項出現的上下限。下文使用"類"，一般包含了class、struct、union等複合資料型別。基本結構.codice_3C++中被mangled的名字都使用問號(?)開始，因為這與用字母數字(alphanumeric)、下劃線(_)或codice_4開頭的C語言程式中的被mangled的名字能完全區分開。C++中的變數與函式，可定義於名字空間或類中。所以變數與函式受到名字空間或類的限定(qualification)。而名字空間、類又可以巢狀(nest)。codice_5表示變數與函式的名字及所定義的名字空間的巢狀情況。並採用與C++程式中作用域巢狀相反的順序編碼。例如，codice_6編碼為codice_7。Name mangling時，名字的字串用codice_4符號作為結束標誌。例如codice_9，表示codice_10這個字串以codice_4符號作為結束標誌。因為名字的長度不是事先確定的。如果一個詞法單元的長度是確定的，這些詞法單元就不用codice_4作為結尾標誌，例如下文中codice_13只需用單個字母表示，則無需額外的結束標誌。codice_14是變數與函式的型別資訊的編碼表示。對於資料物件，就是它的資料型別，見資料物件的name mangling；對於函式，型別資訊就是它的返回值型別、引數型別列表、呼叫協議等情況，見函式的name mangling。資料物件的name mangling.這裡所說的資料物件，包括全域性變數(global variables)、類的靜態資料成員變數(static member variables of classes)。codice_15codice_16用於表示資料物件的類別，其編碼為：codice_13是資料物件的訪問屬性的編碼表示，一般常用的值有：A表示default物件、B表示const物件、C表示volatile物件、D表示const volatile物件。詳見小節:。需要注意的是對於指標、陣列、引用型別的物件，codice_13是對所指向的基型別的記憶體空間的訪問屬性。例如：int alpha; // mangled as ?alpha@@3HA 其中3表示全域性變數，H表示整形，A表示非const非volatilechar beta[6] = "Hello"; // mangled as ?beta@@3PADA//其中P表示為指標(或一維陣列)且指標自身為非const非volatile，//D表示char型別，兩次出現的A都表示基型別為非const非volatileclass myC{static int s_v; // mangled as ?s_v@myC@@0HA 其中0表示私有靜態資料成員函式的name mangling.函式需要分配記憶體空間以容納函式的程式碼，函式的名字實際上都是lvalue，即指向一塊可執行記憶體空間的起始地址。而函式模板的例項化(function template instantiation)，也是lvalue，需要分配記憶體空間儲存例項化後的程式碼，其name mangling在模板例項化的名字編碼中詳述。codice_19其中，codice_20給出了函式是near或far、是否為靜態函式、是類成員函式還是全域性函式、是否為虛擬函式、類成員函式的訪問級別等基本資訊。需要注意，far屬性僅適用於Windows 16位環境，32位或64位環境下使用扁平(flat)記憶體地址模型，函式只能具有near屬性。類成員函式的codice_21是指是否為只讀成員函式(constant member function). 如果不是const，則編碼為A；如果是const，則編碼為B；如果是類的靜態成員函式，則省略該項，因為靜態成員函式沒有this指標，無法修改類物件的資料。codice_22是指函式的呼叫協議。詳見呼叫協議的編碼。常見的呼叫協議的編碼為：__cdecl是A, __pascal是C, __thiscall是E, __stdcall是G, __fastcall是I。在Windows 64位編譯環境中唯一允許的呼叫協議的編碼是A。codice_23是指函式的返回值的是否有const或volatile屬性：codice_24如果函式的返回值不具有const或volatile性質，那麼該項在編碼中被省略；但是如果函式的返回型別是class、struct或union等複合資料型別，此項在編碼中是必需的，不能省略。codice_25是函式的返回值的資料型別，詳見型別的編碼表示。codice_26是函式的形參列表(parameter list)的資料型別的編碼。按照形參從左到右順序給每個引數的資料型別編碼，詳見型別的編碼表示。引數型別列表的編碼為：codice_33是函式丟擲異常的說明，即異常規範(exception specification)。截至Visual C++ 2010，仍是接受但沒有實現異常規範。因此這一項編碼仍然保持為字元codice_31。舉例：void Function1 (int a, int * b); /*mangled as ?Function1@@YAXHPAH@Z其中 Y: 全域性函式A: cdecl呼叫協議X: 返回型別為voidH: 第一個形參型別為intPAH：第二個形參型別為整形指標@： 形參表結束標誌Z: 預設的異常規範 */int Class1::MemberFunction(int a, int * b); /* mangled in 32-bit mode as?MemberFunction@Class1@@QAEHHPAH@Z其中 Q: 類的public functionA: 成員函式不是const member functionE: thiscall呼叫協議H: 返回值為整形H: 第一個形參型別為整形PAH: 第二個形參為整形指標 */C++語言name mangling細節.名字的編碼.C++程式中，需要考慮的具有全域性儲存屬性的變數名字及函式的名字。這些名字受namespace、class等作用域(scope)的限定。因此，帶完整限定資訊的名字定義為：codice_35其中，codice_36是組成名字識別符號的ASCII碼串，規定必須以codice_4作為結尾字尾。codice_38是指建構函式、解構函式、運算子函式(operator function)、虛表(vtable)等內部資料結構等，詳見特殊名字的編碼。codice_39就是指C++程式中的名字空間與類。codice_40是指例項化後的函式模板或類模板，詳見模板例項化的名字編碼。codice_41是對一個函式內部用花括號codice_42給出的不同的作用域(scope)的編號表示，詳見編號名字空間。codice_43是對一個mangled name的ASCII碼串中重複出現的型別或名字的簡寫表示方法，詳見重複出現的名字與型別的簡寫表示。codice_44是對靜態區域性變數所在的函式名的表示方法，詳見巢狀的名字。數的編碼.Visual C++的name mangling，有時會用到數(number)，例如多維陣列的維數等。數的編碼使用一套獨特的方法：例如，8編碼為7。29110編碼為BCD@。-1510編碼為?P@。特殊名字的編碼.特殊名字(special names)是指類的建構函式、解構函式、運算子函式、類的內部資料結構等的名字，表示為字首codice_45後跟編碼。已知的編碼：虛表的mangled name是codice_46,字首codice_47用在codice_48之中. 其含義未知。下表是RTTI相關的編碼，都是在codice_49後跟一個數字. 有些編碼還後跟引數.模板例項化的名字編碼.函式模板例項化後，就是一個具體的函式。類模板例項化後，就是一個具體的類資料型別。codice_50模板的名字以字首codice_51開始。codice_52可以代替codice_53，codice_54可以代替codice_55。模板實參(template argument)，可以分為型別名字(typename)與非型別(non-type)的常量兩類。如果是型別名字或類作為模板實參，那麼其編碼格式詳見型別的編碼表示。如果模板實參是常量(constant)，則已知的編碼格式列為下表：上表中，用"a", "b", "c"表示有符號整數，而"x", "y", "z"表示無符號整數. 這些有符號整數或無符號整數的編碼格式，詳見數的編碼。上表中，實數值的編碼表示codice_56，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開始的編號名字空間, 是codice_57.例如：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。用單個字元編碼的型別不參加編號。對不是該資料型別首次出現的形參，用該型別的單個數字的編號代替該資料型別的多個字元的編碼來簡寫表示。排在前十名之後的多字元編碼的資料型別，不再簡寫。函式的返回值型別不參與此編號及簡寫。如果函式的返回型別或者形參是函式指標型，那麼函式指標型的形參也參與型別排序編號與簡寫，但函式指標型的返回值型別不參與型別排序編號與簡寫。在對型別排序編號時，先編號函式指標型內部的形參的資料型別，再編號函式指標型本身。例如，假如函式的第一個形參是codice_58，那麼codice_59編號為0, codice_60編號為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重複出現的名字的簡寫.在對碼串完成重複出現的型別的簡寫後，再對結果中所有不同的名字排序編號，從0編號到9。排在前10個之後的名字不再編號、簡寫。這裡的名字是指函式、class、struct、union、enum、例項化的帶實參的模板等等的以codice_4作為結尾字尾的名字。例如，在codice_62中, 0指代codice_63, 1指代codice_64，?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) 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 specificationreturn 0;模板例項化時重複出現的實參的簡寫.模板例項化的名字編碼，基本上就是用模板的名字與模板實參作為一個整體，當作codice_65使用。因此模板例項化的名字在參與完成重複出現的型別簡寫與重複出現的名字簡寫兩步處理之後，再單獨處理模板例項化的模板實參表，對其內部重複出現的名字的編號與簡化。其方法與重複出現的名字簡寫的處理相同。例1：template class tc{int i;public: void __stdcall func(tc p2){};class Ce{};int main(int argc, char *argv[])tc ins;ins.func(ins,ins); /* void tc)mangled as ?func@?$tc@VCe@@V1@@@QAGXV1@0@Z *///?$tc@VCe@@V1@@表示例項化的類模板tc，其中的V1@是類模板的實參的重複名字簡寫；//函式func的作用域是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{};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;int main(int argc, char *argv[])class1 vv;name9(class1::name9)mangled as ??$name9@V0class1@@@@YAXVname9@class1@@@Z */// 此例說明模板例項化在做重複名字簡化時，// 模板實參中的name9與模板名字name9相同，因而簡化為編號0return 0;資料型別的編碼表示.這裡所說的型別，包括資料型別、函式指標的型別、函式模板、類模板等不需要分配記憶體空間的一些概念屬性。型別是資料物件與函式這兩類實體的屬性。對於簡單的資料型別，其編碼往往就是一個字母。如int型別編碼為X。對各種衍生的資料型別、函式指標、模板等，在下文中分述。codice_27表示codice_28 僅當用於表示函式的返回型別、形參表的終止或指標的基型別, 否則該編碼表示cointerface. 程式碼 codice_31 (表示ellipsis)僅用於表示不定長度的形參列表(varargs).指標、引用、陣列的型別編碼.codice_69其中codice_70作為字首，用於區分各種情況的指標、引用、陣列。指標自身是const還是volatile等訪問屬性，由codice_70確定。共有八種情況：codice_13表示所指向的基型別(Referred type)是否具有const或volatile等訪問屬性，詳見小節：codice_13。codice_74表示指標或引用的基型別(Referred type)，或陣列的成員型別(element type)。其編碼詳見資料型別的編碼表示。codice_75表示多維陣列的基礎維度資訊，其格式為：codice_76。注意，這裡使用的數字，要用Visual C++ name mangling特有的數字編碼方法，詳見數的編碼。可見，C++語言的陣列是對連續儲存資料的記憶體的直接隨機訪問(random access)的手段；因此一維陣列視作指標，陣列訪問是否越界，完全由程式設計者負責；而對多維陣列，必須知道除了第一維之外其它各維的長度，才能做到直接隨機訪問，所以多維陣列作為函式形參時，必須已知其除了第一維之外其它各維的長度，這些資訊都被編入了陣列的mangled name中。對於函式指標型別的編碼，其codice_74為函式呼叫介面資訊，包括使用的呼叫協議、返回值型別、形參型別、允許丟擲的異常等，詳見函式指標型別的編碼。類成員指標的型別編碼，詳見類成員指標的型別編碼。類成員函式指標的型別編碼，詳見類成員函式指標的型別編碼。2003年x86-64位處理器問世後，第一批64位Windows平臺的C++編譯器曾經使用codice_78作為陣列型別的字首修飾符(type modifier). 但不久就改回了32位Windows平臺C++編譯器使用的codice_79字首.需注意的是，全域性陣列型別被編碼為P(指標型)，同時作為函式形參的陣列型別被編碼為Q(常量指標). 這與其本來含義恰恰相反——全域性陣列型的變數名字表示某塊記憶體地址，該名字不能再改為指向其它記憶體地址；而作為函式形參的陣列型變數的名字所表示的記憶體地址是可以修改的。但陣列型別這種編碼方法已經被各種C++編譯器廣泛接受。顯然，這是為了與老的程式碼保持向後相容。例如：int ia[10]; //ia是陣列型別的非函式形參的變數int main(int argc, char *argv[]) //argv是陣列型別的形參, 其型別為 (char *)[]int j=*(ia++); //編譯錯誤！ia是隻讀的lvalue，不能完成地址的++操作char *c= *(argv++); //編譯正確！argv是可以修改的lvaluereturn 0;例如：typedef int * p1; // coded as PAH 其中P表示default訪問屬性的指標，//A表示對基型別的default訪問屬性，H表示基型別為inttypedef 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 pointertypedef int * volatile p6; //coded as RAH 其中R表示是volatile pointertypedef int * const volatile p7; //coded as SAH 其中S表示是const volatile pointertypedef volatile int * const p6; //coded as QCH 例如這是一個外部IO裝置輸入資料的記憶體地址typedef int &amp;r1; // coded as AAH 其中第一個A表示左值引用型別(l-value reference type)typedef int&amp;&amp; r2; //coded as $$QAH 其中$$Q表示是右值引用型別(r-value reference type)typedef const int&amp;&amp; r3; //coded as $$QBH 其中B表示基型別是const屬性typedef int[8] a1; // global array coded as PAHtypedef int[10][8] a2; //global array coded as PAY07H 其中Y標誌多維陣列，0是(維數-1)即1的編碼// 7是第二維長度8的編碼typedef int[4][16][5] a3; //global array coded as PAY1BA@4H 其中1為(維數-1)即2的編碼//BA@是第二維長度16的編碼(16進位制的10)，4是第3維長度5的編碼int[7][6] // 作為函式形參時，該資料型別編碼為 QAY05H函式指標型別的編碼.函式指標的型別資訊，包括函式返回型別，函式形參型別，呼叫協議等。以字首codice_80開始。各項具體定義可式的型別資訊編碼：codice_81一般地，codice_70取值為codice_79，意為指標；codice_13取值為6，意為指標的基型別為near屬性的非成員函式。例如：typedef const int (__stdcall *FP) (int i); /* coded as P6G?BHH@Z */// 其中?B表示為const類成員指標的型別編碼.指向類成員的指標，其編碼為codice_85其中各項的定義詳見指標、引用、陣列的型別編碼。注意，codice_13是指基型別的屬性，常用的值為：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的最末尾處對基型別訪問屬性的編碼不同於普通的指標，要在最後加上所指向類的帶完整限定資訊的名字：codice_87有的，類成員指標、類成員函式指標的name mangling都必須以Q1@作為結尾，以替代codice_13。從下述幾例可以看出，這種說法是錯誤的。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@@Hvoid 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)，遵從指標型別編碼的一般規則。但與函式指標型別的編碼相比，多了一項codice_89，表示所指的函式是否為只讀成員函式(constant member function)。codice_90一般地，codice_70取值為codice_79，意為指標；codice_13取值為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，對基型別訪問屬性的編碼codice_13不同於普通的指標，要在最後加上所指向類的帶完整限定資訊的名字。codice_95上述定義中，codice_13取值一般是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)的編碼.codice_97其中複合型別的種類作為字首，union編碼為T, struct編碼為U, class編碼為V, coclass編碼為X, cointerface編碼為Y。複合型別的帶限定的名字codice_5，是指按照名字所在的名字空間、所屬的類，逐級列出限定情況(qualifier)，詳見名字的編碼。經常可以看到複合型別的編碼以@@兩個字元結尾，這是因為第一個@表示複合型別名字的字串結束，第二個@表示限定情況的結束(即作用域為全域性，限定情況為空)。編寫程式碼時，經常要用到類的前向宣告(forward declaration)，即提前宣告這個名字是個類，但類的成員尚未給出。例如:class myClassName; //mangled type name is VmyClassName@@class myClassName::embedClassName; //mangled type name is VembedClassName@VmyClassName@@列舉型別(enum)的編碼.codice_99其中，W為列舉型別字首詞。codice_100為列舉型別的帶限定的名字，是指按照名字所在的名字空間、所屬的類，逐級列出限定情況(qualifier)，詳見名字的編碼。列舉實際使用的資料型別，編碼如下：例如：enum namex:unsigned char {Sunday, Monday}; // enum-type coded as W4namex@@看起來Visual C++已經把所有列舉型別用int型實現，因此列舉的基型別(The underlying type of the enumeration identifiers)的編碼總是為4codice_13.codice_13用於普通的資料物件，表示其是否具有const、volatile等訪問屬性；用於指標、陣列、引用型別，則表明對基型別的訪問屬性，而指標自身是否為const、volatile等屬性，則專由codice_70編碼表示。codice_13用於函式指標時，表示該指標所指向的基型別是函式。但與指向資料物件的普通指標不同——函式指標指向的基型別也有自己的記憶體空間，只是這塊記憶體空間必定是隻讀的、可執行的，因此函式指標所指向的基型別記憶體空間不存在const、volatile等訪問屬性。codice_13的取值情況：codice_13可以有0個或多個字首:__based()屬性的變數.指標變數的__based()屬性是Microsoft的C++語言擴充套件. 這一屬性編碼為:例如：int *pBased; // mangled name: ?pBased@@3PAHAint __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。codice_20.codice_20給出了函式是near或far、是否為靜態函式、是否為虛擬函式、類成員函式的訪問級別等資訊：上表中的thunk函式，是指在多繼承時，由編譯器生成的包裝函式(warpper function)，用於多型呼叫實際已被子類對應函式覆蓋(overrided)的父類虛擬函式，並把指向父類的this指標調整到指向子類的起始地址。呼叫協議的編碼.64位程式設計時，唯一可用的呼叫協議的編碼是A檢視Visual C++的函式的修飾後的名字.有多種方法，可以方便地檢視一個函式在編譯後的修飾名字：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)由修飾名字反查其未修飾時的原名.C:\&gt;undname.exe ??$name9@V0class1@@@@YAXVname9@class1@@@ZMicrosoft (R) C++ Name UndecoratorCopyright (C) Microsoft Corporation. All rights reserved.Undecoration of :- "??$name9@V0class1@@@@YAXVname9@class1@@@Z"is :- "void __cdecl name9(class class1::name9)"//UnDecorate.cppint _tmain(int argc, _TCHAR* argv[])TCHAR szUndecorateName[256];memset(szUndecorateName,0,256);if (2==argc)::UnDecorateSymbolName(argv[1],szUndecorateName,256,0);std::cout«szUndecorateName«std::endl;return 0;編譯後，執行上述程式：C:\&gt;UnDecorate.exe ?apiname@@YA_NEEPAD@Zbool __cdecl apiname(unsigned char,unsigned char,char *)C++修飾名字的用途.DLL輸出的C++函式.在Windows平臺上，使用dllexport關鍵字直接輸出C++函式時，DLL的使用者看到的是修飾後的函式名字. 如果不希望使用複雜的C++修飾後的函式名，替代辦法是在DLL的.def檔案中定義輸出函式的別名，或者把函式宣告為extern "C".在彙編源程式或者內聯彙編中引用C/C++函式.在彙編源程式或者內聯彙編中引用了C/C++函式，就必須引用該函式的修飾名字。