一、Mangling介绍
Mangling可以理解为C++编译器为了解决函数名称冲突而进行的一种命名方式。简单地说就是在函数名前面加上一串字符,通过这种方式区分同名的函数,这个过程被称为“名称修饰”。 在编译器中,函数名可以由三个部分组成:函数名、参数列表和返回值类型。因此,名称修饰的方式也有所不同。对于函数名,编译器常常会加上函数的返回值类型,形成如下形式:
int foo();
// 编译器会将foo改成为:
int __Z3foov();
在这个例子中,名称修饰的规则是先将函数名改成为__Z
+ 函数名长度(十六进制)+ 函数名 + 函数参数类型。最终得到的函数名为__Z3foov
。"v"代表"void",即表示该函数没有入参。
二、Mangling过程
下面来介绍一下Mangling的过程。假设我们有一个函数:
bool isPrime(int num);
首先编译器会将函数名改为__Z
+ 函数名长度(十六进制)+ 函数名 + 函数参数类型,得到以下结果:
__Z7isPrimei
其中,“i”代表整型。接下来是参数类型的名称修饰,一般采用以下方式:
- 对于基本类型,都有一个对应的名称,例如:i表示整型,f表示单精度浮点型,d表示双精度浮点型。
- 对于数组类型,是以A开始,然后是数组元素类型和数组长度,例如:[5]i表示有5个整型元素的数组。
- 对于指针类型,是以P开始,然后是指向类型的名称,例如:Pi表示指向整型的指针。
有了这些规则,我们就可以进行参数类型的名称修饰了。对于上面的示例代码,参数类型只有一个整型,即i,因此Mangling后结果为
__Z7isPrimei
:
__Z7isPrimei
三、Mangling的作用
Mangling的主要作用就是消除C程序中的函数名冲突,这是由于在C中,函数的名称可以相同,但是参数列表不能相同。在编译过程中,编译器将加入了名称修饰后的函数名作为函数的唯一标识,避免了函数名冲突。 此外,Mangling还可以通过生成具有语义信息的名称来提供重载函数的一致性。例如,不同的函数有着相同名称但是参数不同。这些不同的函数可以在编译器输出的汇编代码中得到区分,便于程序员调试代码。
四、Mangling的使用
在实际应用中,我们可以使用g命令行选项“-fno-mangle”关闭Mangling功能,即避免编译器对函数名进行名称修饰。此时,我们就可以使用“extern C”机制来调用非C编写的函数。例如:
// test.c
#include <stdio.h>
void hello() {
printf("Hello from C!\n");
}
// main.cpp
#include <iostream>
extern "C" {
void hello();
}
int main() {
hello();
return 0;
}
在上面的例子中,我们在main.cpp中使用extern "C"来声明hello函数,这样就可以成功调用test.c中的hello函数了。
五、Mangling的缺陷
尽管Mangling有着广泛的应用,但它也有一些缺陷。其中最主要的问题是:不同编译器生成的Mangling名称可能不同。这意味着在链接不同编译器的模块时,可能会出现名称不匹配的错误。 此外,Mangling名称一般都是使用十六进制表示函数名长度的,这使得名称长度繁长,难以读懂,也不利于代码可读性的提高。
六、总结
本文详细介绍了Mangling的概念、过程、作用和使用方法,同时也指出了Mangling的缺陷。在实际编程中,我们需要了解Mangling的规则,以正确地处理函数名冲突问题。