> 文章列表 > C和C++程序互相调用方案

C和C++程序互相调用方案

C和C++程序互相调用方案

C++程序调用C函数

项目实际开发需求需要在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"{
#endifint 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程序函数

C程序调用C++函数

场景:基本我们linux底层开发都是基于C语言开发,而上层应用基于C++开发,现在我们有C程序需要调用C++中的功能函数,该如何实现呢?

假设我们有一个C++类 Robot,在文件 robot.hrobot.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.sonm 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" {
#endifvoid 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程序的编译方式,用 gccmain.c 进行编译

gcc main.c -L. -lmiddle

通过objdump -t main命令可以看到可以看到 gcc 编译出的函数符号和 libmiddle.sog++ 编译器编译出的函数符号一致。这样最终在我们的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