Visual_C++_name_mangling

**Name mangling**，或者 **Decorated Name**，是指程式設計語言中具有儲存性質的物件的名字被編譯器覆寫，以適合編譯器、連結器 (linker)、組譯器 (assembler) 使用。所謂的具有儲存性質的物件，即 lvalue 物件，是指要實際占用記憶體空間、有記憶體位址的那些實體物件，例如：變數 (variables)、函式、函式指標等。C++ 中的純虛擬函式作為特例也屬於這一範疇。而資料類型 (data type) 就不屬於具有儲存性質的物件。Name mangling 如何翻譯成中文，尚無廣泛接受的譯法。可翻譯作**改名**或**名字修飾**(name decorating)。

對於支援多載 (overload) 的程式設計語言，name mangling 是必需的因而具有特別重要意義。C++ 允許函式多載

C++ 也允許變數名稱在不同限定（qualifier) 下同名：

因此 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 協定). 例如：

注意在 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>@]∞0 @
``` ``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。

### 資料物件的 name mangling

這裡所說的資料物件，包括全域變數 (global variables)、類的靜態資料成員變數 (static member variables of classes)。

`` ```
 <数据对象的name mangling> ::= ?<name>@[<namespace>@]∞0@<data access modifier>
         <data type><CV Modifier> 
``` ``
`<data access modifier>` 用於表示資料物件的類別，其編碼為：

`<CV Modifier>` 是資料物件的存取屬性的編碼表示，一般常用的值有：A 表示 default 物件、B 表示 const 物件、C 表示 volatile 物件、D 表示 const volatile 物件。詳見小節:<CV Modifier>。需要注意的是對於指標、陣列、參照類型的物件，`<CV Modifier>` 是對所指向的基本類型的記憶體空間的存取屬性。

例如：

### 函式的 name mangling

函式需要分配記憶體空間以容納函式的代碼，函式的名字實際上都是 lvalue，即指向一塊可執行記憶體空間的起始位址。而函式模板的實例化 (function template instantiation)，也是 lvalue，需要分配記憶體空間儲存實例化後的代碼，其 name mangling 在模板實例化的名字編碼中詳述。

`` ```
 <全局函数的name mangling> ::= ?<function name>@ [<namespace>@]∞0 @ <func modifier> 
         <calling conv> [<storage ret>] <return type> <parameter type>∞1 <throw type> 
 <成员函数的name mangling> ::= ?<function name>@ [<namespace>@]∞0 @ <func modifier> 
         [<const modifier>]<calling conv> [<storage ret>] <return type> 
         <parameter type>∞1 <throw type> 
``` `` 其中，`<func modifier>` 給出了函式是 near 或 far、是否為靜態函式、是類別成員函式還是全域函式、是否為虛擬函式、類別成員函式的存取級別等基本資訊。需要注意，far 屬性僅適用於 Windows 16 位元環境，32 位元或 64 位元環境下使用扁平 (flat) 記憶體位址模型，函式只能具有 near 屬性。

類別成員函式的 `<const modifier>` 是指是否為唯讀成員函式 (constant member function). 如果不是 const，則編碼為 A；如果是 const，則編碼為 B；如果是類的靜態成員函式，則省略該項，因為靜態成員函式沒有 this 指標，無法修改類物件的資料。

`<calling conv>` 是指函式的呼叫協定。詳見呼叫協定的編碼。常見的呼叫協定的編碼為：\_\_cdecl 是 A, \_\_pascal 是 C, \_\_thiscall 是 E, \_\_stdcall 是 G, \_\_fastcall 是 I。在 Windows 64 位元編譯環境中唯一允許的呼叫協定的編碼是 A（詳見 64 位元程式的呼叫約定）。

`[<storage ret>]` 是指函式的返回值的是否有 const 或 volatile 屬性：

```
 <storage ret> ::= ？<CV Modifier>   
