-

一 什么是库文件

库文件是一组编译过的代码和资源的集合,它们可以被外部程序在编译或运行时被引用。

源代码库和预编译库的区别?

以C/C++库为例,提供源代码的第三方库会提供声明文件(.h)和实现文件(.c/.cpp),我们可以直接将其复制到自己的工程项目当中,和我们自己的头文件、源文件一起进行编译,必要时我们还可以魔改第三方库的源代码,以实现某些定制化的功能。

提供预编译库的第三方库会提供头文件(.h)以及静态库文件(.a/.lib)或动态库文件(.so/.dll),我们需要在程序中包含头文件,并显式的声明静态库或动态库的路径,这样程序在编译或运行时会自动的去相应的位置找到所需函数的具体实现。这些静态、动态库文件是二进制的,我们无法查看甚至修改库文件的内容。

为什么要使用预编译库?

  1. 保护知识产权,库文件可以隐藏实现细节,只提供调用接口;
  2. 提升编译效率,提供预编译好的库可以节省用户的编译时间;

什么是ABI兼容问题?

即不同平台、不同编译器、不同编译选项编译出的二进制库文件不能通用,比如用MSVC编译出的库文件无法在使用GCC作为编译器的项目中使用,在debug模式下编译出的库文件也无法在使用release模式的项目中使用,这是因为C++缺乏统一的编译标准,不同厂家的编译器对大小端、内存布局、字节对齐、调用约定、入参顺序等都有着自己的主张,以至于想要复用编译后的产物,必须限定相同平台相同编译器。这也是为什么我们在使用第三方库时总是需要用自己平台上的编译器重新编译第三方库的源代码。

而C语言的ABI兼容就相对较好,这是因为主流编译器厂家都形成了共识,它们编译出的ABI接口大都相同,因此用C语言编写的库可以很方便的被其他程序调用。

二 静态库与动态库

静态库(Static Library,.a或.lib文件)

  • 特点:静态库中的内容在程序编译阶段就会被包含到可执行文件中,即静态库的内容被融合到了每一份可执行程序当中。
  • 优点:
    • 部署简单,所需要的库代码都已经被包含在了可执行文件当中;
    • 性能更佳,避免了运行时的动态加载和解析。
  • 缺点:
    • 增加了可执行文件的大小,每个使用该库的可执行文件都会包含有库代码的副本;
    • 使得程序更新变得困难,需要重新编译和链接所有使用该库的程序。
  • 特点:动态库在程序运行时才被加载,且多个程序可以共用内存中的同一份动态库,可以认为动态库和可执行程序是相互独立的,程序通过动态库中定义的接口来使用库的功能。
  • 优点:
    • 节省磁盘空间和内存,多个程序可以共享同一份动态库;
    • 便于程序的更新和维护,只要输出接口不变(即名称、参数、返回值类型和调用约定不变),更换动态库文件不会对可执行文件造成影响,更新时只需替换动态库文件,不需要重新编译源程序。
  • 缺点:
    • 部署更复杂,需要提供相应的运行时库文件;
    • 性能略逊,程序需要在运行时动态加载库文件。

三 库的使用方式

静态库

  1. 程序调用

    将头文件包含进项目当中,然后使用#pragma comment(lib, “”)指令指定静态库文件的路径;

  2. IDE或CMake调用

    将头文件包含进项目当中,然后在IDE(诸如Visual Studio、CLion之类)中设置好静态库文件的路径,或者在CMakeLists.txt中指定静态库文件的路径。

动态库

  1. 隐式调用

    将头文件包含进项目当中,然后在IDE或CMakeLists.txt中指定动态库文件的路径(也可以将动态库文件所在位置设为系统环境变量,或直接将动态库文件复制到可执行文件所在目录下)(PS:针对MSVC编译出的动态库文件需要配合其同名的导入库文件使用,导入库文件的引用方式与静态库文件的引用方式相同)。

  2. 显示调用

    不需要头文件和导入库文件,只需要提供动态库文件,然后在程序中使用函数(Windows系统下使用LoadLibrary系列函数,Linux系统下使用dlopen系列函数)显示的指定动态库的路径以及要调用的函数名称,通过函数指针获取函数的地址从而进行调用。

四 一些注意事项

  1. 使用MSVC编译器编译动态库时,除了会产生dll文件,还会有一个同名的lib文件,注意,此处的lib文件并不是静态库文件,而是MSVC独有的导入库文件,其中包含了dll中函数或变量的符号,但不包含具体的实现,专门用于指引程序去相应的dll文件寻找函数的实现。因此即使是使用动态库,也需要在编译时指定该导入库文件的路径。要区分windows系统下的lib文件是静态库文件还是动态库文件,可以查看该lib文件的内容,其中有大量.dll字段的是导入库,有大量.obj/.o字段的是静态库。通常静态库文件的体积也会比较大,而导入库文件的体积很小。
  2. 使用GCC/MinGW编译器编译出的库文件会自动加上前缀lib。