大多数的 C++开发人员也许会遇到这样的问题:你的开发工程是C++语言实现的,但你使用的库却是标准 C语言编译器编程的库,在编译链接时,总是不停地报出错误。
为了演示我们经常遇到的问题,来看如下这段代码:
/*C 语言头文件:Max.h*/
#ifndef MAX H
#define MAX_H
//Max 函数声明,此函数功能为求得两数据 nA和 nB的大者
#endif
/*C 语言实现文件:Max.c*/
#include "Max.h"
// 求取两数的最大值
int Max(int nA,int nB)
{
return((nA>nB)?(nA):(nB));
}
// C++语言调用文件:Main.cpp
#include "Max.h'
int _tmain(int argc,char* argv[])
{
nt nMax=Max(1,2);
return 0;
}
将上述代码在Visual C++编译器中编译,Visual C++会报出错误:
error LNK2019:无法解析的外部符号"int_cdecl Max(int,int)”(?Max@@YAHHH@Z),该符
号在函数wmain中被引用
为了说明上述代码存在的问题,现在把上述代码修改成如下形式:
/*C语言头文件:Max.h*/
#ifndef_MAX_H
_
#define_MAX_H_
#ifdef _cplusplus
exten"C"{
#endif
int Max(int nA,int nB);
#ifdef _cplusplus
};
#endif
#endif
/*C语言实现文件:Max.c*
#include"Max.h'
int Max(intnA,int nB)
{
retum ((nA>nB)?(nA):(nB));
}
//C++语言调用文件:Main.cpp
#include"Max.h"
int_tmain(int argc, char*argv[])
{
intnMax=Max(1,2);
return 0;
}
在Visual C++编译器中重新编译上述代码,发现此次竟然编译通过了。C++开发新手一般都会认为是代码写错了:Max函数哪里实现或Main函数中哪里的调用方式搞错了。不知你是否注意到,Max 函数实现文件类型是*.c 而不是*.cpp。此处编译出现的 LNK2019 错误就是今天要讨论的主题。
首先,对比上述两段代码发现,第二次的代码比第一次的代码多了如下代码段:
#ifdef _cplusplus
extern"C"{
#endif
#ifdef _cplusplus
};
#endif
所以可得出结论:在C++中调用C的代码必须把原来的C语言声明放到extern “C”{/*code*/}中,否则在 C++中无法编译通过。
当然,好奇心重的读者也许会产生新的问题了:为什么在C++中调用C的代码必须把原来的 C 语言声明放到 extern “C”{/*code*/}中,否则 C++无法编译通过呢?
带着这个问题我们看一下隐藏在现象背后深层次的真实原因:C和 C++语言具有不同的编译和链接方式。C 语言编译器编译函数时不带函数的类型和作用域信息,只包含函数符号名字;而C++编译器为了实现函数的重载,在编译时会带上函数的类型和作用域信息。
例如,假设某一函数原型为:
int Func(int nA, int nB);
C语言编译器把函数编译成类似Func的符号,C链接器只要找到这个符号就可以连接成功,实现调用。所以可以看出,C编译器不会对函数的参数和作用域信息进行校验,只是假设这些信息都是正确的。同样这也是C语言编译器的缺点所在。
而在强调安全的C++语言中,编译器会检查参数类型和作用域信息,上述函数原型会编译成ZFuncint int这样的符号(也正是这种机制为函数重载的实现提供了必要的支持)。在链接过程中,C++链接器会在函数原型所在模块生成的目标文件中查找Z_Func_int_int 这样的符号。
解决上述问题是设置extern”C”语法最直接的原因和动力。extern”C”的作用是告诉 C++编译器在查找调用链接符号时采用C 语言的方式,让编译器寻找_Func 而不是查找Z Func int int。
最后,介绍C++中调用C代码的3种具体实现方式:
(1)修改C代码的头文件,当其被用于C++代码时,在声明中加入extern”C”。代码如下:
/*C语言头文件:Max.h*/
#ifndef_MAX_H_
#define_MAX_H_
exten"C"int Max(int nA,int nB);
#endif
/*C 语言实现文件:Max.c*/
#include"Max.h'
int Max(int nA,int nB)
{
returm ((nA>nB)?(nA):(nB));
}
// C++语言调用文件:Main.cpp
#include"Max.h'
int _tmain(int argc, char* argy[])
{
intnMax=Max(1,2);
returm 0;
}
(2)在C++代码中重新声明一下C 函数,在重新声明时添加extern”C”。代码如下:
/*C语言头文件:Max.h*/
#ifndef_MAX_H_
#define_MAX_H_
exterm int Max(int nA,int nB);
#endif
/*C 语言实现文件:Max.c*/
#include"Max.h'
int Max(int nA, int nB)
{
return ((nA>nB)?(nA):(nB));
}
//C++语言调用文件:Main.cpp
#include "Max.h'
extern "C"intMax(int nA,int nB);
int_tmain(int argc, char* argv[])
{
intnMax=Max(1,2);
return 0;
}
(3)在包含C头文件时,添加 extern “C”。代码如下:
/*C语言头文件:Max.h*/
#ifndef_MAX_H_
#define_MAX_H_
extern int Max(int nA,int nB);
#endif
/*C语言实现文件:Max.c*/
#include "Max.h'
int Max(int nA,int nB)
{
return ((nA>nB)?(nA):(nB));
}
// C++语言调用文件:Main.cpp
extern "C"{
#include "Max.h'
}
int tmain(int argc, char* argv[])
{
intnMax=Max(1, 2);
return 0:
}
注意:exten”C”一定要加在C++的代码文件中才能起作用。
请谨记
●要想实现在 C++中使用大量的 C 程序库,再实现 C++与C语言混合编程,那么必须掌握extern “C”怎么使用,明白 extern”C”的使用方式。
●在 C++和 C 语言混合编程时,extem”C”一定要加在 C++文件中,否则 exten “C”不会发生作用。