C 语言静态库与动态库的工程选择
更新时间:2026-08-26。本文是
languages/c/主题专家层文档。写一个库给别人用,你要决定:打包成静态库(.a)还是动态库(.so)?这决定了部署方式、体积、更新成本。本文用实测对比两种方式,帮你做工程选型。
本文要回答的问题
- 静态库和动态库分别是"什么时候链接"的?
- 为什么动态链接的程序运行时报
libxxx.so: not found?怎么解决? - 工程上怎么选静态库还是动态库?
一、静态库 vs 动态库
| 对比 | 静态库 .a | 动态库 .so |
|---|---|---|
| 链接时机 | 编译/链接期(拷进可执行文件) | 运行期(启动时/运行时加载) |
| 可执行文件体积 | 大(库代码打包进去) | 小(只记录依赖) |
| 更新方式 | 重新链接整个程序 | 替换 .so 即可(ABI 不变) |
| 部署 | 单个可执行文件即可 | 要带上 .so 文件 |
| 内存共享 | 每个进程各一份 | 多个进程共享同一份 |
二、实测:创建与链接
2.1 静态库
bash
gcc -c libtest.c -o libtest.o # 1. 编译成目标文件
ar rcs libtest.a libtest.o # 2. ar 打包成静态库
gcc -o app libmain.c libtest.a # 3. 链接期直接链接实测:
text
libtest.a: 1.5K
app-static: 8.3K ← 库代码已打包进可执行文件2.2 动态库
bash
gcc -shared -fPIC -o libtest.so libtest.c # 1. 编译成共享库(-fPIC 位置无关)
gcc -o app libmain.c -L. -ltest # 2. 链接期只记录依赖实测:
text
libtest.so: 7.8K
app-dynamic 运行时依赖 libtest.so三、动态库的运行时查找路径(核心坑)
动态链接的程序,运行时要能找到 .so。查找顺序:
text
1. LD_LIBRARY_PATH 环境变量
2. rpath(链接时 -Wl,-rpath 写死)
3. ld.so.cache(ldconfig 缓存的系统路径)
4. 默认路径(/lib, /usr/lib)实测 ldd 报错:
text
libtest.so => not found ← 找不到!因为 -L. 只在链接期告诉链接器去哪找,运行期不生效。解决:
bash
# 方式 1:环境变量
LD_LIBRARY_PATH=. ./app
# 方式 2:链接时写死 rpath
gcc -o app libmain.c -L. -ltest -Wl,-rpath,.
# 方式 3:装进系统目录
cp libtest.so /usr/local/lib/ && ldconfig生产环境的库通常装系统目录 +
ldconfig,或用 rpath 指定相对路径。
四、工程选型决策
| 场景 | 选静态库 | 选动态库 |
|---|---|---|
| 单个可执行文件部署 | ✅ 无依赖 | ❌ 要带 .so |
| 多个程序共用同一个库 | ❌ 各存一份 | ✅ 共享节省内存 |
| 需要频繁更新库 | ❌ 每次重新链接 | ✅ 替换 .so 即可 |
| 要控制部署复杂度 | ✅ 一个文件搞定 | ❌ 依赖管理复杂 |
| 库要闭源分发 | ❌ 代码在 .a 里 | ✅ 只发 .so + 头文件 |
经验法则:
- 内部工具、单机部署 → 静态库(省心)
- 系统库、多个程序共享、需热更新 → 动态库
五、与本站主线衔接
- 编译链接:链接期的符号解析,见 符号表与重定位。
- 运行时加载:dlopen 与动态库的运行时机制,见 dlopen 与插件机制。
- GOT/PLT:动态库的延迟绑定,见 符号表与重定位。
- ELF 格式:静态库(ar 归档)与动态库(ELF DYN)的文件格式,见 L5 ELF。
一句话总结
静态库链接期拷进程序(体积大、无依赖),动态库运行期加载(体积小、可共享可热更新);动态库的"not found"是查找路径问题,用 LD_LIBRARY_PATH/rpath/ldconfig 解决——选静态图省心、选动态图共享和更新。