Name mangling is the encoding of function and variable names into unique names so that linkers can separate common names in the language.

Name mangling实际上是对函数(或者变量)名称的一种编码,c++支持函数重载,而c不支持。所以c++的函数签名的编码方式肯定是和c不一样的,举个例子:

int foo(int a);  // foo
int foo(int a);  // foo(int)
int foo(float a);  // foo(float)

也可以理解为,函数原型经过编码之后得到的唯一id,暴露给linker用的。linker根据这个名字去查找、链接相应的函数地址。

同样一段c代码,用c compiler和c++ compiler编出来的函数&变量名称是不一样的。c++的通常会复杂一些,包含namespace,class,形参类型列表等。当使用c++ compiler且不想要上述mangling的时候,可以使用:

extern "C" {
	int  foo(int a);
	void bar(int b);
}

来告诉编译器这些名字不要进行name mangle. 这在c调用c++方法时尤其有用。

场景一:C调用C++

C++头文件中提供函数声明,C的源文件中需要包含这个头文件,并使用这个函数。

// file: cpp.h
 
#ifdef __cplusplus
extern "C" {
#endif
 
int foo(float);
 
#ifdef __cplusplus
}
#endif
// file: c.c
 
#include "cpp.h"
 
int main()
{
    int a = foo(3.14f);
    return 0;
}

使用gcc编译c.c,头文件中的__cplusplus宏处于未定义状态,int foo(float),foo的函数原型不会进行name mangling,直接就是foo. 因此cpp.h可以用于C项目中

使用g++编译其他cpp文件,包含cpp.h,此时__cplusplus宏处于定义状态,extern C的效果产生,即告诉g++不要给int foo(float)进行name mangling,此时编译出的符号,也可以直接用于C++项目,也可以编出lib给C项目使用。

场景二:C++调C

C中实现了某些函数,在某个头文件中声明了这些函数。例如

// file: c.h
 
#ifdef __cplusplus
extern "C" {
#endif
 
int foo();
char bar(char);
 
#ifdef __cplusplus
}
#endif

这里的__cplusplus宏,在原生C项目中(gcc编译)处于未定义状态,压根不起作用。但在C++项目(g++编译)中,如果包含了这个头文件,读到了__cplusplus宏,就不会对foo和bar进行name mangling,因此,C++链接器在寻找这个符号时,能够正确找到此前用gcc生成的未进行name mangling的符号。

c.h中的函数实现,必然在某个C源文件中。因此一般使用gcc编译产生lib. C++要用这个lib,要能找到这个符号,就必须按之前C语言的符号生成规则(不进行name mangling)来寻找(链接)。

You will need extern "C" both when calling:

  • C from C++: tell g++ to expect unmangled symbols produced by gcc,
  • C++ from C: tell g++ to generate unmangled symbols for gcc to use.

See also 链接

References

  1. Name mangling (C++ only)
  2. Stability of the C++ ABI: Evolution of a Programming Language
  3. What is the effect of extern “C” in C++?