静态库
核心特性:
- 链接时机:在编译时将库代码直接嵌入到可执行文件中。
- 文件格式:.a(Linux)、.lib(Windows)。
- 内存占用:每个使用该库的可执行文件都会包含一份完整的库代码副本。
创建静态库(.a文件)
- 编写源代码:首先,编写你需要的库函数的源代码文件,例如mylib.c
- 编译为目标文件:使用gcc/g++编译源码为目标文件(.o文件)
g++ -c mylib.cpp -o mylib.o
- 创建静态库:使用
ar
工具将目标文件打包为静态库(.a文件)
ar rcs libmylib.a mylib.o
- rcs:替换旧文件(r)、创建新库(c)、生成索引(s)
动态库
核心特性:
- 链接时机:在运行时由操作系统动态加载到内存(映射到地址空间的共享区)。
- 文件格式:.so(Linux)、.dll(Windows)。
- 内存占用:多个进程可共享同一份库的内存实例。
创建动态库(.so文件)
- 编写源代码:首先编写库函数的源代码文件,例如mylib.c
- 编译并生成位置无关代码(PIC):在编译为目标文件时,需要添加
-fPIC
选项以生成位置无关代码
g++ -c -fPIC mylib.cpp -o mylib.o
- 创建动态库:使用gcc/g++的
-shared
选项将目标文件链接为动态库(.so文件)
g++ -shared mylib.o -o libmylib.so
库的链接
在gcc/g++的编译和链接过程中,-I(大写i)、-L、-l(小写L)是三个常用的选项,它们分别用于指定头文件的搜索路径、库文件的搜索路径以及要链接的库的名称。
gcc -I /home/user/include -L /home/user/lib -lmylib my_program.c -o my_program
-I/home/user/include 告诉编译器在 /home/user/include目录下查找头文件。
-L/home/user/lib 告诉链接器在 /home/user/lib目录下查找库文件。
-Imylib告诉链接器链接名为mylib的库 (即查找libmylib.a或libmylib.so文件)。
【注】:
- 库就是功能性代码块的包装,也是包含:头文件 和 源文件(所谓的:库文件),头文件和库文件一般都是存储在各自的目录下,所以需要分别链接。
- 链接库名时要去掉前后缀,前缀lib,后缀 .a / .so。
上述操作是让编译器知道了文件路径,但链接完成后的程序运行时动态库还需要加载,加载器还不知道所使用的第三方动态库的路径。
五种方法解决:
- 将动态库文件拷贝到系统默认的库路径 /lib64/ 或 /usr/lib64/;
- 建立系统默认的库路径下的软链接指向第三方库;
- 将库的路径添加进环境变量 LD_LIBRARY_PATH 中
//临时追加
export LD_LIBRARY_PATH=/path/to/your/lib:$LD_LIBRARY_PATH
./your_program
- 编译时指定 rpath(硬编码路径到可执行文件)
g++ main.cpp -L./mylib -lmylib -Wl,-rpath='$ORIGIN/mylib' -o main
//-Wl,-rpath=...:将 rpath 写入可执行文件,$ORIGIN 表示可执行文件所在目录。
- 配置/etc/ld.so.conf.d/,ldconfig更新
在/etc/ld.so.conf.d/路径下创建一个 .conf文件,把动态库所在的路径放到这个.conf文件中,然后ldconfig 重新加载一份即可。
cd /etc/ld.so.conf.d/
touch myname.conf
vim myname.conf //打开后把路径复制粘贴进去就行
怎么简化链接库的命令呢?
- 如果自己创建的头文件和库文件 分别放在系统中存储库的文件的目录中时,就不需要-I和-L了,仅需链接指定库文件即可(就像使用C语言库直接编译那样,还是需要指定库名称)。
- 建立系统标准库目录下的软链接
eg:
#include “myinc/mymath.h”
gcc main.c -o main -lmymath
//在系统头文件和库文件的标准库目录下 分别创建指向自己创建的库的头文件和库文件的软链接
动态库在系统中加载之后,会被所有链接了该库的进程共享,这是如何被共享的?
答:动态库从磁盘加载到物理内存,然后这一份动态库文件可以与所有链接该库的进程建立映射,映射到每个进程的进程地址空间的共享区中。