静态库与动态库区别

什么是库

库是写好的,成熟的,可以复用的代码,一般程序运行都需要依赖许多底层库文件。

本质上来说库是一种可可执行代码的二进制形式,可以被操作系统载入内存执行,库有两种:静态库(.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 # 需要先设置好动态库路径

参考资料

C++静态库与动态库

posted @ 2020-06-21 16:51  Galaxy_hao  阅读(860)  评论(0编辑  收藏  举报