```

如果函式的返回值不具有 const 或 volatile 性質，那麼該項在編碼中被省略；但是如果函式的返回類型是 class、struct 或 union 等複合資料類型，此項在編碼中是必需的，不能省略。

`<return type>` 是函式的返回值的資料類型，詳見類型的編碼表示。

`<parameter type>` 是函式的形參列表 (parameter list) 的資料類型的編碼。按照形參從左到右順序給每個參數的資料類型編碼，詳見類型的編碼表示。參數類型列表的編碼為：

* `X` (即函式沒有參數，或者說參數為 `void`，該編碼也是列表的結束標誌)
* type1 type2 ... typeN `@` (正常 N 個形參。以 `@` 作為列表的結束標誌)
* type1 type2 ... `Z` (形參表最後一項為...，即 ellipsis，其編碼 `Z` 也標誌著列表的結束)

`<throw type>` 是函式丟擲異常的說明，即異常規範 (exception specification)。截至 Visual C++ 2010，仍是接受但沒有實現異常規範。因此這一項編碼仍然保持為字元 `Z`。

舉例：

## C++ 語言 name mangling 細節

### 名字的編碼

C++ 程式中，需要考慮的具有全域儲存屬性的變數名字及函式的名字。這些名字受 namespace、class 等作用域 (scope) 的限定。因此，帶完整限定資訊的名字定義為：

```
 <Qualified Name> ::= <Basic Name> [<Qualifier>]∞0 @
 <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>` 是對靜態局部變數所在的函式名的表示方法，詳見巢狀的名字。

#### 數的編碼

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[ <class name> ]∞0 @6B@`,

字首 `_P` 用在 `?_PX` 之中。其含義未知。

下表是 RTTI 相關的編碼，都是在 `_R` 後跟一個數字。有些編碼還後跟參數.

#### 模板實例化的名字編碼

函式模板實例化後，就是一個具體的函式。類別模板實例化後，就是一個具體的類資料類型。

`` ```
<类模板实例化的名字> ::= ?$ <类模板的名字> <模板实参的编码>  
<函数模板实例化的名字> ::= ?$ <函数模板的名字> <模板实参的编码> 
<函数模板实例化的名字manging> ::= ?$ <函数模板的名字> <模板实参的编码> <函数的类型信息>
``` `` 模板的名字以字首 `?$` 開始。`?$ <函数模板的名字> <函数模板实参的编码>` 可以代替 `<function name>`，`?$ <类模板的名字> <类模板实参的编码>` 可以代替 `<class name>`。

模板實參 (template argument)，可以分為類型名字 (typename) 與非類型 (non-type) 的常數兩類。如果是類型名字或類作為模板實參，那麼其編碼格式詳見類型的編碼表示。如果模板實參是常數 (constant)，則已知的編碼格式列為下表：

上表中，用 _a_, _b_, _c_ 表示有符號整數，而 _x_, _y_, _z_ 表示無符號整數。這些有符號整數或無符號整數的編碼格式，詳見數的編碼。上表中，實數值的編碼表示 `$2ab`，a、b 都是有符號整數，但計算實數的值時，實際上規範到以 10 為基數的科學計數法的表示形式。

例如：

#### 編號命名空間

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

特例情況是，以？A 開始的編號命名空間，是 `('anonymous namespace')`.

例如：

#### 重複出現的名字與類型的簡寫

在對一個名字做 mangling 時，用簡寫方法表示非首次出現的同一個名字或同一個類型。整個簡寫過程需要對 ASCII 碼串做 3 次掃描處理：

1. 對函式或函式指標的形參表中的重複出現的參數資料類型的編碼簡寫；
2. 把第一步獲得結果中重複出現的名字的編碼簡寫；
3. 把第二步獲得的結果中的模板實例化 (模板名字 + 模板實參表) 內部重複出現的名字的編碼簡寫。

##### 重複出現的類型的簡寫

這適用於函式與函式指標的形參列表。只有編碼超過一個字元的類型參與簡寫，包括指標、函式指標、參照、陣列、bool、\_\_int64、class、實例化的模板類、union、struct、enum 等資料類型。形參表中前 10 種多字元編碼的類型按照出現次序依次編號為 0,1,...,9。用單個字元編碼的類型不參加編號。對不是該資料類型首次出現的形參，用該類型的單個數字的編號代替該資料類型的多個字元的編碼來簡寫表示。排在前十名之後的多字元編碼的資料類型，不再簡寫。函式的返回值類型不參與此編號及簡寫。

