项目实际开发需求需要在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
#include "add.h"
int main()
{
std::cout << add(1,2)<
编译: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
class Robot
{
public:
Robot(std::string name) : name_(name) {}
void sayHi();
private:
std::string name_;
};
------------------------
robot.cpp
#include
#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