函数签名(function signature)与符号修饰(name decoration)【转】
函数签名
修饰后名称(符号名)
int func(int)
_Z4funci
float func(float)
_Z4funcf
int C::func(int)
_ZN1C4funcEi
int C::C2::func(int)
_ZN1C2C24funcEi
int N::func(int)
_ZN1N4funcEi
int N::C::func(int)
_ZN1N1C4funcEi
?
GCC的基本C++名称修饰方法如下:所有的符号都以"_Z"开头,对于嵌套的名字(在名称空间或在类里面的),后面紧跟"N",然后是各个名称空 间和类的名字,每个名字前是名字字符串长度,再以"E"结尾。比如N::C::func经过名称修饰以后就是_ZN1N1C4funcE。对于一个函数来 说,它的参数列表紧跟在"E"后面,对于int类型来说,就是字母"i"。所以整个N::C::func(int)函数签名经过修饰为 _ZN1N1C4funcEi。更为具体的修饰方法我们在这里不详细介绍,有兴趣的读者可以参考GCC的名称修饰标准。幸好这种名称修饰方法我们平时程序 开发中也很少手工分析名称修饰问题,所以无须很详细地了解这个过程。binutils里面提供了一个叫"c++filt"的工具可以用来解析被修饰过的名 称,比如:
?
函数签名
修饰后名称
int func(int)
?func@@YAHH@Z
float func(float)
?func@@YAMM@Z
int C::func(int)
?func@C@@AAEHH@Z
int C::C2::func(int)
?func@C2@C@@AAEHH@Z
int N::func(int)
?func@N@@YAHH@Z
int N::C::func(int)
?func@C@N@@AAEHH@Z
?
我们以int N::C::func(int)这个函数签名来猜测Visual C++的名称修饰规则(当然,你只须大概了解这个修饰规则就可以了)。修饰后名字由"?"开头,接着是函数名由"@"符号结尾的函数名;后面跟着由"@" 结尾的类名"C"和名称空间"N",再一个"@"表示函数的名称空间结束;第一个"A"表示函数调用类型为"__cdecl"(函数调用类型我们将在第4 章详细介绍),接着是函数的参数类型及返回值,由"@"结束,最后由"Z"结尾。可以看到函数名、参数的类型和名称空间都被加入了修饰后名称,这样编译器 和链接器就可以区别同名但不同参数类型或名字空间的函数,而不会导致link的时候函数多重定义。
?
Visual C++的名称修饰规则并没有对外公开,当然,一般情况下我们也无须了解这套规则,但是有时候可能须要将一个修饰后名字转换成函数签名,比如在链接、调试程 序的时候可能会用到。Microsoft提供了一个UnDecorateSymbolName()的API,可以将修饰后名称转换成函数签名。下面这段代 码使用UnDecorateSymbolName()将修饰后名称转换成函数签名:
?
[cpp] view plaincopy- /*?2-4.c?*?Compile:?cl?2-4.c?/link?Dbghelp.lib?
- *?Usage:?2-4.exe?DecroatedName?*/??
- #include?<Windows.h>??#include?<Dbghelp.h>??
- int?main(?int?argc,?char*?argv[]?)??{??
- char?buffer[256];??????if(argc?==?2)???
- {??UnDecorateSymbolName(?argv[1],?buffer,?256,?0?);??
- printf(?buffer?);??}???
- else???{??
- printf(?"Usage:?2-4.exe?DecroatedName\n"?);??}??
- ????return?0;??}??
由于不同的编译器采用不同的名字修饰方法,必然会导致由不同编译器编译产生的目标文件无法正常相互链接,这是导致不同编译器之间不能互操作的主要原因之一。我们后面的关于C++ ABI和COM的这一节将会详细讨论这个问题。