如果函式的返回類型或者形參是函式指標型，那麼函式指標型的形參也參與類型排序編號與簡寫，但函式指標型的返回值類型**不參與**類型排序編號與簡寫。在對類型排序編號時，先編號函式指標型內部的形參的資料類型，再編號函式指標型本身。例如，假如函式的第一個形參是 `void (__cdecl*)(class alpha, class beta)`，那麼 `class alpha` 編號為 0, `class beta` 編號為 1, 最後整個函式指標編號為 2.

例如：

##### 重複出現的名字的簡寫

在對碼串完成重複出現的類型的簡寫後，再對結果中所有不同的名字排序編號，從 0 編號到 9。排在前 10 個之後的名字不再編號、簡寫。這裡的名字是指函式、class、struct、union、enum、實例化的帶實參的模板等等的以 `@` 作為結尾字尾的名字。例如，在 `alpha@?1beta@@(即beta::'2'::alpha)` 中，0 指代 `alpha@`, 1 指代 `beta@`，?1 是編號命名空間『2』的編碼。特殊名字、編號命名空間的名字都不參加此輪名字的排序編號與簡寫。
例如：

對於實例化模板，模板名字後跟模板實參作為一個整體視作一個名字，參加此輪排序編號與簡寫。而模板實參表中的參數序列，單獨處理它的編號及簡寫。例如：

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

模板實例化的名字編碼，基本上就是用模板的名字與模板實參作為一個整體，當作 `<func name>或<class name>` 使用。因此模板實例化的名字在參與完成重複出現的類型簡寫與重複出現的名字簡寫兩步處理之後，再單獨處理模板實例化的模板實參表，對其內部重複出現的名字的編號與簡化。其方法與重複出現的名字簡寫的處理相同。

例 1：

例 2：

例 3：

### 資料類型的編碼表示

這裡所說的類型，包括資料類型、函式指標的類型、函式模板、類別模板等不需要分配記憶體空間的一些概念屬性。類型是資料物件與函式這兩類實體的屬性。

對於簡單的資料類型，其編碼往往就是一個字母。如 int 類型編碼為 X。對各種衍生的資料類型（如指標）、複合的資料類型（如類）、函式指標、模板等，在下文中分述。

`X` 表示 `void` 僅當用於表示函式的返回類型、形參表的終止或指標的基本類型，否則該編碼表示 cointerface. 代碼 `Z` (表示 ellipsis) 僅用於表示不定長度的形參列表 (varargs).

#### 指標、參照、陣列的類型編碼

`` ```
 <指针类型的编码> ::= <type modifier> <CV Modifier> <base type>
 <左值引用类型的编码> ::= <type modifier> <CV Modifier> <base type>
 <右值指针类型的编码> ::= $$Q <CV Modifier> <base type>
 <一维数组类型的编码> ::= <指针类型的编码>
 <多维数组类型的编码> ::= <type modifier> <CV Modifier> <Array property><base type>
``` `` 其中 `<type modifier>` 作為字首，用於區分各種情況的指標、參照、陣列。指標自身是 const 還是 volatile 等存取屬性，由 `<type modifier>` 確定。共有八種情況：

`<CV Modifier>` 表示所指向的基本類型 (Referred type) 是否具有 const 或 volatile 等存取屬性，詳見小節：`<CV Modifier>`。

`<base type>` 表示指標或參照的基本類型 (Referred type)，或陣列的成員類型 (element type)。其編碼詳見資料類型的編碼表示。

