资讯详情

GNU C++的符号改编机制介绍(函数的名称粉碎格式解析)

转载:http://blog.csdn.net/roland_sun/article/details/43233565

众所周知,强大C 与C相比,增加了许多功能。它包括类别、命名空间和重载。

对于类,相同的函数和变量可以在不同的类别中定义,不会相互干扰。命名空间可以确保不同名称空间中的类别、函数和变量名称不会相互影响。重载可以确保函数名称可以相同,即使在相同的命名空间中,只要参数不同。

这种设计方便了程序开发者,不用担心不同开发者定义相同名称的函数。然而,这也使符号管理更加复杂。

在最终的编译和链接过程中,如何区分不同类别中的同名函数,或不同名称空间中的同名函数,或同名空间或类别中的同名重载函数?C 人们发明了所谓的符号改编(Name Mangling)机制。

其原理其实很简单,就是根据函数所在的名称空间、类别和参数,按照一定的规则重新命名函数。不同的编译器有不同的命名规则。这里我们主要介绍一下GNU C 编译器所使用的规则。主要分为以下几种情况:

1)全局变量:

也就是说,在命名空间和类之外的变量中,改编后的符号名称是变量名,即不进行任何修改。

2)全局函数:

以“_Z开头,然后是函数名字符的数量,然后是函数名,最后是函数参数的别名。

以后会详细介绍函数参数的别名。

3)类或命名空间中的变量或函数:

以“_ZN一开始,然后是变量或函数所在名称空间或类别名称的字符长度,然后是真实的名称空间或类别名称,然后是变量或函数名称的长度和变量或函数名称,然后是字母E最后,如果是函数,则与参数别名,如果是变量,则无需添加任何东西。

4)构造函数和分析函数

以”_ZN一开始,然后构建函数所在的名称空间和类名的字符长度,然后是真实的名称空间或类名,然后构建函数连接到C1”或者“C二、析构函数接D1”或者“D然后加上字母2E最后,接函数参数的别名结束。

在介绍了命名规则之后,我们将详细介绍函数参数别名的规则。主要分为以下情况:

1)函数参数的基本类型

每种基本类型的别名如下表所示:

参数类型 参数别名
void v
wchar_t w
bool b
char c
signed char a
unsigned char h
short s
unsigned short t
int i
unsigned int j
long l
unsigned long m
long long或__int64 x
unsigned long long或unsigned __int64 y
__int128 n
unsigned __int128 o
float f
double d
long double或__float80 e
__float128 g

2)函数参数为类或结构体

当函数参数中包含类或结构体时,在类或结构体名称前添加类或结构体名称的字符长度。

例如,全局函数int structure_func(int i, struct test s, double d),符号改编后,函数名变成_Z14structure_funci4testd

3)函数参数为指针(*)时

当函数参数中含有指针时,该参数的别名是P(大写)加上指针指向的参数类型的别名。当参数为指针时,参数的别名为PP加上指向参数类型的别名,以此类推。

4)函数参数是一维数组

当函数参数中含有一维数组时,它与指针相同,也是P加上数组作为参数的元素类型的别名。

5)函数参数是多维数组

对于多维数组,第一维可以看作是指针,维则视为数组。

当函数参数中含有多维数组时,P(代表数组的第一维)开始,后面连接A加上各维数组的长度,以_间隔,以下划线和数组元素类型的别名结束。

例如,全局函数void multi_array_func(int a[10][10][20][30],其经过符号改编后,函数名变成了_Z16multi_array_funcPA10_A20_A30_i

6)包含函数参数const修饰符时

包含在函数参数中const修饰符时,用K(大写)从修改参数类型的别名开始。

7)引用函数参数(&)时

当函数参数中含有引用时,该参数的别名是“R(大写)加上引用的变量类型的别名。

例如,全局函数void ref_const_func(const int &i),符号改编后,函数名变成_Z14ref_const_funcRKi

8)函数参数是其他命名空间中的类或结构

当函数的参数包含其他命名空间中的类或结构时,该参数的别名为N(大写),加上空间名的长度,加上空间名,然后是类或结构名的长度和类或结构名,最后是E(大写)结束。

假设代码如下:

view plain copy
  1. namespaceNS1
  2. {
  3. classTest1
  4. {
  5. };
  6. }&nsp; 
  7.   
  8. namespace NS2  
  9. {  
  10.     class Test2  
  11.     {  
  12.     public:  
  13.     void MyFunction(NS1:Test1 t) {}  
  14.     };  
  15. }  

那么MyFunction经过符号改变后变成了什么呢?答案是:_ZN3NS25Test210MyFunctionEN3NS15Test1E

最后,稍微总结一下。其实所谓GNU C++的符号改编机制非常简单,只要记住下面几点就可以了:

1)除了全局变量不用做改编之外,其它所需要改编符号的时候,都是以_Z开始;

2)若想表示某个符号是在命名空间或类中的,要以“N”开始,以“E”结束;

3)所有的名字空间名、类名、函数名或变量名,改编的时候都是名字所包含的字符数加上真正的名字;

4)所有的名字按照从外层到里层的顺序进行改编;

5)如果是函数的话,所有的参数按照前后出现的顺序进行改编。

最后再提一句,这里的符号改编机制都是暗地里编译器帮你做的。只要你的程序使用GNU C++编译器进行编译,它都会用上文所述的规则对你的各种符号名进行改编(包括变量和函数)。

如果你的程序有一些用C语言编写及编译,而另外一些用C++语言编写及编译,并且这两部分还会互相调用到,则需要进行特殊处理。

C++程序在编译的时候会用符号改编,而C程序在调用的时候并不会用符号改编,而是还用原始的函数名作为符号名进行调用,这样C程序就找不到那个对应的C++函数了。

或者,倒过来,C程序在编译的时候不会进行符号重编,而C++程序在调用的时候也会将这个函数名进行重编,这样C++程序同样也找不到那个对应的C函数了。

解决的方法是把那些需要让C程序用到的C++程序中的变量和函数,或者C++程序用到的C程序中的变量和函数,单独抽出来,让编译器不对它们进行符号重编。

具体方法是将它们用extern "C"包起来:

view plain copy
  1. extern "C"  
  2. {  
  3.     void func();      
  4.     ......  
  5. }  
当然,也可以一个一个指定:
view plain copy
  1. extern "C" void func();  
  2. ...... 

标签: 二极管丝印z16

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台