库的制作和原理
这里写目录标题
- 库文件
- 指令:ldd test
- 具体查看:file test
- 制作静态库
- 使用静态库
- 编写 Makefile
- 打包与发布
- 他人如何使用你的库?
- 制作动态库
- 使用 Makefile 构建动态库
- 命令行直接构建
- 编译链接动态库
- 为什么静态库没有这个问题?
- 动态库的加载查找问题
- 方法一:拷贝到系统目录
- 方法二:建立软链接
- 方法三:配置环境变量
库文件
库的本质,就是.o文件的集合
我们之前经常用的,就是c/c++的标准库
指令:ldd test
解释:查看tes里面用到了什么库函数
其中,静态链接用到的是静态链接库,动态链接用到的是动态链接库
windows的动态库文件的尾缀是.dll
windows的静态库文件的尾缀是.lib
Linux的动态库文件的尾缀是.so
Linux的静态库文件的尾缀是.a
具体查看:file test
c/c++使用gcc,g++编译链接的时候,默认使用动态链接
如果强制使用静态链接
gcc -test.c -o test -static
我们知道,我们的原文件是先编译后链接的,假设我现在写了两个文件,一个是string.c文件,一个是map文件
假设当前路径下存在上述文件,内容分别是 string.c 和 map.c 的实现。通常我们习惯直接使用gcc -o target main.c string.c map.c一次性完成编译和链接。
但更规范的流程应该是先分别编译,再统一链接:
编译阶段:
gcc-cmain.c-omain.o gcc-cstring.c-ostring.o gcc-cmap.c-omap.o链接阶段:
gcc-otarget main.o string.o map.o这种分离编译的好处在于:每个源文件编译成.o文件后,与其他源文件是独立的。这意味着你可以只提供.o文件(包含函数实现)和对应的.h头文件(声明函数接口),其他人只需编写自己的test.c,编译成.o后再与你的.o文件链接即可生成可执行程序。这样,对方无需知道你的.c源码也能完成编译。
库的本质就是 .o 文件的集合
当项目源文件很多时,逐个管理.o文件会非常繁琐。因此,我们需要将所有相关的.o文件打包成一个单一文件,这就是真正的库文件——静态库(.a)或动态库(.so)。
库文件本质上是 .o 文件的集合
制作静态库
ar(archive)是 GNU 归档工具,rc参数表示替换并创建(replace and create)。使用以下命令可以将所有.o文件打包成静态库:
ar-rclibmystdio.a *.o命令说明:
libmystdio.a:生成的静态库文件名。其中lib前缀和.a后缀是静态库的命名规范。*.o:表示当前目录下所有的.o文件,它们将被归档到库中。
执行后,会生成libmystdio.a静态库文件。
使用静态库
假设用户手头有string.h、map.h、main.c以及我们刚生成的libmystdio.a文件,使用步骤如下:
编译主程序:
gcc-cmain.c-omain.o此步骤生成
main.o目标文件。链接静态库生成可执行文件:
gcc-otarget main.o-lmystdio-L./参数说明:
-l mystdio:指定要链接的库名(去掉lib前缀和.a后缀)。-L ./:指定库文件的搜索路径为当前目录。
编写 Makefile
Makefile 中的%是模式匹配符(静态模板),$用于提取具体文件名(动态变量)。常用自动化变量:
$^:表示规则中所有依赖文件的列表(例如map.o string.o)。$@:表示规则中目标文件的完整名称(例如libmystdio.a)。
一个简单的 Makefile 示例如下:
libmystdio.a: map.o string.o ar -rc $@ $^ %.o: %.c gcc -c $< .PHONY: clean clean: rm -rf *.o *.a至此,我们就成功制作了一个名为mystdio的静态库。发布库时,需要同时提供.a库文件和.h头文件(相当于库的使用手册)。
所以,.h 头文件主要是为了通过【编译】阶段的语法检查,而不是为了【链接】。
打包与发布
如何将库打包并发布给他人使用呢?可以在 Makefile 中添加output目标:
libmystdio.a: map.o string.o ar -rc $@ $^ %.o: %.c gcc -c $< .PHONY: clean clean: rm -rf *.o *.a mylib .PHONY: output output: mkdir -p mylib mkdir -p mylib/include mkdir -p mylib/lib cp *.h mylib/include cp *.a mylib/lib执行make output后,会生成一个mylib目录,其结构如下:
mylib/ ├── include/ # 存放所有头文件 └── lib/ # 存放静态库文件他人如何使用你的库?
对于第三方库,通常需要使用-l(小写 L)选项来链接。以下是三种常见的使用方法:
方法一:安装到系统目录
将库文件安装到系统默认搜索路径,之后编译时可直接链接。
sudocpmylib/include/* /usr/include/sudocpmylib/lib/libmystdio.a /lib64/ gcc main.c-omain-lmystdio方法二:使用-I、-L、-l选项指定路径
在编译时显式指定头文件路径、库文件路径和库名。
gcc main.c-omain-I./mylib/include-L./mylib/lib-lmystdio参数详解:
-I ./mylib/include:告诉编译器在预处理阶段,除了当前目录和系统目录,还要在指定目录下查找头文件。-L ./mylib/lib:告诉链接器在指定目录下查找库文件。-l mystdio:指定要链接的库名为mystdio。
方法三:建立软链接
将库文件软链接到系统库目录,头文件仍需复制。
sudoln-s$(pwd)/mylib/lib/libmystdio.a /lib64/libmystdio.asudocpmylib/include/* /usr/include/ gcc main.c-omain-lmystdio制作动态库
使用 Makefile 构建动态库
动态库的 Makefile 与静态库类似,但编译和链接参数有所不同:
libmystdio.so: map.o string.o gcc -shared -o $@ $^ %.o: %.c gcc -fPIC -c $< .PHONY: clean clean: rm -rf *.o *.so mylib .PHONY: output output: mkdir -p mylib mkdir -p mylib/include mkdir -p mylib/lib cp *.h mylib/include cp *.so mylib/lib关键参数说明:
-fPIC:生成位置无关代码(Position Independent Code),这是动态库所必需的。-shared:指示编译器生成共享库(动态库)。
命令行直接构建
如果不使用 Makefile,也可以通过以下命令手动构建动态库:
编译生成位置无关的目标文件:
gcc-fPIC-c*.c该命令会为所有
.c文件生成对应的.o文件,这些目标文件包含位置无关代码。打包生成动态库:
gcc-shared-olibmystdio.so *.o该命令将所有
.o文件链接成一个名为libmystdio.so的动态库。
编译链接动态库
编译链接动态库的命令与静态库类似:
gcc main.c-omain-Imylib/include/-L./mylib/lib/-lmystdio问题:编译成功但无法执行
虽然上述命令能够成功编译生成main可执行文件,但运行时可能会报错:
./main: error while loading shared libraries: libmystdio.so: cannot open shared object file: No such file or directory原因分析:
-L ./mylib/lib/只是在编译链接阶段告诉链接器在哪里查找libmystdio.so。- 当程序生成后,运行时由**加载器(loader)**负责加载动态库,而加载器并不知道
./mylib/lib/这个路径。 - 可以理解为:编译器知道库在哪里,但系统(运行时环境)不知道。
为什么静态库没有这个问题?
静态库的代码在链接时被完整拷贝到可执行文件中,程序运行时不再需要原始的.a文件。而动态库的代码在程序运行时才被加载,因此需要系统能够找到对应的.so文件。
动态库的加载查找问题
动态库的查找是运行时的问题,不是编译时的问题。有以下几种解决方案:
方法一:拷贝到系统目录
将动态库文件复制到系统默认的库搜索路径(如/lib64/或/usr/lib/):
sudocpmylib/lib/libmystdio.so /lib64/方法二:建立软链接
在系统库目录中创建指向动态库的软链接:
sudoln-s$(pwd)/mylib/lib/libmystdio.so /lib64/libmystdio.so方法三:配置环境变量
通过设置LD_LIBRARY_PATH环境变量,添加自定义库路径:
exportLD_LIBRARY_PATH=$LD_LIBRARY_PATH:$(pwd)/mylib/lib建议:
- 对于系统级库,建议使用方法一或方法二,将库安装到系统目录。
- 方法三(环境变量)主要用于临时测试,因为环境变量设置是临时的,重启终端后会失效。除非将其写入 shell 配置文件(如
~/.bashrc),但这会影响整个用户环境。
