Visual 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等複合數據類型。

基本結構

<C++的name mangling> ::= ?<Qualified Name> <Type Information>
 <Qualified Name> ::= <name>@ [<namespace>@]∞₀ @

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

名字的編碼

C++程序中，需要考慮的具有全局存儲屬性的變量名字及函數的名字。這些名字受namespace、class等作用域(scope)的限定。因此，帶完整限定信息的名字定義為：
 <Qualified Name> ::= <Basic Name> [<Qualifier>]∞₀ @
 <Basic Name> ::= <Name Fragment> | <Special Name>
 <Qualifier> ::= <namespace> | <class name> | <Template Instantiation> 
                | <Numbered Namespace> | <Back Reference> | <Nested Name> 
其中，<Name Fragment>是組成名字標識符的ASCII碼串，規定必須以@作為結尾後綴。<Special Name>是指構造函數、析構函數、運算符函數(operator function)、虛表(vtable)等內部資料結構等，詳見特殊名字的編碼。
<namespace>与<class name>就是指C++程序中的名字空間與類。<Template Instantiation>是指實例化後的函數模板或類模板，詳見模板實例化的名字編碼。<Numbered Namespace>是對一個函數內部用花括號{ ... }給出的不同的作用域(scope)的編號表示，詳見編號名字空間。<Back Reference>是對一個mangled name的ASCII碼串中重複出現的類型或名字的簡寫表示方法，詳見重複出現的名字與類型的簡寫表示。<Nested Name>是對靜態局部變量所在的函數名的表示方法，詳見嵌套的名字。

編號名字空間

編號名字空間(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;}

重複出現的類型的簡寫

這適用於函數與函數指針的形參列表。只有編碼超過一個字符的類型參與簡寫，包括指針、函數指針、引用、數組、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 *///FPfuncfp (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 T> class tc{
       public: void __stdcall func(tc<T>){};};int main(int argc, char *argv[]){
tc<int> ins;
ins.func(ins); /* tc<int>::func(tc<int>) mangled as ?func@?$tc@H@@QAEXV1@@Z */
                  //其中，?$tc@H@表示实例化的类模板tc<int>，里面的H表示模板实例化的实参是整型
                  //Q表示public的成员函数；A表示非只读成员函数；E表示thiscall
                  //X表示函数返回类型void；
                  //V1@表示形参为一个class，class的名字为''1''号名字，本例中即tc<int>的编码?$tc@H@；
                  //注意，本例中''0''号名字是函数名，即func@
                  //最后一个@表示函数的形参表结束；Z表示缺省的exception specification
return 0;}

模板實例化時重複出現的實參的簡寫

模板實例化的名字編碼，基本上就是用模板的名字與模板實參作為一個整體，當作<func name>或<class name>使用。因此模板實例化的名字在參與完成重複出現的類型簡寫與重複出現的名字簡寫兩步處理之後，再單獨處理模板實例化的模板實參表，對其內部重複出現的名字的編號與簡化。其方法與重複出現的名字簡寫的處理相同。
例1：
template<class T1, class T2> class tc{
int i;
     public: void __stdcall func(tc<T1,T2> p1,tc<T1,T2> p2){};};class Ce{};int main(int argc, char *argv[]){
    tc<Ce,Ce> ins;
  ins.func(ins,ins);  /* void tc<Ce,Ce>::func(tc<Ce,Ce>,tc<Ce,Ce>)                   mangled as ?func@?$tc@VCe@@V1@@@QAGXV1@0@Z */
     //?$tc@VCe@@V1@@表示实例化的类模板tc<Ce,Ce>，其中的V1@是类模板的实参的重复名字简写；
     //函数func的作用域是tc<Ce,Ce>,即tc<Ce,Ce>::func编码为func@?$tc@VCe@@V1@@@  ；//V1@表示成员函数func的第一个形参为一个类，类名为''1''号名字，本例中即tc<Ce,Ce>，这是重复名字的简写；
     //0表示该函数func的第二个形参与形参表中的''0''号形参相同，即tc<Ce,Ce>，这是重复类型的简写   
 
    return 0;}
