赞
踩
项目实际开发需求需要在C++程序中调用c语言实现的库,那么需要怎么实现呢?
原理:C++函数为了支持函数重载,g++编译器会对函数名进行修饰,而C函数编译的符号表中只是函数名。同样的代码用C编译器(gcc)和C++编译器(g++)编译产生的函数名不同,导致C++连接找不到C语言的函数,所以C++程序中直接C函数是不能直接实现的,需要通过某种处理
下面通过一个例子简单说明C++和C编译原理的不同之处
add.c
#include "add.h"
int add(int m, int n)
{
return m + n;
}
add.h
#ifndef ADD_H__
#define ADD_H__
int add(int m, int);
#endif
上面源码用gcc编译
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m1qdyXwu-1678875989098)(C:/Users/100011704/Desktop/md%E6%96%87%E6%A1%A3/images/12.png)]
同样源码使用g++编译
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yGss3thl-1678875989098)(C:/Users/100011704/Desktop/md%E6%96%87%E6%A1%A3/images/23.png)]
解决:
extern "C"的主要作用就是为了实现c++代码能够调用其他c语言代码。加上extern "C"后,这部分代码编译器按c语言的方式进行编译和链接,而不是按c++的方式。
我们只要在c的.h文件下进行如下操作,再在c++文件中调用c的头文件,就可以链接成功。
c文件的.h文件代码如下
add.c #include "add.h" int add(int m, int n) { return m + n; } add.h #ifndef ADD_H__ #define ADD_H__ #ifdef __cpluscplus extern "C"{ #endif int add(int m, int); #ifdef __cpluscplus }; #endif #endif
(extern"C"是C++特有的,所以上面加上宏__cpluscplus判断当使用C++编译时才加上extern "C"关键字)
下面写个简单的c++程序去调用c函数
main.cpp
#include <iostream>
#include "add.h"
int main()
{
std::cout << add(1,2)<<std::endl;
}
编译:g++ main.cpp add.c -o main
运行
到这里已经完成C++去调用C程序函数
场景:基本我们linux底层开发都是基于C语言开发,而上层应用基于C++开发,现在我们有C程序需要调用C++中的功能函数,该如何实现呢?
假设我们有一个C++类 Robot,在文件 robot.h 和 robot.cpp 中定义。Robot 类中有个成员函数 sayHi() 我们想在C程序中调用这个函数。
C++部分代码如下:
robot.h #pragma once #include <string> class Robot { public: Robot(std::string name) : name_(name) {} void sayHi(); private: std::string name_; }; ------------------------ robot.cpp #include <iostream> #include "robot.h" void Robot::sayHi() { std::cout << "Hi, I am " << name_ << "!\n"; }
我们编译生成g++的动态库
g++ -fpic -shared robot.cpp -o librobot.so
nm librobto.so #nm命令可以查看动态库的符号表
此时完成c++动态库的生成,如果我们现在有C程序想要根据C++动态库和头文件直接使用是不行的(因为动态库是基于C++编译的不能修改编译方式的,C编译器并不能识别)
解决方案:
直接调用不可行,可以定义一个中间类完成转换
如下定义一个中间类Middle的接口,定义在Middele.h和Middle.cpp,该接口会定义一个C函数 Robot_sayHi(const char *name), 这个函数会创建一个类 Robot 的实例,并调用 Robot 的成员函数 sayHi()。
Middele.h #pragma once #ifdef __cplusplus extern "C" { #endif void Robot_sayHi(const char *name); #ifdef __cplusplus } #endif ----------------------------------------- Middle.cpp #include "Middle.h" #include "robot.h" #ifdef __cplusplus extern "C" { #endif // 因为我们将使用C++的编译方式,用g++编译器来编译 robot_c_api.cpp 这个文件, // 所以在这个文件中我们可以用C++代码去定义函数 void Robot_sayHi(const char *name)(在函数中使用C++的类 Robot), // 最后我们用 extern "C" 来告诉g++编译器,不要对 Robot_sayHi(const char *name) 函数进行name mangling // 这样最终生成的动态链接库中,函数 Robot_sayHi(const char *name) 将生成 C 编译器的符号表示。 void Robot_sayHi(const char *name) { Robot robot(name); robot.sayHi(); } #ifdef __cplusplus } #endif
同样用编译C++代码的方式进行编译
g++ -fpic -shared Middle.cpp -L. -lrobot -o libmiddle.so
通过nm libmiddle.so 命令可知动态链接库提供了一个C函数 Robot_sayHi(const char *name),我们可以在C程序中调用它了
下面就可以写一个测试的c文件完成调用了
main.c
-
使用C程序的编译方式,用 gcc 对 main.c 进行编译
gcc main.c -L. -lmiddle
通过objdump -t main命令可以看到可以看到 gcc 编译出的函数符号和 libmiddle.so中 g++ 编译器编译出的函数符号一致。这样最终在我们的C程序中可以正确的链接到动态库中的Robot_sayHi(const char *name) 函数。
运行可能出现如下报错:
./main: error while loading shared libraries: libmiddle.so: cannot open shared object file: No such
运行可能找不到动态库的路径导致报错,可以配置/etc/ld.so.conf 或者~/bashrc或者/etc/profile配置文件既可以
可参考如下连接:
https://blog.csdn.net/weixin_42310458/article/details/125180410
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。