好嘞,直接上干货指令 ✅
✅ 情况 1:你修改了 .cpp
或 .h
,使用 静态库(.a
)
你需要运行的命令:
g++ -c math.cpp -o math.o # 重新生成目标文件
ar rcs libmath.a math.o # 重新打包静态库
g++ main.cpp -I. -L. -lmath -o app # 重新编译并链接主程序
./app # 重新运行程序
✅ 情况 2:你修改了 .cpp
或 .h
,使用 动态库(.so
)
如果只改了实现(.cpp
),接口没变:
g++ -fPIC -shared math.cpp -o libmath.so # 重新生成动态库
./app # 直接重新运行主程序即可
✅ 不需要重新编译主程序(前提是接口没变)
如果接口变了(改了 .h
中的函数声明):
g++ -fPIC -shared math.cpp -o libmath.so # 重新生成动态库
g++ main.cpp -I. -L. -lmath -o app # 重新编译主程序
./app
你就按这个表走,准没错:
改了啥 | 用静态库要重新编译主程序 | 用动态库要重新编译主程序 |
---|---|---|
改 .cpp | ✅ 是 | ❌ 否(接口不变) |
改 .h | ✅ 是 | ✅ 是(接口变了) |
好的,这次我给你详细讲清楚如何手动创建动态库 .so
和静态库 .a
,它们之间的本质区别、各自的优点与使用场景,让你彻底搞懂,不止能用、还能讲清楚!
🧱 一、如何创建静态库 .a
静态库就是把 .o
目标文件打包成 .a
文件,然后在编译时一次性“嵌入”进可执行文件。
✅ 示例代码结构:
.
├── main.cpp
├── math.cpp
├── math.h
✅ 文件内容:
math.h
:
#ifndef MATH_H
#define MATH_H
int add(int a, int b);
#endif
math.cpp
:
#include "math.h"
int add(int a, int b) {return a + b;
}
main.cpp
:
#include <iostream>
#include "math.h"int main() {std::cout << "3 + 4 = " << add(3, 4) << std::endl;return 0;
}
✅ 创建静态库的指令:
g++ -c math.cpp -o math.o # 1. 编译为目标文件
ar rcs libmath.a math.o # 2. 打包为静态库(.a 文件)
g++ main.cpp -I. -L. -lmath -o app # 3. 编译主程序并链接静态库
./app # 4. 运行
🧊 二、如何创建动态库 .so
动态库在程序运行时加载,并不是打包进可执行文件中。多个程序可以共享一个 .so
文件。
✅ 修改 math.cpp
加上 fPIC
编译选项:
g++ -fPIC -c math.cpp -o math.o # 1. 编译为位置无关代码
g++ -shared -o libmath.so math.o # 2. 打包为动态库(.so)
g++ main.cpp -I. -L. -lmath -o app # 3. 编译主程序并链接动态库
export LD_LIBRARY_PATH=. # 4. 让系统能找到 .so 文件
./app # 5. 运行
⚔️ 三、静态库 vs 动态库 的区别和优缺点
项目 | 静态库 .a | 动态库 .so |
---|---|---|
链接方式 | 编译时链接,直接打包进可执行文件 | 运行时动态加载 |
文件大小 | 可执行文件体积大 | 可执行文件体积小,依赖 .so 文件 |
共享性 | 每个程序各自包含一份 | 多个程序共享一份 .so |
更新方式 | 更新 .a 后必须重新编译所有用到它的程序 | 只更新 .so 文件,程序可直接使用新版本 |
性能 | 加载速度略快(不需要运行时查找符号) | 初始加载稍慢(动态查找函数符号) |
部署方便 | 一般更方便,因为不需要额外文件 | 要确保 .so 文件存在并在 LD_LIBRARY_PATH 中 |
安全性 | 更稳定、不易被篡改 | 有安全风险(比如恶意篡改 .so ) |
🧠 总结一句话:
✅ 静态库适合体积小、依赖少、无需频繁更新的程序;
✅ 动态库适合大型项目、多个程序共享、经常更新库版本的情况。
☑️ 面试加分点:
-
你能解释 .a vs .so 的运行原理
-
你知道
.a
被嵌入.exe
,.so
是延迟加载(lazy loading) -
你知道如何用
nm
或ldd
检查库的符号和依赖