例2：
class class1{
     public:  class class2{} ee2;};
 
union class2{};template <class T1, class T2, class T3> void func( T1,T2,T3 ){}int main(int argc, char *argv[]){
class1 a;
class2 b;
func<class2, class1::class2, class2>(b,a.ee2,b); 
   /* func<class2,class1::class2,class2>(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 <class T> void name9 ( T p1){}int main(int argc, char *argv[]){
class1 vv;
 name9<class1::name9>(vv.ee); /* void name9<class1::name9>(class1::name9)        mangled as ??$name9@V0class1@@@@YAXVname9@class1@@@Z *///       此例说明模板实例化在做重复名字简化时，//       模板实参中的name9与模板名字name9相同，因而简化为编号0 
    return 0;}

函數指針類型的編碼

函數指針的類型信息，包括函數返回類型，函數形參類型，調用協議等。以前綴P6開始。各項具體定義可參見函數的類型信息編碼：
<global function pointer type info> ::= <type modifier> <CV Modifier> <calling conv> 
    [<storage ret>]<return type>[<parameter type>]∞₁<throw type>
 
一般地，<type modifier>取值為P，意為指針；<CV Modifier>取值為6，意為指針的基類型為near屬性的非成員函數。
例如：
  typedef const  int (__stdcall *FP) (int i); /* coded as P6G?BHH@Z *///                                         其中?B表示<storage ret>为const

類成員指針的類型編碼

指向類成員的指針，其編碼為
 <pointer-to-Member Type> = <type modifier> <CV Modifier>
                            <base-class Qualified Name> <base type>

其中各項的定義詳見指針、引用、數組的類型編碼。注意，<CV Modifier>是指基類型的屬性，常用的值為：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的最末尾處對基類型訪問屬性的編碼不同於普通的指針，要在最後加上所指向類的帶完整限定信息的名字：
 <pointer-to-member name mangling> ::= ?<Qualified Name> <data access modifier>
             <pointer-to-Member Type> <CV Modifier> <base-class Qualified Name>
有的文獻稱，類成員指針、類成員函數指針的name mangling都必須以Q1@作為結尾，以替代<CV Modifier>。從下述幾例可以看出，這種說法是錯誤的。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)，遵從指針類型編碼的一般規則。但與函數指針類型的編碼相比，多了一項<const Modifier>，表示所指的函數是否為只讀成員函數(constant member function)。
<pointer-to-member-function type info> ::= 
              <type modifier> <CV Modifier> <base-class Qualified Name>
              [<const Modifier>] <calling conv> [<storage ret>] <return type> 
              [<parameter type>]∞₁  <throw type>

一般地，<type modifier>取值為P，意為指針；<CV Modifier>取值為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，對基類型訪問屬性的編碼<CV Modifier>不同於普通的指針，要在最後加上所指向類的帶完整限定信息的名字。
 <pointer-to-member name mangling> ::= ?<Qualified Name> <data access modifier>
            <pointer-to-Member-Function Type> <CV Modifier> <base-class Qualified Name>

上述定義中，<CV Modifier>取值一般是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。複合類型的帶限定的名字<Qualified Name>，是指按照名字所在的名字空間、所屬的類，逐級列出限定情況(qualifier)，詳見名字的編碼。
經常可以看到複合類型的編碼以@@兩個字符結尾，這是因為第一個@表示複合類型名字的字串結束，第二個@表示限定情況的結束(即作用域為全局，限定情況為空)。
編寫代碼時，經常要用到類的前向聲明(forward declaration)，即提前聲明這個名字是個類，但類的成員尚未給出。例如:
class myClassName; //mangled type name is VmyClassName@@class myClassName::embedClassName; //mangled type name is VembedClassName@VmyClassName@@

函數的類型信息編碼

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

DLL輸出的C++函數

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

在彙編源程序或者內聯彙編中引用C/C++函數

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