c c++函数名编译符号修饰符
前言
函数的名字修饰(Decorated Name)就是编译器在编译期间创建的一个字符串。用来指明函数的定义或原型。
LINK程序或其它工具有时须要指定函数的名字修饰来定位函数的正确位置。
多数情况下程序猿并不须要知道函数的名字修饰。LINK程序或其它工具会自己主动区分他们。
当然,在某些情况下须要指定函数的名字修饰,比如在C++程序中, 为了让LINK程序或其它工具可以匹配到正确的函数名字,就必须为重载函数和一些特殊的函数(如构造函数和析构函数)指定名字装饰。
还有一种须要指定函数的 名字修饰的情况是在汇编程序中调用C或C++的函数。
假设函数名字,调用约定。返回值类型或函数參数有不论什么改变,原来的名字修饰就不再有效,必须指定新的 名字修饰。
C和C++程序的函数在内部使用不同的名字修饰方式,以下将分别介绍这两种方式
1、C编译器的函数名修饰规则
对于__stdcall调用约定,编译器和链接器会在输出函数名前加上一个下划线前缀,函数名后面加上一个“@”符号和其參数的字节数。比如 _functionname@number。
2. C++编译器的函数名修饰规则
无论 __cdecl,__fastcall还是__stdcall调用方式,函数修饰都是以一个“?”開始,后面紧跟函数的名字。再后面是參数表的開始标识和 依照參数类型代号拼出的參数表。
參数表标识:
__stdcall方式,是“@@YG”;
__cdecl方式,是“@@YA”;
__fastcall方式,是“@@YI;
参数类型 | 拼写代号 |
---|---|
void | X |
char | D |
unsigned char | E |
short | F |
int | H |
unsigned int | I |
long | J |
unsigned long(DWORD) | K |
float | M |
double | N |
bool | _N |
struct | U |
指针 | PA |
const指针 | PB |
PA or PB后面的代号表明指针类型。
假设同样类型的指针连续出现,以“0”取代,一 个“0”代表一次反复。
U表示结构类型。通常后跟结构体的类型名,用“@@”表示结构类型名的结束。
參数表后以“@Z”标识整个名字的结束。假设该函数无參数,则 以“Z”标识结束。
对于C++的类成员函数(其调用方式是thiscall)。函数的名字修饰与非成员的C++函数稍有不同,首先就是在函数名字和參数表之间插入以“@”字 符引导的类名。其次是參数表的開始标识不同,公有(public)成员函数的标识是“@@QAE”,保护(protected)成员函数的标识是 “@@IAE”,私有(private)成员函数的标识是“@@AAE”,假设函数声明使用了constkeyword,则对应的标识应分别为 “@@QBE”,“@@IBE”和“@@ABE”。
假设參数类型是类实例的引用。则使用“AAV1”,对于const类型的引用。则使用“ABV1”。
例子:
1 |
|
其函数修饰名为: ?Function1@@YGHPADK@Z
1 |
|
其函数修饰名则为?Function2@@YGXXZ
1 |
|
对于成员函数Function,其函数修饰名为?Function@CTest@@AAEXH@Z 对于成员函数CopyInfo,其函数修饰名为 “?CopyInfo@CTest@@IAEXABV1@@Z” DrawText是一个比較复杂的函数声明。不仅有字符串參数。还有结构体參数和HDC 句柄參数。须要指出的是HDC实际上是一个HDC结构类型的指针,这个參数的表示就是“PAUHDC@@”,其完整的函数修饰名为 “?DrawText@CTest@@QAEJPAUHDC__@@JPBDUtagRGBQUAD@@E_N@Z”。 对于InsightClass是一个共有的const函数。它的成员函数标识是“@@QBE”,完整的修饰名就是 “?InsightClass@CTest@@QBEJK@Z”。
不管是C函数名修饰方式还是C++函数名修饰方式均不改变输出函数名中的字符大写和小写。这和PASCAL调用约定不同,PASCAL约定输出的函数名无不论什么修饰且所有大写。
3、相关函数
1 |
|
被包含于头文件<dbghelp.h>
参数说明:
1 |
|
flag值:
值 | 含义 |
---|---|
UNDNAME_32_BIT_DECODE 0x0800 |
反修饰 32 位已修饰名。 |
UNDNAME_COMPLETE 0x0000 |
启用完全的反修饰。 |
UNDNAME_NAME_ONLY 0x1000 |
只反修饰初等声明的名称。返回 [作用域::]名称 。不展开模板形参。 |
UNDNAME_NO_ACCESS_SPECIFIERS 0x0080 |
禁用成员的访问指定符的展开。 |
UNDNAME_NO_ALLOCATION_LANGUAGE 0x0010 |
禁用声明语言说明符的展开。 |
UNDNAME_NO_ALLOCATION_MODEL 0x0008 |
禁用声明模型的展开。 |
UNDNAME_NO_ARGUMENTS 0x2000 |
不反修饰函数参数。 |
UNDNAME_NO_CV_THISTYPE 0x0040 |
禁用初等声明的 this 类型上的 CodeView 修饰符的展开。 |
UNDNAME_NO_FUNCTION_RETURNS 0x0004 |
禁用初等声明的返回类型展开。 |
UNDNAME_NO_LEADING_UNDERSCORES 0x0001 |
从 Microsoft 关键字中移除前导下划线。 |
UNDNAME_NO_MEMBER_TYPE 0x0200 |
禁用成员的 static 或 virtual 属性的展开。 |
UNDNAME_NO_MS_KEYWORDS 0x0002 |
禁用 Microsoft 关键字的展开。 |
UNDNAME_NO_MS_THISTYPE 0x0020 |
禁用初等声明的 this 类型上的 Microsoft 关键字的展开。 |
UNDNAME_NO_RETURN_UDT_MODEL 0x0400 |
禁用用户定义类型返回的 Microsoft 模型的展开。 |
UNDNAME_NO_SPECIAL_SYMS 0x4000 |
不反修饰特殊名称,如 vtable 、 vcall 、 vector 和 metatype 等。 |
UNDNAME_NO_THISTYPE 0x0060 |
禁用 this 类型上的所有修饰符。 |
UNDNAME_NO_THROW_SIGNATURES 0x0100 |
禁用函数和函数指针的 throw 签名的展开。 |
返回值
若函数成功,则返回 UnDecoratedName 缓冲区中的字符数,不包括 NULL 终止符。
若函数失败,则返回值为零。欲取得额外的错误信息,需调用 GetLastError 。
若函数失败且返回零,则 UnDecoratedName 缓冲区的内容不确定。
例1
1 |
|
1 |
|
例2
1 |
|
4、神奇的事
因为C的编译方式和C++有点不同,所以会导致同一个函数,各自在两种语言的的环境下名字修饰会不一样 例如:
long MakeFun(long lFun);
在C下是_MakeFun@4
,但是在CPP下则是MakeFun@@YGJJ@Z