首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C 語言調用 C++ 動態庫:二次封裝與 dlopen 實戰

     編輯:關於C語言

C 語言調用 C++ 動態庫:二次封裝與 dlopen 實戰

核心問題

C++ 編譯器對函數名進行 **Name Mangling**(如 _ZN1A2gtEv),導致 C 代碼無法直接鏈接調用 C++ 庫的類成員函數。解決方案:**C++ 內部封裝 C 接口(extern "C"),導出純 C 風格函數,C 端通過 dlopen/dlsym 動態加載調用。**

完整實戰流程

1. C++ 類定義(head.h)

class A {
public:
    A();
    virtual ~A();
    int gt();  // 設置內部值
    int pt();  // 打印內部值
private:
    int s;
};

2. C++ 實現(firstso.cpp)

#include 
#include "head.h"

A::A() {}
A::~A() {}
int A::gt() { s = 10; return 0; }
int A::pt() { std::cout << s << std::endl; return 0; }

3. 編譯 C++ 動態庫

g++ -shared -o libmy.so firstso.cpp
cp libmy.so /usr/lib/

4. 二次封裝:導出 C 接口(secso.cpp)

#include 
#include "head.h"

extern "C" {  // 關鍵:禁用 Name Mangling,導出純 C 符號
    int f();  // 導出函數聲明
    int f() { // 實現:內部實例化 C++ 類並調用
        A a;
        a.gt();
        a.pt();
        return 0;
    }
}

5. 編譯二次封裝庫

gcc -shared -o sec.so secso.cpp -L. -lmy
cp sec.so /usr/lib/
  • -L.:當前目錄查找庫。
  • -lmy:鏈接 libmy.so。
  • 輸出 sec.so 導出未修飾的 f 符號。

6. C 端動態加載調用(test.c)

#include 
#include 

#define SOFILE "sec.so"

int main() {
    void *handle = dlopen(SOFILE, RTLD_LAZY);
    if (!handle) {
        fprintf(stderr, "dlopen failed: %s\n", dlerror());
        return 1;
    }
    int (*f)() = dlsym(handle, "f");
    if (!f) {
        fprintf(stderr, "dlsym failed: %s\n", dlerror());
        dlclose(handle);
        return 1;
    }
    f();  // 調用封裝函數
    dlclose(handle);
    return 0;
}

7. 編譯 C 主程序

gcc -rdynamic -o myapp test.c -ldl
  • -rdynamic:將所有符號加入動態符號表(供 dlsym 查找)。
  • -ldl:鏈接 libdl(提供 dlopen/dlsym)。

8. 運行驗證

$ ./myapp
10

關鍵點總結

步驟關鍵動作目的
C++ 庫g++ -shared生成 .so,符號經過 Name Mangling
二次封裝extern "C" + 內部調用 C++ 類導出無修飾 C 符號,隱藏 C++ 實現細節
封裝庫編譯gcc -shared -lmy鏈接 C++ 庫,生成純 C 接口 .so
C 端加載dlopen + dlsym + 函數指針調用運行時動態綁定,無需編譯期鏈接 C++ 符號

進階:直接鏈接而非 dlopen

若不需運行時動態加載,C 主程序可直接鏈接 sec.so:

gcc -o myapp test.c ./sec.so -L. -lmy
# test.c 中直接声明 extern int f(); 並調用 f()

避坑指南

  • C++ 類析構函數必須 virtual:確保通過基類指針刪除時正確調用派生類析構(封裝層雖用棧對象,但良好習慣)。
  • 異常安全:C++ 代碼拋出異常不得穿透 extern "C" 邊界,封裝層需 try-catch 轉為錯誤碼返回。
  • 線程安全:若 C++ 庫有全局狀態,需在封裝層加鎖。
  • 符號可見性:GCC 默認導出所有符號,大型項目建議使用 -fvisibility=hidden 並显式標記 __attribute__((visibility("default")))。

總結

「C++ 實現 + extern "C" 封裝 C 接口 + C 端 dlopen 調用」是跨語言動態庫互操作的標準模式。掌握 Name Mangling 原理、extern "C" 作用、dlopen/dlsym 流程,即可將 C++ 庫無縫集成到 C 項目中。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved