赞
踩
在实际项目中,如果一个C++工程需要调用C语言编写的静态库或者动态库,那么可以直接调用吗?
显然是不可以直接调用的,对于C++代码,因为C++有自己的函数名修饰规则,在编译和汇编阶段形成符号表的时候,编译器是按照C++自己的函数名修饰规则来表示符号的,而对于C的代码,也有自己的函数名修饰规则,在编译和汇编阶段,编译器是按照C的函数名修饰规则来形成符号表的,那么在链接阶段,由于符号表中表示同一函数的符号名都是不同的,链接器无法找到相应函数的地址,也就无法进行符号表的合并和重定位,就会产生链接错误。
同理,一个C工程要调用C++语言编写的静态库或者动态库,也是不可以直接调用的。
那么一个C++工程如何调用C语言编写的代码呢?一个C工程又如何调用C++语言编写的代码呢?
这实际上是一个代码可移植问题,这里就需要用到条件编译和extern "C"了。
下面演示一个在C++工程中使用C语言静态库的例子:
/* * 注意: * 在实现该库时,并不知道将来使用该静态库的工程是C语言工程还是C++工程 * 为了能在C/C++工程中都能使用,此时就需要用到条件编译和extern "C" * * __cplusplus:是C++编译器中定义的宏,C++项目会自带该宏,所以我们可以用该宏来检测是C工程还是C++工程 * *C语言静态库定义如下 * #ifdef __cplusplus extern "C" { #endif // 要导出函数的声明 #ifdef __cplusplus } #endif 作用:如果是C++工程,编译器已经定义_cplusplus宏,编译时该宏是可以被识别的,被声明的函数就被extern "C"修饰了,此时C++编译器就知道,静态库中的函数是按照C的方式编译的,这样在链接时就会按照C的方式找函数名字。 如果是C工程,编译器未定义_cplusplus宏,编译时该宏无法被是被识别的,则条件编译就无效,函数就不会被extern "C"修饰,C工程调用C语言代码,当然不会有问题 */ /*示例: // 创建一个C语言的静态库 //calc.h/// #pragma once #ifdef __cplusplus extern "C" { #endif int Add(int left, int right); int Sub(int left, int right); #ifdef __cplusplus } #endif //calc.c/// #include "calc.h" int Add(int left, int right) { return left + right; } int Sub(int left, int right) { return left - right; } // // 创建一个c++工程,使用上面程序编程工程的静态库 #include <iostream> using namespace std; #include "./../Debug/calc.h" #pragma comment(lib, "./../Debug/CalcLib.lib") int main() { int ret = Add(10, 20); cout << ret << endl; ret = Sub(30, 20); cout << ret << endl; return 0; }
此时是C++工程,编译器自动定义_cplusplus宏,条件编译有效,对头文件进行文本替换后,所有函数声明都添加了extern “C”,此时C++编译器就知道静态库中的函数是按照C的方式编译的 ,那么在编译和链接阶段就会按照C的方式去找名字,就不会产生链接错误。
而如果是C工程调用该静态库,那么此时条件编译失效,对头文件进行文本替换后,所有函数声明前面没有extern “C”,C工程调用C语言编写的代码,没有任何问题。
如果没有条件编译和extern “C”,那么对于C++代码,编译器对C++代码按照C++的方式处理,在编译和汇编阶段形成符号表时就是按照C++的方式用符号表示函数,对于C代码,编译器按照C的方式处理,在编译和汇编阶段按照C的方式用符号表示函数,由于C和C++对函数名的修饰规则不同,因此,对于同一函数,C和C++表示函数的符号就不同,如果C++工程调用C的静态库,那么在链接阶段就无法通过符号名找到相应函数的地址,那么就会产生链接错误。
那如果静态库是用C++写的代码,同样,我们不知道调用该静态库的是C工程还是C++工程,因此,为保证代码的可移植性,也是一样需要对静态库中的.h文件使用条件编译和extern “C”。
// 创建一个C++语言的静态库 //calc.h/// #pragma once #ifndef __cplusplus extern "C" { #endif int Add(int left, int right); int Sub(int left, int right); #ifndef __cplusplus } #endif //calc.cpp/// #include "calc.h" int Add(int left, int right) { return left + right; } int Sub(int left, int right) { return left - right; } // // 创建一个c工程,使用上面程序编程工程的静态库 #include <iostream> using namespace std; #include "./../Debug/calc.h" #pragma comment(lib, "./../Debug/CalcLib.lib") int main() { int ret = Add(10, 20); printf("%d\n",ret); ret = Sub(30, 20); printf("%d\n",ret); return 0; }
如果是C工程调用该静态库,不会有_cplusplus宏,因此条件编译有效,此时所有函数声明前面就会加上extern “C”,那么对于这些函数就会按照C的方式去编译,链接阶段就不会产生问题。如果是C++工程调用该静态库,那么就是C++工程调用C++的代码,完全没有问题。
如果没有条件编译和extern “C”,那么就会产生链接错误,具体产生链接错误的原因,上面已经详细阐述过。
如果C++的静态库要被C工程调用,那么C++的静态库就不可以支持函数重载了,因为可能是按照C的方式去编译,编译的时候就无法通过。
当然,对于条件编译和宏,我们也可以这样用,以C静态库为例
// 创建一个C语言的静态库 //calc.h/// #pragma once #ifdef _cpluscplus #define EXTERN_C extern "C" #else #define EXTERN_C #endif EXTERN_C void add(int x,int y); EXTERN_C void sub(int x,int y); EXTERN_C void mul(int x,int y); //calc.c/// #include "calc.h" int Add(int left, int right) { return left + right; } int Sub(int left, int right) { return left - right; } // // 创建一个c++工程,使用上面程序编程工程的静态库 #include <iostream> using namespace std; #include "./../Debug/calc.h" #pragma comment(lib, "./../Debug/CalcLib.lib") int main() { int ret = Add(10, 20); cout << ret << endl; ret = Sub(30, 20); cout << ret << endl; return 0; }
此时,如果是C++工程调用这个C静态库,此时条件编译有效,会把函数声明前面的EXTERN_C全部替换为extern “C”,此时编译器就会按照C的方式来处理这些函数了。而如果是C工程调用这个C静态库,EXTERN_C就会消失,C工程调用C代码,不会产生任何问题。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。