静态库是将目标文件打包为.a归档文件,链接时拷贝所需代码到可执行文件,运行时不依赖外部库;动态库需用-fPIC和-shared编译为.so,运行时通过LD_LIBRARY_PATH或-rpath指定路径加载。

直接上手写一个能跑通的 GCC 动态库示例,关键不是“写完”,而是“链接不报错、运行不提示 libxxx.so: cannot open shared object file”——这问题 80% 出在 -fPIC 漏了、-shared 忘了,或者运行时根本找不到 so 文件。
怎么编译出合法的 .so 文件
动态库不是直接 gcc -o libxxx.so xxx.c 就行。必须加两个强制参数:-fPIC(生成位置无关代码)和 -shared(告诉 gcc 这是要做共享库,不是可执行文件)。
-
gcc -fPIC -c math_utils.c -o math_utils.o—— 先编译成 .o,必须带-fPIC -
gcc -shared math_utils.o -o libmath.so—— 再打包成 so,必须带-shared - 如果跳过 .o 直接一步:
gcc -fPIC -shared math_utils.c -o libmath.so也行,但多文件时推荐分步,更可控
链接时为什么总提示 “undefined reference to xxx”
链接阶段报这个错,说明头文件声明了函数,但链接器没找到对应实现——常见于:没把 .so 文件路径传给链接器,或函数名在 so 里根本没导出。
- 确保
main.c包含了正确的头文件:#include "math_utils.h",且该头文件里有double add(double, double);这类声明 - 链接命令必须显式指定库路径和库名:
gcc main.c -L. -lmath -o app——-L.告诉链接器去当前目录找库,-lmath对应的是libmath.so(自动补前缀lib和后缀.so) - 不要写成
gcc main.c libmath.so -o app:虽然有时能过,但不符合标准链接流程,且后续加其他库容易混乱
运行时报 “cannot open shared object file” 怎么办
编译成功 ≠ 能运行。Linux 运行时动态链接器(ld-linux.so)默认只查 /lib、/usr/lib、/usr/local/lib 这些系统路径,你放在当前目录的 libmath.so 它根本不会看。
- 临时解决(开发调试用):
LD_LIBRARY_PATH=. ./app—— 把当前目录加进运行时库搜索路径 - 永久解决(不推荐随意改系统路径):把 so 放到
/usr/local/lib,再运行sudo ldconfig刷新缓存 - 最稳妥(项目级):编译时就嵌入运行时路径:
gcc main.c -L. -lmath -Wl,-rpath=. -o app——-Wl,-rpath=.表示把.写进可执行文件的DT_RUNPATH,运行时自动从这里找 so
为什么同一个目录下有 libmath.a 和 libmath.so,gcc 默认用动态库
这是链接器的默认策略:只要 -lmath 同时匹配到 libmath.a(静态)和 libmath.so(动态),它优先选 .so。这不是 bug,是设计使然。
- 想强制用静态库?加
-static:gcc main.c -L. -lmath -static -o app—— 但注意:这会把所有依赖(包括 libc)都静态链接,生成的文件可能大几 MB,且在某些发行版上会失败 - 只想对某一个库静态链接?用完整路径:
gcc main.c ./libmath.a -o app,绕过-l机制 - 真正要关注的不是“哪个被选”,而是“是否真的需要动态加载”——比如你要部署到嵌入式设备,而目标系统没装 glibc 或版本不兼容,那动态链接反而会崩
最容易被忽略的一点:动态库的符号必须实际存在且可导出。用 nm -D libmath.so 看一眼,输出里得有 T add 这样的行(T 表示全局文本符号),如果全是 U 或空,说明函数根本没进 so,大概率是源文件没参与编译,或者函数被 static 修饰了。


















