动态库与静态库【Linux】
程序编译过程
源代码(.cpp) → 预处理(.i) → 编译(.s) → 汇编(.o) → 链接(可执行文件)
g++ -o main.i -E main.cpp 参数说明:
参数 | 功能 | 输出文件类型 |
---|---|---|
-E | 仅预处理 | .i |
-S | 预处理 + 编译 | .s |
-c | 预处理 + 编译 + 汇编 | .o |
无 | 完整流程(预处理→编译→汇编→链接) | 可执行文件 |
若省略 -o main.i
,预处理结果会默认输出到终端(可通过重定向保存
g++
对参数顺序无严格要求,-o
参数与源文件名的位置可互换,约定俗成将 -o
参数放在源文件名之前(如 g++ -o hello hello.cpp
),以提高可读性。
链接的目的:
将多个目标文件(.o)和库文件合成最终的可执行文件。链接分为两种:【静志链接】和【动态链接】
静态库和动态库
一、区别:
特性 | 静态库 | 动态库 |
---|---|---|
文件扩展名 | Linux: .a Windows: .lib | Linux: .so Windows: .dll |
链接时机 | 编译时直接嵌入可执行文件 | 运行时动态加载到内存 |
可执行文件 | 独立运行,不依赖外部库 | 需确保动态库存在于系统路径 |
内存占用 | 库代码复制到每个进程,内存冗余 | 多个进程共享同一内存副本,节省资源 |
更新维护 | 需重新编译整个程序 | 替换库文件即可生效(需接口兼容) |
二、工作原理
静态库
-
本质是目标文件(
.o
)的归档集合,通过ar
工具打包生成.a
文件。 - 编译时链接器将库代码复制到可执行文件中,导致文件体积较大
生成和使用静态库:
g++ -c add.cpp -o add.o # 编译为目标文件
ar rcs libadd.a add.o # 打包为静态库
g++ main.cpp -L. -ladd -o app # 链接静态库-L.的.表示当前目录, -l加文件名
./app #运行可执行文件app
动态库
- 编译时需添加
-fPIC
生成位置无关代码,确保加载地址灵活。 - 运行时通过动态链接器(如
ld-linux.so
)加载,依赖LD_LIBRARY_PATH
或系统库路径
生成和使用动态库:
gcc -fPIC -c add.cpp # 生成add.o文件,一定要加fPIC,表示任意路径都能用
g++ -shared -o libadd.so add.o #生成动态库
gcc main.c -L. -lcalc -o app # 链接动态库
export LD_LIBRARY_PATH=. # 设置临时库路径,.表示在当前目录
./app #运行app文件
编译时候直接使用:g++ mythread.cpp -lpthread -o app
三、适用场景
- 优先选静态库:
✅ 需独立分发的嵌入式程序(无外部依赖)
✅ 对运行时效率要求苛刻的场景(无动态加载开销) - 优先选动态库:
✅ 多进程共享的公共模块(如系统库)
✅ 频繁更新的功能模块(避免重新编译主程序)
四、常见问题
- 静态库链接冲突
若同时存在同名静态库(.a
)和动态库(.so
),默认优先链接动态库。强制静态链接需指定-static
或-Bstatic
参数 - 动态库加载失败
原因包括路径未配置(LD_LIBRARY_PATH
)、权限不足或 ABI 不兼容。可通过ldd app
检查依赖路径。 - 位置无关代码必要性
动态库编译时-fPIC
是关键,避免加载时修改代码段(违反现代操作系统的 W^X 安全策略)