25.2.3 和C代码链接(1)
在前面的例子中,我们假设您有可用的原始C语言代码。这个例子利用了一个事实,即大部分C语言代码都可以通过C++(www.cppentry.com)编译器成功编译。如果您只有编译后的C语言代码,可能是库的形式,那么您仍然可以在C++(www.cppentry.com)程序中使用这些代码,只不过需要一些额外的步骤。
为了实现函数重载,复杂的C++(www.cppentry.com)名称空间被"扁平化"了。例如,如果有一个C++(www.cppentry.com)程序,可以合法地编写:
- void MyFunc(double);
- void MyFunc(int);
- void MyFunc(int, int);
然而这意味着链接器会看到几个不同的名称,所有名称都称为MyFunc,不知道您想调用哪一个。因此,所有C++(www.cppentry.com)编译器都执行了一项名为名称变异(name mangling)的操作,逻辑上等同于生成了以下名称:- MyFunc_double
- MyFunc_int
- MyFunc_int_int
为了避免和其他您可能定义的名称冲突,生成的名称通常都会包含一些对链接器合法但是对C++(www.cppentry.com)源代码不合法的字符。例如,Microsoft VC++(www.cppentry.com)生成的名称如下: - MyFunc@@YAXN@Z
- MyFunc@@YAXH@Z
- MyFunc@@YAXHH@Z
这种编码很复杂,而且通常是与特定厂商相关的。C++(www.cppentry.com)标准没有规定函数重载应该怎样在指定平台上实现,因此没有标准的名称变异算法。
在C语言中,函数重载是不受支持的(编译器会对重复定义报错)。因此,C编译器生成的名称是非常简单的,例如_MyFunc:
现在,如果您通过C++(www.cppentry.com)编译器编译一个简单的程序,即使这个程序只有一个MyFunc名称的实例,仍然会生成链接变异名称的请求。但是,当您和C语言库链接的时候,链接器找不到所需要的变异的名称,因此链接器会报错。因此,需要告诉C++(www.cppentry.com)编译器不要将名称变异。这是通过在两个头文件中通过extern "language"限定(为了指导客户代码创建一个和指定语言兼容的名称)的方式实现的,而且如果您的库源代码是用C++(www.cppentry.com)写的,还需要在定义的地方加上(为了指导库代码生成一个和指定语言兼容的名称)。
extern "language" 的语法如下所示:
- extern "language" declaration1();
- extern "language" declaration2();
或:- extern "language" {
- declaration1();
- declaration2();
- }
C++(www.cppentry.com)标准指出可以使用任何语言规范,所以编译器原则上能支持以下代码:- extern "C" MyFunc(int i);
- extern "FORTRAN" MatrixInvert(Matrix* M);
- extern "Pascal" SomeLegacySubroutine(int n);
- extern "Ada" AimMissileDefense(double angle);
在实际中,许多编译器只支持"C"。每一个编译器厂商都会告诉您支持哪一些语言标记。