目录
将我们所实现的方法给别人用有以下两种方式:
- **提供源码文件(.h 头文件 + .c 源文件)。**使用者需要重新编译,但能看到你的实现代码,优点是可调试、可修改、跨平台适应性好(源码可以在用户环境中针对特定平台重新编译),缺点是暴露实现细节,编译时间长
- **提供库文件(.h 头文件 + .a/.so/.lib/.dll 库文件)。**使用者直接链接,看不到源代码,优点是保护知识产权,编译快,版本控制方便,缺点是调试困难(未知实现),平台依赖性强(架构等绑定问题:x64库不能在ARM上运行)
头文件的本质是库的说明书,它告诉编译器有什么,没有头文件,编译器就无法生成正确的函数调用代码
1.静态库
1.1静态库的概念特点与静态链接
静态库是一个包含多个已编译目标文件的归档文件,通常用于代码复用
在不同系统中有不同的格式和扩展名:
Linux/Unix: .a 文件(Archive,由 ar 命令创建)
Windows: .lib 文件(Library)
macOS: .a 或 .framework(框架包含静态库)
静态库的特点
- 编译时链接:库代码在程序编译时被直接复制到最终的可执行文件中
- 独立运行:生成的可执行文件不依赖外部库文件
- 体积较大:相同库代码在每个使用它的程序中都有副本
- 部署简单:只需分发可执行文件,不需要在系统上安装额外的库文件
- 更新困难:库更新需要重新编译链接所有使用它的程序
- 加载快,但占用更多磁盘/内存
静态库命名约定(去掉前后缀)
格式:lib<名称>.a
示例:libmath.a、libutils.a
链接时使用:-lmath 会自动查找 libmath.a
静态链接链接的是静态库 ,静态链接发生在编译的最后阶段,主要步骤 :
源代码 → 编译 → 目标文件(.o) + 静态库(.a) → 链接器 → 可执行文件
1.2静态库的创建与使用
创建静态库
# 1. 编译源文件为目标文件
gcc -c file1.c file2.c -o file1.o file2.o
# 2. 打包成静态库
ar rcs libmylib.a file1.o file2.o
编译不链接,生成目标文件 :gcc -c 只编译源文件为独立的可重定位目标文件(.o),不进行链接操作
打包加索引,创建静态库:ar rcs 将多个目标文件打包成归档文件(.a)并建立符号索引,供链接器快速查找。
ar
ar (archive)是 Unix/Linux 系统上的归档管理工具,主要用于创建、修改和提取静态库文件(.a 文件)
ar 有以下核心功能
创建静态库 :将多个目标文件(.o)打包成单个归档文件
维护库内容 :可添加、删除、替换库中的目标文件
提取文件:从库中提取单个或多个目标文件
ar [选项] <库文件名> <目标文件列表>
常用参数说明
| 参数 | 全称 | 作用 |
|---|---|---|
r |
replace | 插入文件到归档(替换已存在的) |
c |
create | 创建新归档(如不存在) |
s |
write index | 创建/更新符号表索引 |
t |
table/list | 列出归档内容 |
x |
extract | 从归档提取文件 |
d |
delete | 从归档删除文件 |
v |
verbose | 显示详细操作信息 |
使用静态库(形成可执行文件)
标准链接法(需要路径查找)
gcc main.c -L. -lmylib 告诉链接器:在当前目录(-L.)查找名为libmylib.a的库并链接。特点:
- -L. 指定库搜索路径(.表示当前目录)
- -lmylib 会自动查找 libmylib.a(自动添加lib前缀和.a后缀)
- 如果不在-L指定目录或系统默认库目录,链接器会报错找不到库
直接链接法(无需路径查找)
gcc main.c libmylib.a 直接将静态库文件当作输入文件,编译器知道如何从其中提取需要的符号。特点:
- 直接把库文件当作普通输入文件处理
- 不依赖任何搜索路径,直接定位库文件
- 即使库在当前目录,也不需要 -L.
- 库不在当前目录,指明路径即可
gcc默认在系统目录和当前目录下寻找.h头文件,
但只在系统目录下寻找库文件(除非用-L指定当前目录)
gcc main.c -I ./lib/include(指定头文件路径)-L ./lib/mymathlib(库的路径)-l(指定哪一个库,库名称)
此时的可执行文件能直接运行,这是因为静态库函数的实现已拷贝至可执行文件中
makefile文件
#库名称
dy-lib=libmymethod.so
static-lib=libmymath.a
#一次性形成一个动态库与一个静态库
.PHONY:all
all:$(dy-lib) $(static-lib)
#制作动态库
$(dy-lib):mylog.o myprint.o
gcc -shared -o $@ $^
mylog.o:mylog.c
gcc -fPIC -c $^
myprint.o:myprint.c
gcc -fPIC -c $^
#制作静态库
$(static-lib):mymath.o
ar -rc $@ $^ #将.o文件打包成库文件的命令,replace and create
mymath.o:mymath.c
gcc -c $^ #自动形成同名的.o文件
#清理文件
.PHONY:clean
clean:
rm -rf *.o *.a *.so mylib
#创建新目录存放自定义库
.PHONY:output
output:
mkdir -p mylib/include
mkdir -p mylib/lib
cp *.h mylib/include
cp *.a mylib/lib
cp *.so mylib/lib
使用静态库(配置静态库路径)
(1)直接复制到系统目录(将静态库和头文件直接安装到系统标准目录,让编译器和链接器在默认搜索路径中找到它们)
# 1. 安装头文件到系统include目录
sudo cp /path/to/mymath.h /usr/include/
# 或更好位置
sudo cp /path/to/mymath.h /usr/local/include/
# 2. 安装静态库到系统库目录
sudo cp /path/to/libmymath.a /usr/lib64/
# 或
sudo cp /path/to/libmymath.a /usr/local/lib/
# 3. 直接使用库名链接
gcc main.c -lmymath -o program
(2)创建软连接到系统目录(在系统目录中创建软链接(快捷方式)指向实际的库文件和头文件,避免复制文件,便于更新)
# 1. 创建头文件目录的软链接
sudo ln -s /actual/path/to/include /usr/include/project_inc
# 2. 创建静态库文件的软链接
sudo ln -s /actual/path/to/libmymath.a /usr/lib64/libmymath.a
# 3. 使用库名链接
gcc main.c -I/usr/include/project_inc -lmymath -o program
# 或如果系统已配置搜索/usr/include
gcc main.c -lmymath -o program
2.动态库
2.1动态库的概念特点与动态链接
动态库 是一种在程序运行时才被加载的代码库,也称为:共享库、动态链接库
文件格式:
Linux/Unix: .so (Shared Object)
Windows: .dll (Dynamic-Link Library) + .lib (导入库)
macOS: .dylib (Dynamic Library)
动态库的特点
动态库通过运行时链接和代码共享机制实现了显著的内存节省和灵活的模块化开发,其核心优势在于允许多个程序共享同一份库代码副本、支持独立更新维护、促进跨语言调用和降低开发耦合度;然而这些优势也带来了运行时依赖、加载性能开销、版本兼容性复杂性和部署依赖性等挑战,形成了典型的空间换时间和灵活性换复杂性的权衡
动态库命名约定(去掉前后缀)
格式:lib<名称>.so(Linux/Unix)或 lib<名称>.dylib(macOS)
链接时使用:-lmath 会自动查找 libmath.so
动态链接链接的是动态库,在编译时建立程序与动态库的"调用约定",在运行时由操作系统动态加载库并完成地址绑定,实现代码的共享和延迟加载
main.c → 编译 → main.o → 链接器 + libmath.so引用 → program(包含math的调用桩) → 运行时动态链接器加载libmath.so并绑定真实地址 → 运行
2.2动态库的创建与使用
创建动态库
# 编译为位置无关代码
gcc -fPIC -c math.c -o math.o
gcc -fPIC -c utils.c -o utils.o
# 创建动态库
gcc -shared -o libmymath.so math.o utils.o
# 或一步完成
gcc -fPIC -shared math.c utils.c -o libmymath.so
编译为位置无关代码 :gcc -fPIC -c 将源代码编译成可在内存任意位置执行的目标文件 ,为动态库创建基础组件
创建动态库 :gcc -shared 将多个位置无关的目标文件打包 成单个可在运行时被多个程序共享的动态库文件
一步完成方式:gcc -fPIC -shared 直接从源代码生成动态库,将编译和链接合并为单个步骤,简化创建过程
-fPIC(Flag Position Independent Code):生成位置无关代码,使代码可在内存任意地址加载执行动态库在运行时会被加载到内存的任意空闲地址,无法预先知道具体位置。因此必须生成能在任何地址正确执行的代码,所以需要-fPIC
使用动态库(形成可执行文件)
# 编译程序(链接动态库)
gcc main.c -L. -lmymath -o program
使用编译器时指定的-L和-l选项是为了让链接器在编译时找到动态库,验证符号存在并建立引用关系(确认:1. 调用的函数确实存在于某个库中;2. 建立程序到该库的调用链接框架)。
此时的可执行文件并不能直接运行,这是因为动态库函数调用地址绑定未完成,需要运行时重定位
运行时需要通过LD_LIBRARY_PATH或系统配置告诉操作系统的动态链接器(加载器)动态库的实际位置,以完成地址绑定和代码加载
动态库的搜索路径
Linux 搜索顺序:
- LD_LIBRARY_PATH 环境变量指定的目录
- /etc/ld.so.cache 中缓存的路径(由 ldconfig 维护)
- 默认目录:/lib, /usr/lib, /usr/local/lib
Windows 搜索顺序:
- 程序所在目录
- 系统目录(C:\Windows\System32)
- PATH 环境变量指定的目录
使用动态库(配置动态库路径)
(1)安装到系统目录
将动态库直接复制到 /lib64 或 /usr/lib64 等系统标准库目录
# 1. 复制动态库到系统目录
sudo cp /path/to/libmymath.so /usr/local/lib/
# 2. 更新动态库缓存
sudo ldconfig
# 3. 直接使用(不需要额外路径)
gcc main.c -lmymath -o program
./program
(2)创建系统软链接
# 1. 创建软链接(库文件保持原位置)
sudo ln -s /home/user/project/lib/libmymath.so /usr/lib/libmymath.so
# 2. 可选择更新缓存
sudo ldconfig
# 3. 使用方式不变
gcc main.c -lmymath -o program
./program
(3)设置环境变量
# 1. 临时设置(当前终端有效)
export LD_LIBRARY_PATH=/home/user/project/lib:$LD_LIBRARY_PATH
# 2. 编译运行
gcc main.c -L/home/user/project/lib -lmymath -o program
./program
# 3. 永久设置(用户级)
echo 'export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/user/project/lib' >> ~/.bashrc
source ~/.bashrc
# 4. 永久设置(系统级,不推荐)
sudo echo '/home/user/project/lib' > /etc/profile.d/mylib.sh
(4)配置系统路径
# 1. 创建配置文件
sudo nano /etc/ld.so.conf.d/mylib.conf
# 内容:/home/user/project/lib
# 2. 更新动态库缓存
sudo ldconfig
# 3. 验证是否生效
ldconfig -p | grep mymath
# 4. 使用(系统已识别路径)
gcc main.c -L/home/user/project/lib -lmymath -o program
./program
ldconfig
ldconfig 是用于管理动态链接器运行时绑定的工具,主要功能是扫描系统库目录、创建共享库缓存,加速动态库的查找速度
主要功能:创建/更新缓存
# 扫描配置的目录,生成 /etc/ld.so.cache
sudo ldconfig
- 读取 /etc/ld.so.conf 和 /etc/ld.so.conf.d/*.conf
- 扫描所有配置目录中的 .so 文件
- 创建二进制缓存文件 /etc/ld.so.cache
- 加速运行时库查找(避免每次遍历目录)