- 创建共享库:编写 C 源代码,并将其编译成
.so文件。 - 编写调用程序:编写另一个 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后缀结尾,遵循这个约定可以让链接器更容易找到它。
- 命名约定:在 Linux 系统中,共享库通常以
执行后,你会看到当前目录下生成了一个 libmath_utils.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.c 和 libmath_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 文件。
解决方案:
有几种方法可以解决这个问题:
-
设置
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 -
使用
ldconfig(推荐用于系统安装) 如果你打算把这个库作为系统库来使用,可以把它复制到标准库路径(如/usr/lib),然后更新共享库缓存。# 复制到系统库目录 (需要 sudo 权限) sudo cp libmath_utils.so /usr/lib/ # 更新共享库缓存 sudo ldconfig # 现在可以直接运行,无需任何环境变量 ./my_app
-
使用
rpath(编译时指定) 你可以在编译时就将运行时库的路径硬编码到可执行文件中,这样运行时就完全不需要依赖LD_LIBRARY_PATH了。# 使用 -Wl,-rpath,. 来指定运行时库的路径 gcc -o my_app_rpath main.c -L. -lmath_utils -Wl,-rpath,. # 直接运行 ./my_app_rpath
这个方法对于分发自包含的应用程序非常有用。
-
创建库文件:
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 -
编译为 .so:
gcc -fPIC -shared -o libmath_utils.so math_utils.c
-
创建主程序:
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 -
链接并编译:
gcc -o my_app main.c -L. -lmath_utils
-
运行 (需要设置
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;
}
这样,你的库接口会更加清晰和安全。
