前言

在开始的时候,源文件与目标文件的函数的符号名是相同的。后来UNIX平台和C语言发明时,已经存在很多汇编编写的库和目标文件,一个C程序要使用他们,就不能定义这些库中定义的函数和变量的名字作为符号,否则会产生冲突。为了防止类似的符号名冲突,UNIX下的C语言就规定,C语言源代码文件中的所有全局的变量和函数经过编译以后,相对应的符号名前加上下划线”“。而Fortran语言的源代码经过编译以后,所有的符号名前加上”“,后面也加上”_”。比如一个C语言函数”foo”,那么它编译后的符号名就是”_foo”;如果是Fortran语言,就是”foo“。 这种方法能够暂时减少多种语言目标文件之间的符号冲突的概率,但还是没有从根本上解决符号冲突的问题。比如同一种语言编写的目标文件还有可能会产生符号冲突,于是像C++这样的后来设计的语言开始考虑到了这个问题,增加了名称空间(Namespace)的方法来解决多模块的符号冲突问题。 在现在的Linux下的GCC编译器中,默认情况下已经去掉了在C语言符号前加”“的这种方式;但是Windows平台下的编译器还保持的这样的传统,比如Visual C++编译器就会在C语言符号前加”“,GCC在Windows平台下的版本(cygwin、mingw)也会加”_”。GCC编译器也可以通过参数选项”-fleading-underscore”或”-fno-leading-underscore”来打开和关闭是否在C语言符号前加上下划线。

符号修饰

int func(int);  
float func(float);  
class C {  
    int func(int);  
    class C2 {  
        int func(int);  
    };  
};  
namespace N {  
    int func(int);  
    class C {  
        int func(int);  
    };  
} 

上面的6个函数签名在GCC编译器下,相对应的修饰后名称如下所示:

函数签名                       符号名
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。

C++中的全局变量和静态变量也有同样的机制

对于全局变量来说,它跟函数一样都是一个全局可见的名称,它也遵循上面的名称修饰机制,比如一个名称空间foo中的全局变量bar,它修饰后的名字为:_ZN3foo3barE。

变量的类型并没有被加入到修饰后名称中,所以不论这个变量是整形还是浮点型甚至是一个全局对象,它的名称都是一样的。

名称修饰机制也被用来防止静态变量的名字冲突。

比如 main() 函数里面有一个静态变量叫foo,而 func() 函数里面也有一个静态变量叫foo。

为了区分这两个变量,GCC会将它们的符号名分别修饰成两个不同的名字”_ZZ4mainE3foo”和”_ZZ4funcE3foo”.



C语言      C++符号修饰

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!