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++函数，就必须引用该函数的修饰名字。