简述静态库与动态库的概念及差异,c++静态库和动态库

  简述静态库与动态库的概念及差异,c++静态库和动态库

  来源:微信官方账号【编程明珠】

  作者:手表老师

  https://www.yanbinghu.com/2019/06/27/47343.html

  前言我们写代码的时候经常会用到现有的接口。它们以库的形式提供给我们,常见的形式有两种,一种是静态库。作为后缀的a;另一个是动态库。所以作为后缀。那么这两个库有什么区别呢?

  注:本文主要说明Linux下的情况,windows不涉及。

  目标文件在解释静态库和动态库之前,你需要知道什么是目标文件。目标文件通常以特定的格式组织。linux下是ELF(可执行可链接格式),windows下是PE(可移植可执行)。

  通常,目标文件有三种形式:

  让我们看一个简单的例子:

  //main . c # includesdio . h # includesdio . h # includes math . hint main(int argc,char * argv[]){ printf( hello programming pearl n );intb=2;doublea=exp(b);printf(%lfn ,a);return0}代码计算E的二次幂并打印结果。由于代码中使用了exp函数,它位于数学库libm.so或libm.a中,所以编译时需要添加-lm。

  生成可重定位的目标文件main.o:

  $gcc-cmain.c#生成可重定位目标文件$readelf-hmain.o#查看elf文件的头文件信息elf头文件:magic:7f 454 c 460201010000000000000 Class:ELF 64 data:2的完成,littlendinversion:1(当前)OS/ABI:Unix-SystemVabiVersion:0 type:rel(可重定位文件)(省略其他内容)生成main.c作为可重定位目标文件您还可以从readelf命令中看到,类型是REL(可重定位文件)。

  观察共享目标文件libm.so:

  $ readelf-h/lib/x86 _ 64-Linux-GNU/libm.so . 6 elf header:Magic:7f 454 c 4602010103000000000000 class:elf 64 data:2 complement,littlendinversion:1(当前)OS/ABI:Unix-GNU ABI version:0 type:Dyn(共享对象文件)(省略其他内容)libm . so在不同系统中的位置可能不同,可以定位命令可参考《Linux中的文件查找技巧》使用。结果显示libm.so是一个共享的目标文件。

  检查可执行目标文件main:

  $gcc-omainmain.o-lm#被编译成最终的可执行文件$readelf-hmain#。检查ELF文件的头文件ELF header:magic:7f 454 c 460201010000000000000 class:ELF 64 data:2的完成,littlendinversion:1(当前)OS/ABI:Unix-SystemVabiVersion:0 Type:Exec(可执行文件)(省略其他内容)这里必须强调一下,

如果使用到的函数没有在libc库中,那么你就需要指定要链接的库

。本文需要链接libm.so或者libm.a可以看到,最后生成的主要类型是可执行文件,也就是可以执行的目标文件。

  什么是静态库?前面提到的可重定位目标文件以特定的方式打包成单个文件,当链接生成可执行文件时,它将自己所需的内容从这个单个文件“复制”到最终的可执行文件中。这个单独的文件称为静态库。Linux通常有后缀。a(存档)。

  或者以前面的例子为例,我们使用静态链接来构建我们的可执行文件:

  $ gcc-cmain . c $ gcc-static-omain main . o-lm这个过程中会用到系统中的静态库libm.a。这个过程做了什么?首先,第一个命令会将main.c编译成

可重定位目标文件

