修饰的命名约定在各种版本的 Visual Studio 中有所不同,并且可能在不同的目标体系结构上也不同。 若要正确链接使用 Visual Studio、C 和 C++ 创建的源文件,应使用相同的编译器工具集、标志和目标体系结构编译 DLL 和库。
通过 Visual Studio 2022,Visual Studio 2015 或更高版本生成的库可由使用更高版本的 Visual Studio 生成的应用程序使用。 有关详细信息,请参阅
Visual Studio 版本之间的 C++ 二进制兼容性
。
使用修饰名
通常情况下,无需知道修饰名也可编写已成功编译和链接的代码。 修饰名是编译器和链接器内部的实现详细信息。 通常,这些工具可以处理未修饰形式的名称。 但是,在向链接器和其他工具指定函数名时,有时则需要修饰名。 例如,若要匹配重载的 C++ 函数、命名空间成员、类构造函数、析构函数和特殊成员函数,则必须指定修饰名。 有关选项标志和其他需要修饰名的情形的详细信息,请参阅正在使用的工具和选项的相关文档。
如果你更改函数名、类、调用约定、返回类型或任何参数,则修饰名也会改变。 在这种情况下,你必须获取新的修饰名,并将其用于指定了修饰名的任何位置。
在链接到用其他编程语言编写的或使用其他编译器的代码时,名称修饰也很重要。 编译器不同,则使用名称修饰约定不同。 在可执行文件链接到用另一种语言编写的代码时,必须特别留意将导出和导入的名称与调用约定相匹配。 程序集语言代码必须使用 MSVC 修饰名和调用约定来链接到使用 MSVC 编写的源代码。
C + + 函数的修饰名包含以下信息:
函数名称。
函数所属的类(如果该函数为成员函数)。 修饰可能包括含有函数所属的类的类,等等。
函数所属的命名空间(如果该函数是命名空间的一部分)。
函数参数的类型。
调用约定。
函数的返回类型。
可选的目标特定元素。 在 ARM64EC 对象中,名称中会插入
$$h
标记。
修饰名中编码有函数名和类名。 修饰名的其余部分是具有仅用于编译器和链接器的内部意义的代码。 以下示例介绍的是未修饰和修饰的 C++ 名称。
对于具有 C 链接的 ARM64EC 函数(无论是编译为 C 还是通过使用
extern "C"
),修饰名前会预置
#
。
查看修饰名
在编译包含数据、对象或者函数定义或原型的源文件之后,可以获取符号名称的修饰形式。 若要检查你的程序中的修饰名,可以使用以下方法之一:
若要使用列表查看修饰名
生成列表的方式如下:将
/FA
(列出文件类型)
编译器选项设置为带源代码的程序集 (
/FAs
),从而编译包含数据、对象或者函数定义或原型的源文件。
例如,在开发人员命令提示符处输入
cl /c /FAs example.cpp
以生成列表文件
example.asm
。
在生成的列表文件中,找到以
PUBLIC
开头、分号 (
;
) 结尾且后接未修饰的数据或函数名的行。
PUBLIC
和分号之间的符号即为修饰名。
若要使用 DUMPBIN 查看修饰名
若要在 OBJ 或 LIB 文件中查看导出的符号,请在开发人员命令提示符处输入
dumpbin /exports <obj-or-lib-file>
。
若要查找符号的修饰形式,请查找括号中的未修饰名。 修饰名位于同一行中,在未修饰名之前。
查看未修饰名
可以使用 undname.exe 将修饰名转换为其未修饰形式。 此示例演示其工作原理:
C:\>undname ?func1@a@@AAEXH@Z
Microsoft (R) C++ Name Undecorator
Undecoration of :- "?func1@a@@AAEXH@Z"
is :- "private: void __thiscall a::func1(int)"
其他 MSVC 生成工具
使用 extern
指定链接