`<Array property>` 表示多維陣列的基礎維度資訊，其格式為：`Y<数组总的维数-1><第2维的长度>...<最后的第N维的长度>`。注意，這裡使用的數字，要用 Visual C++ name mangling 特有的數字編碼方法，詳見數的編碼。可見，C++ 語言的陣列是對連續儲存資料的記憶體的直接隨機存取 (random access) 的手段；因此一維陣列視作指標，陣列存取是否越界，完全由編程者負責；而對多維陣列，必須知道除了第一維之外其它各維的長度，才能做到直接隨機存取，所以多維陣列作為函式形參時，必須已知其除了第一維之外其它各維的長度（以及總的維數），這些資訊都被編入了陣列的 mangled name 中。

對於函式指標類型的編碼，其 `<base type>` 為函式呼叫介面資訊，包括使用的呼叫協定、返回值類型、形參類型、允許丟擲的異常等，詳見函式指標類型的編碼。類別成員指標的類型編碼，詳見類別成員指標的類型編碼。類別成員函式指標的類型編碼，詳見類別成員函式指標的類型編碼。

2003 年 x86-64 位元處理器問世後，第一批 64 位元 Windows 平台的 C++ 編譯器曾經使用 `_O` 作為陣列類型的字首修飾詞 (type modifier). 但不久就改回了 32 位元 Windows 平台 C++ 編譯器使用的 `P` 字首.

需注意的是，全域陣列類型被編碼為 P (指標型)，同時作為函式形參的陣列類型被編碼為 Q (常數指標). 這與其本來含義恰恰相反 —— 全域陣列型的變數名字表示某塊記憶體位址，該名字不能再改為指向其它記憶體位址；而作為函式形參的陣列型變數的名字所表示的記憶體位址是可以修改的。但陣列類型這種編碼方法已經被各種 C++ 編譯器廣泛接受。顯然，這是為了與老的代碼保持向下相容。例如：

例如：

#### 函式指標類型的編碼

函式指標的類型資訊，包括函式返回類型，函式形參類型，呼叫協定等。以字首 `P6` 開始。各項具體定義可參見函式的類型資訊編碼：

`` ```
<global function pointer type info> ::= <type modifier> <CV Modifier> <calling conv> 
    [<storage ret>]<return type>[<parameter type>]∞1<throw type>
``` `` 一般地，`<type modifier>` 取值為 `P`，意為指標；`<CV Modifier>` 取值為 6，意為指標的基本類型為 near 屬性的非成員函式。

例如：

#### 類別成員指標的類型編碼

指向類別成員的指標，其編碼為

`` ```
 <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。

例如：

#### 類別成員指標變數的 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@結尾：

例 2：

#### 類別成員函式指標的類型編碼

類別成員函式的指標 (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>]∞1  <throw type>
``` `` 一般地，`<type modifier>` 取值為 `P`，意為指標；`<CV Modifier>` 取值為 8，意為指標的基本類型為 near 屬性的類別成員函式。其它各項取值參見函式的 name mangling。

例如：

#### 類別成員函式的指標變數的 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

例如：

#### 複合類型 (union, struct, class, coclass, cointerface) 的編碼

```
<复合类型的编码> ::= <复合类型的种类><复合类型的带限定的名字>
```

其中複合類型的種類作為字首，union 編碼為 T, struct 編碼為 U, class 編碼為 V, coclass 編碼為 X, cointerface 編碼為 Y。複合類型的帶限定的名字 `<Qualified Name>`，是指按照名字所在的命名空間、所屬的類，逐級列出限定情況 (qualifier)，詳見名字的編碼。

經常可以看到複合類型的編碼以 @@兩個字元結尾，這是因為第一個 @表示複合類型名字的字串結束，第二個 @表示限定情況的結束 (即作用域為全域，限定情況為空)。

編寫代碼時，經常要用到類的前向聲明 (forward declaration)，即提前聲明這個名字是個類，但類的成員尚未給出。例如:

#### 列舉類型 (enum) 的編碼

`` ```
<枚举类型的编码> ::= W <枚举实际使用的数据类型> <enum-type Qualified Name>
<枚举成员的编码> ::= W <枚举实际使用的数据类型> <enumerator name>@ <enum-type Qualified Name>
``` `` 其中，W 為列舉類型字首詞。`<enum-type Qualified Name>` 為列舉類型的帶限定的名字，是指按照名字所在的命名空間、所屬的類，逐級列出限定情況 (qualifier)，詳見名字的編碼。列舉實際使用的資料類型，
編碼如下：

例如：

看起來 Visual C++ 已經把所有列舉類型用 int 型實現，因此列舉的基本類型 (The underlying type of the enumeration identifiers) 的編碼總是為 4

#### `<CV Modifier>`

`<CV Modifier>` 用於普通的資料物件，表示其是否具有 const、volatile 等存取屬性；用於指標、陣列、參照類型，則表明對基本類型的存取屬性，而指標自身是否為 const、volatile 等屬性，則專由 `<type modifier>` 編碼表示。

`<CV Modifier>` 用於函式指標時，表示該指標所指向的基本類型是函式。但與指向資料物件的普通指標不同 —— 函式指標指向的基本類型（即函式）也有自己的記憶體空間，只是這塊記憶體空間必定是唯讀的、可執行的，因此函式指標所指向的基本類型記憶體空間不存在 const、volatile 等存取屬性。

`<CV Modifier>` 的取值情況：

`<CV Modifier>` 可以有 0 個或多個字首:

##### \_\_based () 屬性的變數

指標變數的 \_\_based () 屬性是 Microsoft 的 C++ 語言擴充。這一屬性編碼為:

* `0` (意味著 `__based(void)`)
* `2<Qualified Name>` (意味著 `__based(<Qualified Name>)`)
* `5` (意味著沒有 `__based()`)

例如：

 

### 函式的類型資訊編碼

函式的類型資訊，是指呼叫函式時必須考慮的 ABI (Application Binary Interface)，包括呼叫協定、返回類型、函式形參表、函式丟擲異常的說明 (exception specification) 等，參見函式的 name mangling。

#### `<func modifier>`

`<func modifier>` 給出了函式是 near 或 far（但 far 屬性僅適用於 Windows 16 位元環境，32 位元或 64 位元環境下只能函式具有 near 屬性）、是否為靜態函式、是否為虛擬函式、類別成員函式的存取級別等資訊：

上表中的 thunk 函式，是指在多繼承時，由編譯器生成的包裝函式 (warpper function)，用於多型呼叫實際已被子類對應函式覆蓋 (overrided) 的父類別虛擬函式，並把指向父類別的 this 指標調整到指向子類的起始位址。

#### 呼叫協定的編碼

64 位元編程時，唯一可用的呼叫協定的編碼是 A

## 檢視 Visual C++ 的函式的修飾後的名字

有多種方法，可以方便地檢視一個函式在編譯後的修飾名字：

1. 直接用工具軟體 (如微軟開發環境提供的 dumpbin) 檢視 obj、exe 等二進位檔案。使用 dumplib 檢視.obj 或.lib 檔案時，使用 "/SYMBOLS" 命令列選項。
2. 編譯時使用 "/FA [c|s|u]" 編譯選項，生成帶有豐富注釋資訊的組譯源程式，其副檔名是.cod 或者.asm，可以檢視每個 C/C++ 函式的修飾名字。
3. 在源程式中使用微軟提供的預定義宏 (Microsoft-Specific Predefined Macros)—— **\_\_FUNCDNAME\_\_**，例如：

## 由修飾名字反查其未修飾時的原名

* 使用微軟 Visual C++ 中的解析修飾名字的工具軟體 undname.exe。例如：

* 使用 Windows 提供的系統呼叫 UnDecorateSymbolName () 把修飾名字翻譯為未修飾名字。UnDecorateSymbolName 在 DbgHelp.h 或 imagehlp.h 中聲明，在 DbgHelp.dll 中實現，需要使用匯入庫 DbgHelp.lib。Windows SDK 中包含了 DbgHelp.h 與 DbgHelp.lib。範例程式：

編譯後，執行上述程式：

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

### DLL 輸出的 C++ 函式

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

### 在組譯源程式或者行內組譯中參照 C/C++ 函式

在組譯源程式或者行內組譯中參照了 C/C++ 函式，就必須參照該函式的修飾名字。

## 參考文獻