C语言动态调用C++动态链接库的封装方法

沈知远
沈知远
发布时间:2026-10-13 | 4人浏览过
原创

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

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表