C语言如何调用C++的so文件?

99ANYc3cd6
预计阅读时长 18 分钟
位置: 首页 C语言 正文
  1. 创建共享库:编写 C 源代码,并将其编译成 .so 文件。
  2. 编写调用程序:编写另一个 C 程序,这个程序会使用我们刚刚创建的 .so 文件中的函数。
  3. 编译并运行:将调用程序与共享库链接,并运行最终的可执行文件。

场景设定

假设我们有一个简单的数学库,它提供一个加法函数和一个减法函数。

c语言调用c 的so
(图片来源网络,侵删)

第一步:创建共享库

1 编写源代码

创建一个名为 math_utils.c 的文件,包含我们要导出的函数。

// math_utils.c
#include <stdio.h>
// 这是我们要在库中暴露的函数
int add(int a, int b) {
    printf("Info: Calling add function from the library.\n");
    return a + b;
}
int subtract(int a, int b) {
    printf("Info: Calling subtract function from the library.\n");
    return a - b;
}

2 编译成共享库 (.so)

使用 GCC 的 -fPIC (Position-Independent Code) 和 -shared 选项来编译。

  • -fPIC: 生成位置无关代码,这是创建共享库所必需的。
  • -shared: 指示 GCC 创建一个共享库,而不是一个可执行文件。

打开终端,执行以下命令:

gcc -fPIC -shared -o libmath_utils.so math_utils.c
  • -o libmath_utils.so: 指定输出的文件名为 libmath_utils.so
    • 命名约定:在 Linux 系统中,共享库通常以 lib 前缀开头,以 .so 后缀结尾,遵循这个约定可以让链接器更容易找到它。

执行后,你会看到当前目录下生成了一个 libmath_utils.so 文件。

c语言调用c 的so
(图片来源网络,侵删)

第二步:编写调用程序

我们创建一个主程序来使用这个库。

1 编写源代码

创建一个名为 main.c 的文件。

// main.c
#include <stdio.h>
// 声明我们要从库中使用的函数
// 注意:这里不需要函数的实现,只需要声明
extern int add(int a, int b);
extern int subtract(int a, int b);
int main() {
    int x = 10;
    int y = 5;
    int sum = add(x, y);
    printf("Result of %d + %d = %d\n", x, y, sum);
    int difference = subtract(x, y);
    printf("Result of %d - %d = %d\n", x, y, difference);
    return 0;
}

关键点

  • extern int add(...);:这个声明告诉编译器 add 函数存在于某个地方(在链接阶段会找到它),但现在我们先假设它存在,编译器会根据这个声明来检查你的调用是否正确(比如参数类型、数量是否匹配)。

第三步:编译并运行

这是最关键的一步,我们需要将 main.clibmath_utils.so 链接起来。

1 链接共享库并编译

使用 GCC 的 -L-l 选项。

  • -L.: 告诉链接器在当前目录 () 下查找共享库,如果库在其他路径,你需要写上完整路径,如 -L/path/to/your/libs
  • -lmath_utils: 告诉链接器链接名为 libmath_utils.so 的库。
    • 规则-l 后面跟的是库名,要去掉 lib 前缀和 .so 后缀

执行以下命令:

gcc -o my_app main.c -L. -lmath_utils
  • -o my_app: 指定最终输出的可执行文件名为 my_app

执行后,你会看到生成了一个 my_app 可执行文件。

2 运行程序

尝试运行 my_app

./my_app

如果遇到错误

你可能会看到类似下面的错误: ./my_app: error while loading shared libraries: libmath_utils.so: cannot open shared object file: No such file or directory

原因: 系统在运行时找不到 libmath_utils.so 文件,可执行文件在编译时知道去哪里找库(通过 -L),但运行时,它使用系统的默认路径(如 /lib, /usr/lib 等)来查找 .so 文件。

解决方案

