静态库与动态库区别
什么是库
库是写好的,成熟的,可以复用的代码,一般程序运行都需要依赖许多底层库文件。
本质上来说库是一种可可执行代码的二进制形式,可以被操作系统载入内存执行,库有两种:静态库(.a、.lib)和动态库(.so、.dll)。
静态、动态是指链接,将一个程序编译成可执行程序步骤如下:
静态库
所谓静态库,是因为在链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中,对应的链接方式成为静态链接。
静态库与汇编生成的目标文件一起链接成为可执行文件,那么可以得出,静态库的格式跟.o文件格式相似,其实一个静态库可以简单看成是一组目标文件(.o/.ojb文件)的集合,即很多目标文件经过压缩打包后形成的一个文件。
- 优点
- 程序在运行时与函数库就没有关系,移植方便
- 缺点
- 浪费空间和资源,所有相关的目标文件与牵涉的函数库被链接合成一个可执行文件
Linux下创建与使用静态库
Linux下静态库命名规则
必须是lib{your_library_name}.a:lib为前缀,中间是静态库名,扩展名为.a
创建静态库(.a)
下面以一个简单四则运算C++类为例,将其编译为静态库给他人用。
头文件
class StaticMath {
public:
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();
};
实现
#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) {
return a / b;
}
void StaticMath::print() {
std::cout << "Static Math Library" << std::endl;
}
Linux通过ar工具,Windows下vs使用lib.exe,将目标文件压缩到一起,并且对其进行编号和索引,以便于查找和检索。一般创建静态库的步骤如下:
- 将代码文件编译为目标文件.o(StaticMath.o)
g++ -c StaticMath.cpp
- 通过ar命令将目标文件打包为.a静态文件
ar -crv libstaticmath.a StaticMath.o
生成静态库libstaticmath.a
使用静态库
- 编写测试代码
#include "StaticMath.h"
#include <iostream>
using namespace std;
int main(int argc, char *argv[]) {
double a = 10;
double b = 2;
cout << "a + b = " << StaticMath::add(a, b) << endl;
cout << "a - b = " << StaticMath::sub(a, b) << endl;
cout << "a * b = " << StaticMath::mul(a, b) << endl;
cout << "a / b = " << StaticMath::div(a, b) << endl;
StaticMath sm;
sm.print();
return 0;
}
Linux环境下使用静态库,只需要在编译的时候,指定静态库搜索路径(-L选项)和指定库名
(-l选项)
# 编译
g++ -o staticmatch StaticMathTest.cpp -L/home/username/googletest/mybuild/mytest -lstaticmath
# 执行
./staticmatch
动态库
为什么需要动态库?
- 静态库会造成空间浪费,如下图:
- 静态库对程序更新、部署和发布会带来麻烦,如果静态库更新,则所有使用它的应用程序都需要重新编译、发布给用户(一个小的改动,可能导致整个程序重新下载)。
动态库优点
- 可以实现进程之间资源共享(因此动态库也称为共享库),如下图:
- 使程序升级变得简单
Linux下创建与使用动态库
Linux下动态库命名规则
命名形式为libxxx.so,前缀是lib,后缀名为“.so”
创建静态库(.so)
类似四则运算代码
头文件
class DynamicMath {
public:
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();
};
实现
#include "DynamicMath.h"
#include <iostream>
double DynamicMath::add(double a, double b) {
return a + b;
}
double DynamicMath::sub(double a, double b) {
return a - b;
}
double DynamicMath::mul(double a, double b) {
return a * b;
}
double DynamicMath::div(double a, double b) {
return a / b;
}
void DynamicMath::print() {
std::cout << "DynamicMath Math Library" << std::endl;
}
与静态库不同,创建动态库不需要打包工具(ar,lib.exe),直接使用编译器即可创建动态库。
- 生成目标文件,加编译选项-fpic
g++ -fPIC -c DynamicMath.cpp
- 生成动态库,加编译选项-shared
g++ -shared -o libdynmath.so DynamicMath.o
上面两个命令也可以合并为一个
g++ -fPIC -shared -o libdynmath.so DynamicMath.cpp
使用静态库
- 编写测试代码
#include "DynamicMath.h"
#include <iostream>
using namespace std;
int main(int argc, char *argv[]) {
double a = 10;
double b = 2;
cout << "a + b = " << DynamicMath::add(a, b) << endl;
cout << "a - b = " << DynamicMath::sub(a, b) << endl;
cout << "a * b = " << DynamicMath::mul(a, b) << endl;
cout << "a / b = " << DynamicMath::div(a, b) << endl;
DynamicMath dyn;
dyn.print();
return 0;
}
由于动态库是在程序运行时进行链接,所以在程序运行时需要让系统能够找到动态库,系统一般会依次搜索:环境变量LD_LIBRARY_PATH、/etc/ld.so.cache文件列表、/lib、/usr/lib目录找到库文件后将其载入内存。因此主要有三种方法来设置动态库路径。
- 将动态库绝对路径加入环境变量LD_LIBRARY_PATH
- 将动态库绝对路径加入/etc/ld.so.cache文件中,步骤如下:
- 编辑/etc/ld.so.conf文件,加入文件所在目录的路径
- 运行ldconfig,重建/etc/ld.so.cache
- 将动态库移到/lib或/usr/lib中
# 编译
g++ -o dynamicmath DynamicMathTest.cpp -L/home/dgh/googletest/mybuild/mytest -ldynmath
# 执行
./dynamicmath # 需要先设置好动态库路径