Linux动静态库
1、库
在程序翻译时,会先分别将代码文件和头文件编译到 .o
文件,再通过程序翻译的链接阶段,将这些 .o
文件链接在一起形成 .exe
文件 。如果一个可执行程序需要链接很多个 .o
文件,且这些 .o
文件需要分类管理,那么这些 .o
文件就需要被打包才方便管理,库文件本质就是把.o
文件打包。
库的分类(按使用方式):静态库 (.a、.lib)和动态库(.so、.dll)。
静态、动态是指链接。
链接:我们的程序需要与某些库结合才能执行,与库结合的过程就是链接。每个语言都要有自己的标准库,在安装开发环境的时候就需要安装语言的标准库和头文件。

2、静态库
静态库形成可执行程序时,静态库在编译时代码会被完整地复制并链接到可执行文件中 ,即静态库直接被加载到了可执行程序的代码里 ,变成了可执行程序的一部分。所以,多个可执行程序使用同一个静态库时,每个可执行程序中都单独有静态库的拷贝。


2.1 静态库的形成
库的形成
- 将库的代码和头文件翻译成
.o
文件。- 将
.o
文件打包成库。
实例代码:
C++
#pragma once
class StaticMath {
public:
StaticMath(void) {};
~StaticMath(void) {};
static double add(double a, double b);//加法
static double sub(double a, double b);//减法
static double mul(double a, double b);//乘法
static double div(double a, double b);//除法
void print();
};
C++
#include "StaticMath.h"
#include <iostream>
// 加法实现
double StaticMath::add(double a, double b) {
return a + b;
}
// 减法实现
double StaticMath::sub(double a, double b) {
return a - b;
}
// 乘法实现
double StaticMath::mul(double a, double b) {
return a * b;
}
// 除法实现
double StaticMath::div(double a, double b) {
if (b == 0) {
std::cerr << "Error: 除0!" << std::endl;
return 0;
}
return a / b;
}
// 打印函数实现
void StaticMath::print() {
std::cout << "StaticMath class contains basic arithmetic operations." << std::endl;
}
首先,通过 g++ 让程序翻译在链接前停下来,这个命令默认形成的就是静态的 .o
文件(StaticMath.o)
C++
g++ -c [file] #形成静态.o


接着,要使用操作系统提供的命令将静态 .o
文件打包成静态库:
bash
ar -rc [file].a *.o #rc表示(replace and create)存在就替换,不存在就创建
或者使用
bash
ar -crv [库文件名].a [file].o #cr表示与rc相同,v显示每个被添加到库的文件
库文件的命名规范必须是"lib[name].a":lib为前缀,中间是库名,扩展名为.a。
上述两个方法的区别:
ar -crv [库文件名].a [file].o
只将单个目标文件[file].o
添加到静态库中。ar -rc [file].a *.o
会将当前目录下所有的.o
目标文件添加到静态库中。


2.2 静态库的使用
Linux下使用静态库,只需要在编译的时候,指定静态库的搜索路径(-L选项) 、指定静态库名 (不需要lib前缀和.a后缀,-l选项)。
-L:表示要连接的库所在目录
-l:指定链接时需要的动态库,编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.a或.so来确定库的名称。
更详细的内容在第三节。
3、动态库
对于动态库,动态库不直接加载到可执行程序里,而是动态加载到内存中 。所以多个可执行程序使用同一个动态库时,所有的可执行程序共用同一个动态库 。在程序运行时动态库的加载由操作系统动态加载到内存中 。所以相对于静态库,使用动态库要告诉系统动态库在什么地方。

3.1 动态库的形成
首先,通过 g++ 让程序翻译在链接前停下来,使用 g++ 形成动态的 .o
文件,还需要使用而外的选项,使库中各函数等的地址与位置无关:
bash
gcc -fPIC -c [file] #形成动态.o -fPIC表示与产生位置无关码(position independent code)
fPIC 创建与地址无关的编译程序(pic,position independent code),是为了能够在多个应用程序间共享。
然后,生成动态库,此时要加链接器选项-shared
bash
g++ -shared *.o -o [库文件名].so #-shared表示形成共享库格式(其实就是告诉g++不要形成可执行程序)
当然同静态库:
bash
g++ -shared -o [库文件名].so [file].o
上述指令也可以合并
bash
g++ -fPIC -shared -o [库文件名].so [file].cpp


