C语言动态调用C++动态链接库的封装方法
C语言无法直接实例化C++类或调用带有名称修饰的成员函数。通过二次封装,利用 extern "C" 导出C风格的全局函数,并使用动态加载技术 dlopen,可以在C语言中安全地调用C++实现的动态链接库。
核心难点与解决方案
C++编译器会对类成员函数进行名称修饰(Name Mangling)以支持重载,而C语言没有这一机制。此外,C++类对象包含构造函数、析构函数及虚函数表指针,直接通过C链接器导入会导致符号未定义或内存模型不一致的问题。解决此问题的标准做法是构建一个“桥接层”:将C++类逻辑隐藏在第二个动态库中,对外仅暴露遵循 extern "C" 约定的全局函数。
第一步:准备原始C++代码并编译基础库
首先创建被调用的C++类。创建一个头文件 head.h 和实现文件 firstso.cpp。该类包含基本的数据成员和成员函数。编写完毕后,使用 g++ 将其编译为共享对象文件。
head.h:
class A
{
public:
A();
virtual ~A();
int gt();
int pt();
private:
int s;
};
firstso.cpp:
#include <iostream>
#include "head.h"
A::A(){}
A::~A(){}
int A::gt()
{
s=10;
}
int A::pt()
{
std::cout<<s<<std::endl;
}
执行以下命令生成第一个动态库 libmy.so:
g++ -shared -o libmy.so firstso.cpp
生成的库文件可以直接拷贝至系统路径(如 /usr/lib/)或直接放在当前工作目录供后续步骤引用。
第二步:建立C兼容的二次封装库
为了让C语言能够访问上述C++类,需要创建一个新的封装文件 secso.cpp。关键在于使用 extern "C" 块包裹C接口函数,这会告诉编译器不要对内部函数名进行修饰处理,从而生成标准的C语言符号。
secso.cpp:
#include <iostream>
#include "head.h"
extern "C"
{
// 声明导出的C风格接口
int f();
// 实现C风格接口
int f()
{
// 在这里完成C++对象的创建和使用
A a;
a.gt();
a.pt();
return 0;
}
}
使用该库依赖 libmy.so 的信息,编译生成最终的封装库:
gcc -shared -o sec.so secso.cpp -L. -lmy
生成的 sec.so 现在对外表现为一个标准的C动态库,其唯一的入口点是全局函数 f。
第三步:在C语言程序中动态加载并调用
C语言程序不直接链接该库,而是通过运行时动态加载工具(RTLD)加载。这需要引入 <dlfcn.h> 头文件来处理句柄和操作符。
test.c:
#include "stdio.h"
#include "dlfcn.h"
#define SOFILE "sec.so"
int (*f)();
int main()
{
void *dp;
// 动态加载SO文件
dp=dlopen(SOFILE,RTLD_LAZY);
if(!dp) {
fprintf(stderr, "%s\n", dlerror());
return 1;
}
// 获取函数地址
f=dlsym(dp,"f");
// 调用函数
f();
// 释放库
dlclose(dp);
return 0;
}
编译C语言测试程序:
gcc -rdynamic -s -o myapp test.c
执行生成的可执行程序,预期输出如下:
10
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