main.o,第二个命令的static参数告诉链接器应该使用静态链接,-lm参数表示链接libm.a(同样,如果要链接libxxx.a,使用-lxxx即可)。因为在main.c中使用了libm.a中的exp函数,所以在链接时会将libm.a中需要的代码“复制”到最终的可执行main中。

  注意-lm必须放在后面。最后,是这样一个解析过程:

  如果把-lm放在前面会怎么样?

  如果在前面加上-lm,编译结果如下:

  $ gcc-static-lm-omain main . omain . o:在函数 main 中:main.c: (.text0x2f):对 exp collect2的未定义引用:错误:LD返回1退出状态有关更详细的解释,请参考《一个奇怪的链接问题》。

  让我们看看最终生成的文件大小:

  $ ls-LH main-rwxrwxr-x1 hyb hyb 988k June 2720:22 main生成的可执行文件大小为988k。关于ls的高级用法,请参考《ls命令常见实用用法》。

  因为最终的可执行文件已经包含了exp相关的二进制代码,所以这个可执行文件可以在没有libm.a的linux系统中正常运行

  什么是动态库?动态库类似于静态库,但在链接时并不将所有需要的二进制代码“复制”到可执行文件中,而只是“复制”一些重定位和符号表信息,在程序运行时就可以完成真正的链接过程。Linux通常有。所以(共享对象)作为其后缀。

  通常,我们编译的程序默认是一个实用的动态链接:

  默认情况下,$gcc-omainmain.c-lm#使用动态链接。让我们看看最终生成的文件大小:

  $ ls-LH Main-RWXRWXR-X1 HYB HYB 8.5K 6月27日20:25main可通过动态链接程序

只有8.5k

查看!

  此外,我们还可以通过ldd命令观察哪些动态库链接到了可执行文件:

  $ lddmainlinux-vdso . so . 1=(0x 00007 ffc 7 b5 a 2000)libm . so . 6=/lib/x86 _ 64-Linux-GNU/libm . so . 6(0x 00007 Fe 9642 BF 000)libc . so . 6=/lib/x86 _ 64-Linux-GNU/libc . so . 6(0x 0007 Fe 963 ef 5000)/lib 64/LD-Linux-x86-64。

  有什么区别?在这里,我们大致了解了静态库和动态库的区别。静态库与目标代码一起使用,并以可执行文件结束(它只会有自己的用途),而动态库,另一方面,在运行时或加载时链接它们的目标代码。正是这种不同,才会导致以下不同。

  可执行文件大小不同。

  从前面还可以观察到,静态链接的可执行文件比动态链接的可执行文件大得多,因为它从二进制文件中“复制”了所需代码的副本,而静态库只是复制了一些重定位和符号表信息。

  占用的磁盘大小不同。

  如果有多个可执行文件,那么静态库中同一个函数的代码会被复制多个副本,而动态库中只有一个副本,所以使用静态库占用的磁盘空间相对来说要比动态库大。

  不像兼容性。

  如果静态库中某个函数的实现发生变化,可执行文件必须重新编译。对于动态链接生成的可执行文件,只需要更新动态库本身,不需要重新编译可执行文件。因此,使用动态库的程序易于升级和部署。

  依赖就不一样了。

  静态链接的可执行文件可以不依赖于其他内容而运行,而动态链接的可执行文件必须依赖于动态库的存在。所以当你安装某个软件时,提示动态库不存在也就不足为奇了。

  即便如此,系统中有大量的一类常用库,使用动态库也没有问题。

  复杂就不一样了。

  相对来说,动态库的处理比静态库更复杂,比如运行时如何确定地址?多个进程如何共享一个动态库?当然,作为打电话的人,我们不需要理会。另外,动态库版本的管理也是一项技术活。这也超出了本文的范围。

  加载速度不同。

  因为静态库是在链接的时候和可执行文件链接的,而动态库是在加载或者运行的时候链接的,所以对于同一个程序,静态链接的加载速度要比动态链接的快。所以选择静态库还是动态库是对空间和时间的考虑。然而,一般来说,牺牲这种性能来换取程序的空间节省和部署灵活性是值得的。加上

局部性原理

,牺牲不了多少性能。

  总结静态库和动态库是如何链接的超出了本文的范围。本文只简单介绍静态库和动态库的一些区别。此外,本文中提到的其他linux系统也指具有相同处理器架构的系统。但是知道这些基本信息可以帮助我们解决很多编译问题。更多信息,可以看关于加载和链接自己的书。后面的文章也会介绍更多的相关信息。

  原地址:https://www.yanbinghu.com/2019/06/27/47343.html

  本文相关阅读参考书

  关注微信官方账号【编程明珠】,获取更多Linux/C/C/Python/Go/algorithms/tools等原创技术文章。后台免费获取经典电子书和视频资源。

  阅读原文查看最新内容。

简述静态库与动态库的概念及差异,c++静态库和动态库