3.2 动态库的使用
引用动态库编译成可执行文件跟静态库方式相同
4、可执行程序链接库
如果动静态库不在系统的库目录,g++就需要知道头文件和库的所在目录路径才能找到找到库 ,其中头文件只需要传它们所在的目录即可 ,但库文件除了给出目录的路径还需要给出库的具体名字。
g++链接库:
bash
g++ [file] -I [头文件路径] -L [库文件路径] -l [库文件名] #-I表示指定用户自定义头文件路径 -L表示用户自定义库文件路径 -l表示执行确定的第三方库名称
4.1 链接静态库



头文件不用给出名字的原因:
#inlcude<>
中的<>
表示在系统的库目录中查找 。实际上代码文件中的#inlcude<>
中就已经给出了头文件的具体名字,所以不用在 g++ 加上头文件的名字。如果是
#include""
则表示在当前路径查找,如果在""
里再带上相对路径或绝对路径,g++ 就能通过这个找到其他路径的头文件 ,如果这么写,g++ 里甚至可以不写头文件的路径。
4.2 链接动态库
同静态库链接

然而,当我们运行代码时,会发现程序运行错误

这是因为系统默认 的动态库搜索路径 中不包含 libdynmath.so
文件所在的目录,因此程序无法找到该库。具体解决方法在下一节。
5、找到库的方法
5.1 把库安装到系统(不推荐)
所有的系统动态库都在 lib64 目录下存储,如果把第三方库直接粘贴到该目录下,系统与该目录的动态库是松耦合关系,只要动态库在目录里就能直接被找到,且在可执行程序的代码中,头文件可以使用 <>
。
但是不推荐这种做法,原因是系统的库由系统管理,如果把第三方库直接放到系统库当中,会造成管理混乱,且库文件一旦多起来,在删除第三方库时也会有误删系统库的风险。
5.2 设置环境变量法(不推荐)
可以通过 LD_LIBRARY_PATH
设置临时环境变量,让系统在找寻库的时候在特定的路径下查找,添加环境变量的方式为:
bash
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:[库路径]
这种环境变量的添加方式为内存集,即该环境变量在每次系统登陆时都会刷新,不能永久保存。如果想永久保存设置的环境变量,可以修改 .barshrc 环境变量配置文件,让环境变量永久生效。
5.3 软链接法(推荐)
虽然不推荐把第三方库直接安装到 lib64 目录下,但是可以在 lib64 目录里建立一个第三方库的软链接 。相对于直接把第三方库拷贝到 lib64 下,这种方式能直观地显示哪些库是第三方库,哪些库是系统库,因为在 lib64 目录中的软连接肯定是自己放进去的第三方库,且软连接有自己的创建命令和删除命令,不用担心误删到系统库。用软连接链接第三方库的方法在实践中也常用到。
注意,在 lib64 目录下进行写操作需要 root 权限。
软连接创建命令:
bash
ln -s [被链接的文件的路径+名字] [软件链接的路径+名字].link
lib64 的绝对路径已经被保存在了环境变量里。
链接删除命令:
bash
unlink [link_name]
5.4 设置conf(推荐)
最推荐的方式是设置新增动态库搜索的配置文件,这种做法简单高效,和软连接法在实践中使用率很高。但要注意这种方法也需要 root 权限才能进行操作。
先进入这个目录:
bash
/etc/ld.so.conf.d
在该路径下添加一个文件:
bash
touch [name].conf
用vim打开刚刚新建的.conf
文件,在配置文件里将动态库的路径拷贝进去(绝对路径) ,然后使用 ldconfig
命令使配置文件生效。
bash
ldconfig

6. 动静态库的优缺点
6.1 动态库
优点:
-
更加节省内存并减少页面交换。
-
库文件与程序文件独立,只要输出接口不变,更换库文件不会对程序文件造成任何影响,因而极大地提高了可维护性和可扩展性。
-
不同编程语言编写的程序只要按照函数调用约定就可以调用同一个库函数。
-
适用于大规模的软件开发,使开发过程独立、耦合度小,便于不同开发者和开发组织之间进行开发和测试。
缺点:
-
运行时依赖,否则找不到库文件就会运行失败。
-
运行加载速度相较静态库慢一些。
-
需要对库版本之间的兼容性做出更多处理。
6.2 静态库
优点:
-
运行加载速度块。
-
使用静态库的可执行程序一旦形成,就与库无关。
缺点:
-
有多少个程序依赖静态库,静态库在代码中就有多少份拷贝,造成资源浪费。
-
修改静态库代码后,需要重新形成所有依赖这个库的可执行程序,造成时间浪费。
CSDN文章作者同名