一文搞懂动态库和静态库
什么是静态库?
Lib(Library)文件是静态库文件(linux下面对应.a文件),它包含一组编译好的目标代码,可以被静态链接到应用程序中。当应用程序被编译时,编译器会将所需要的静态库文件的代码复制到应用程序的可执行文件中,这样应用程序就可以在运行时独立地执行,不需要依赖外部的库文件。
静态库的缺点是什么?
通过上面的介绍就可以看到,当我们的可执行程序加载静态库的时候。编译器会将整个静态库文件复制到可执行文件中,想象一下,如果现在有三个应用程序需要用到同样一份静态库,那就要分别拷贝一份到各自的exe中,那如果有30个应用程序使用呢?显然这么做大的问题在于内存开销很大。你的应用程序也将变得非常庞大。
动态库有什么优点?
为了解决以上问题,我们自然而然就能想到能不能多个程序共享一份库,这可比每人拷贝一份强太多。 于是就有了动态库.dll(linux对应.so)。当应用程序被加载到内存中运行时,操作系统会自动加载 DLL 文件并将其链接到应用程序中。因此,一个 DLL 文件可以被多个应用程序共享,从而实现代码的复用和共享。当然你或许会有疑问,大家都用同一份这样效率不就变低了吗,程序在切换的时候就得轮流使用,没错,你说的是对的,但是相比较他的好处,这点小小的性能问题在现在的计算机上显然不是什么大问题。当然这样做的好处可不止共享,想象一下如果你的库里的某个小功能升级了,那你只需要让用户可以方便地升级库文件而无需重新编译整个应用程序。
我什么时候只用lib,什么时候只用dll?
如果你只使用静态链接,那么你只需要链接相应的静态库文件(.lib或.a),就可以把需要的代码嵌入到你的可执行文件中,不需要再使用动态链接库(.dll或.so)。这种方式相对于动态链接库,可以减少运行时的依赖和管理,但是可能会增加可执行文件的大。
有些使用场景下可能需要静态链接(即使用lib),比如嵌入式系统或者需要快速部署的应用程序。
如果需要多个应用程序共享同一个库的情况,那你只要在运行的时候调用这个dll就可以了。
在 Windows 上,你可以使用 LoadLibrary 和 GetProcAddress 函数来加载 DLL 并调用其中的函数。在 Linux 上,你可以使用 dlopen 和 dlsym 函数来实现相同的功能。
这个时候你的可执行文件和dll其实是分开的,当可执行文件需要的时候才会去调用dll。
那如果你想把第三方的dll编译到你的dll文件里去,这个时候光有dll就不行了,你还需要.lib。
总之,当你编译一个包含其他 DLL 代码的 DLL 时,需要同时提供这些 DLL 的二进制文件(即 .dll 文件)以及相应的导出库(即 .lib 文件),以便编译器能够正确地编译链接你的 DLL。
为什么我有dll还不够,编译的时候还需要.lib?
虽然你的 DLL 中已经包含了需要调用的函数和变量的代码,但是编译器仍然需要知道这些函数和变量的名称、参数、返回类型等信息,这些信息包含在导出库(export library)中。
当你链接一个 DLL 时,你需要提供该 DLL 的二进制文件(即 .dll 文件)以及相应的导出库(即 .lib 文件),以便编译器能够正确地编译链接你的代码。导出库告诉编译器需要链接哪些函数和变量,以及它们的名称、参数和返回类型等信息,从而使编译器能够正确地生成可执行代码。
简而言之,DLL 包含了可执行代码和数据,LIB 包含了链接器所需的一些信息。DLL 文件被其他程序调用,而 LIB 文件被用于链接 DLL 文件,以便其他程序可以调用 DLL 中的函数和变量。
在windows上我把别人的dll编译到我的dll既需要.dll同时也需要.lib,但是在linux平台上我为什么需要.so就可以呢
在 Windows 平台上,为了能够在编译时将需要使用的 DLL 文件链接到自己的程序中,需要使用该 DLL 的导出库(export library),也就是 .lib 文件。这是因为 Windows 操作系统使用的是基于名称的导入(name-based import)机制,在编译时需要确切地知道需要链接哪些函数和变量。因此,对于需要使用的 DLL,需要将其导出库与程序一起编译链接。
而在 Linux 平台上,使用的是基于符号的导入(symbol-based import)机制,它并不需要像 Windows 平台那样显式地链接导出库。在 Linux 上,共享库的导出符号信息保存在 .so 文件本身中,并且在运行时由动态链接器(dynamic linker)根据需要进行动态链接。因此,在 Linux 平台上,只需要将共享库文件(.so)放在正确的位置,就可以在运行时动态链接共享库。
需要注意的时候在Linux上默认所有函数都是导出的,但是在windows上并不是,需要你显示指定哪些函数或者类需要导出。在windows上,如果你想将自己的库暴露给外部使用,或者想使用外部库中的函数或数据,那么你需要在相关的函数或数据定义前添加 __declspec(dllexport) 关键字,将它们导出(Export)到库中。这样可以使得外部应用程序在链接你的库时可以访问这些函数和数据,否则你的函数只能在你的库内部访问。
实际上不管是windows的dll、lib、exe其格式都是PE,都是十分相似的,而linux下的ELF又和PE继承自COFF格式,所以这些格式的实现其实本质上都类似,如果要详细了解和学习,推荐下面这本书:《程序员的自我修养:链接、装在与库》。里面有一章写的非常详细。

浙公网安备 33010602011771号