有几种方法可以解决这个问题:

  1. 设置 LD_LIBRARY_PATH (推荐用于测试) 这个环境变量告诉动态链接器在哪些额外的目录中查找共享库。

    # 临时设置(仅在当前终端会话有效)
    export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
    # 现在再运行程序
    ./my_app

    你会看到正确的输出:

    Info: Calling add function from the library.
    Result of 10 + 5 = 15
    Info: Calling subtract function from the library.
    Result of 10 - 5 = 5
  2. 使用 ldconfig (推荐用于系统安装) 如果你打算把这个库作为系统库来使用,可以把它复制到标准库路径(如 /usr/lib),然后更新共享库缓存。

    # 复制到系统库目录 (需要 sudo 权限)
    sudo cp libmath_utils.so /usr/lib/
    # 更新共享库缓存
    sudo ldconfig
    # 现在可以直接运行,无需任何环境变量
    ./my_app
  3. 使用 rpath (编译时指定) 你可以在编译时就将运行时库的路径硬编码到可执行文件中,这样运行时就完全不需要依赖 LD_LIBRARY_PATH 了。

    # 使用 -Wl,-rpath,. 来指定运行时库的路径
    gcc -o my_app_rpath main.c -L. -lmath_utils -Wl,-rpath,.
    # 直接运行
    ./my_app_rpath

    这个方法对于分发自包含的应用程序非常有用。


  1. 创建库文件:

    echo '#include <stdio.h>
    int add(int a, int b) { printf("Library: add called.\n"); return a+b; }
    int subtract(int a, int b) { printf("Library: subtract called.\n"); return a-b; }' > math_utils.c
  2. 编译为 .so:

    gcc -fPIC -shared -o libmath_utils.so math_utils.c
  3. 创建主程序:

    echo '#include <stdio.h>
    extern int add(int, int);
    extern int subtract(int, int);
    int main() { printf("Main: 10 + 5 = %d\n", add(10, 5)); printf("Main: 10 - 5 = %d\n", subtract(10, 5)); return 0; }' > main.c
  4. 链接并编译:

    gcc -o my_app main.c -L. -lmath_utils
  5. 运行 (需要设置 LD_LIBRARY_PATH):

    export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
    ./my_app

预期输出:

Main: 10 + 5 = 15
Library: add called.
Main: 10 - 5 = 5
Library: subtract called.

(注意:printf 的顺序可能因为缓冲区问题而略有不同,但函数调用和结果肯定是对的。)


高级话题:头文件与符号控制

使用头文件

在实际项目中,通常会创建一个头文件(如 math_utils.h)来声明库中的函数,方便调用者包含。

math_utils.h:

#ifndef MATH_UTILS_H
#define MATH_UTILS_H
int add(int a, int b);
int subtract(int a, int b);
#endif // MATH_UTILS_H

修改后的 main.c:

#include <stdio.h>
// 包含头文件,而不是手动声明
#include "math_utils.h" // main.c 和 math_utils.h 在同一目录,用 ""
int main() {
    // ... 代码不变 ...
}

控制符号导出

默认情况下,.so 文件会导出所有非 static 的全局函数和变量,如果你只想导出特定的函数,可以使用 __attribute__((visibility("default")))

修改后的 math_utils.c:

#include <stdio.h>
// 明确标记此函数为可见(导出)
__attribute__((visibility("default")))
int add(int a, int b) {
    printf("Info: Calling add function from the library.\n");
    return a + b;
}
// 如果不声明,默认也是可见的,但显式声明是好习惯
__attribute__((visibility("default")))
int subtract(int a, int b) {
    printf("Info: Calling subtract function from the library.\n");
    return a - b;
}
// 这个函数不会被导出,外部无法调用
static int internal_helper() {
    return 0;
}

这样,你的库接口会更加清晰和安全。

-- 展开阅读全文 --
头像
dede 5.5下载哪里能找到安全版本?
« 上一篇 今天
织梦CKeditor如何配置使用?
下一篇 » 今天

相关文章

取消
微信二维码
支付宝二维码

目录[+]