静态库和动态库区别

静态库和动态库区别转自静态库和动态库区别前言我们在编写代码的时候经常用到已有的接口 他们是以库的形式提供给我们使用的 而常见形式有两种 一种常以 a 为后缀 为静态库 另一种以 so 为后缀 为动态库 那么这两种库有什么区别呢 说明 本文主要说明 Linux 下的情况 windows 不涉及 目标文件在解释静态库和动态库之前 需要简单了解一下什么是目标文件 目标文件常常按照特定格式来组织 在 linux 下 它是 ELF 格式 ExecutableLi 可执行可链接格式 而在 windows 下是 PE P

转自 静态库和动态库区别

前言

我们在编写代码的时候经常用到已有的接口,他们是以库的形式提供给我们使用的,而常见形式有两种,一种常以.a为后缀,为静态库;另一种以.so为后缀,为动态库。那么这两种库有什么区别呢?

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

目标文件

在解释静态库和动态库之前,需要简单了解一下什么是目标文件。目标文件常常按照特定格式来组织,在linux下,它是ELF格式(Executable Linkable Format,可执行可链接格式),而在windows下是PE(Portable Executable,可移植可执行)。

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

  • 可执行目标文件。即我们通常所认识的,可直接运行的二进制文件。
  • 可重定位目标文件。包含了二进制的代码和数据,可以与其他可重定位目标文件合并,并创建一个可执行目标文件。
  • 共享目标文件。它是一种在加载或者运行时进行链接的特殊可重定位目标文件。

我们来看一个简单实例:

//main.c #include<stdio.h> #include<math.h> int main(int argc,char *argv[]) { 
    printf("hello 编程珠玑\n"); int b = 2; double a = exp(b); printf("%lf\n",a); return 0; } 

代码计算e的2次方并打印结果。由于代码中用到了exp函数,它位于数学库libm.so或者libm.a中,因此编译时需要加上-lm。

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

$ gcc -c main.c #生成可重定位目标文件 $ readelf -h main.o #查看elf文件头部信息 ELF Header: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: REL (Relocatable file) (省略其他内容) 

通过上面的命令将main.c生成为可重定位目标文件。通过readelf命令也可以看出来:REL (Relocatable file)。

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

$ readelf -h /lib/x86_64-linux-gnu/libm.so.6 ELF Header: Magic: 7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - GNU ABI Version: 0 Type: DYN (Shared object file) (省略其他内容) 

不同系统中libm.so的位置可能不一样,你可以通过locate命令来查找。locate命令的用法可参考《Linux中的文件查找技巧》。从结果可以看到,libm.so是共享目标文件(Shared object file)。

查看可执行目标文件main:

$ gcc -o main main.o -lm #编译成最终的可执行文件 $ readelf -h main #查看ELF文件头 ELF Header: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) (省略其他内容) 

这里必须要强调一点,如果使用到的函数没有在libc库中,那么你就需要指定要链接的库,本文中需要链接libm.so或libm.a。可以看到,最终生成的main类型是Executable file,即可执行目标文件。

什么是静态库

前面所提到可重定位目标文件以一种特定的方式打包成一个单独的文件,并且在链接生成可执行文件时,从这个单独的文件中“拷贝”它自己需要的内容到最终的可执行文件中。这个单独的文件,称为静态库。linux中通常以.a(archive)为后缀

还是拿前面的例子来说,我们使用静态链接构建我们的可执行文件:

$ gcc -c main.c $ gcc -static -o main 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放在后面。放在最后时它是这样的一个解析过程:

  • 链接器从左往右扫描可重定位目标文件和静态库
  • 扫描main.o时,发现一个未解析的符号exp,记住这个未解析的符号
  • 扫描libm.a,找到了前面未解析的符号,因此提取相关代码
  • 最终没有任何未解析的符号,编译链接完成

那如果将-lm放在前面,又是怎样的情况呢?

  • 链接器从左往右扫描可重定位目标文件和静态库
  • 扫描libm.a,由于前面没有任何未解析的符号,因此不会提取任何代码
  • 扫描main.o,发现未解析的符号exp
  • 扫描结束,还有一个未解析的符号,因此编译链接报错

如果把-lm放在前面,编译结果如下:

$ gcc -static -lm -o main main.o main.o: In function `main': main.c:(.text+0x2f): undefined reference to `exp' collect2: error: ld returned 1 exit status 

更详细的解释也可以参考《一个奇怪的链接问题》。

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

$ ls -lh main -rwxrwxr-x 1 hyb hyb 988K 6月 27 20:22 main 

生成的可执行文件大小为988k。ls的高级用法可参考《ls命令常见实用用法》。

由于最终生成的可执行文件中已经包含了exp相关的二进制代码,因此这个可执行文件在一个没有libm.a的linux系统中也能正常运行。

什么是动态库

动态库和静态库类似,但是它并不在链接时将需要的二进制代码都“拷贝”到可执行文件中,而是仅仅“拷贝”一些重定位和符号表信息,这些信息可以在程序运行时完成真正的链接过程。linux中通常以.so(shared object)作为后缀。

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

$ gcc -o main main.c -lm #默认使用的是动态链接 

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

$ ls -lh main -rwxrwxr-x 1 hyb hyb 8.5K 6月 27 20:25 main 

可以看到,通过动态链接的程序只有8.5k

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

$ ldd main linux-vdso.so.1 => (0x00007ffc7b5a2000) libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fe9642bf000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe963ef5000) /lib64/ld-linux-x86-64.so.2 (0x00007fe9645c8000) 

正因为我们并没有把libm.so中的二进制代码“拷贝”可执行文件中,我们的程序在其他没有上面的动态库时,将无法正常运行。

有什么区别

到这里我们大致了解了静态库和动态库的区别了,静态库被使用目标代码最终和可执行文件在一起(它只会有自己用到的),而动态库与它相反,它的目标代码在运行时或者加载时链接。正是由于这个区别,会导致下面所介绍的这些区别。

可执行文件大小不一样

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

占用磁盘大小不一样

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

扩展性与兼容性不一样

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

依赖不一样

静态链接的可执行文件不需要依赖其他的内容即可运行,而动态链接的可执行文件必须依赖动态库的存在。所以如果你在安装一些软件的时候,提示某个动态库不存在的时候也就不奇怪了。

即便如此,系统中一班存在一些大量公用的库,所以使用动态库并不会有什么问题。

复杂性不一样

相对来讲,动态库的处理要比静态库要复杂,例如,如何在运行时确定地址?多个进程如何共享一个动态库?当然,作为调用者我们不需要关注。另外动态库版本的管理也是一项技术活。这也不在本文的讨论范围。

加载速度不一样

由于静态库在链接时就和可执行文件在一块了,而动态库在加载或者运行时才链接,因此,对于同样的程序,静态链接的要比动态链接加载更快。所以选择静态库还是动态库是空间和时间的考量。但是通常来说,牺牲这点性能来换取程序在空间上的节省和部署的灵活性时值得的。再加上局部性原理,牺牲的性能并不多。

总结

静态库和动态库具体是何如链接的已经超出了本文的介绍范围,本文仅简单介绍了一些静态库和动态库的区别,另外文中提到的在其他的linux系统,也指的是同样处理器架构的系统。但是了解这些基本信息,就能够帮助我们解决很多编译问题了。更多内容可自己阅读装载,链接方面的书籍。后面的文章也会介绍更多相关信息。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/233591.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 原地算法矩阵置0_原地排序算法有哪些

    原地算法矩阵置0_原地排序算法有哪些给定一个 m x n 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。进阶:一个直观的解决方案是使用 O(mn) 的额外空间,但这并不是一个好的解决方案。一个简单的改进方案是使用 O(m + n) 的额外空间,但这仍然不是最好的解决方案。你能想出一个仅使用常量空间的解决方案吗?示例 1:输入:matrix = [[1,1,1],[1,0,1],[1,1,1]]输出:[[1,0,1],[0,0,0],[1,0,1]]示例 2:输入:matrix

    2022年8月9日
    4
  • 什么是Web 3.0?Web 3.0是什么

    什么是Web 3.0?Web 3.0是什么

    2022年3月7日
    117
  • request.getParameterValues与request.getParameter的区别

    request.getParameterValues与request.getParameter的区别一、简单的对比request.getParameter用的比较多,相对熟悉  request.getParameterValues(String  name)是获得如checkbox类(名字相同,但值有多个)的数据。  接收数组变量,如checkobx类型    request.getParameter(String  name)是获得相应名的数据,

    2022年7月22日
    6
  • 一般人到底要不要学Python_Python值得学吗

    一般人到底要不要学Python_Python值得学吗前言本人纯屌丝一枚,在学python之前对电脑的认知也就只限于上个网,玩个办公软件。这里不能跑题,我为啥说自学python,一般人我还是劝你算了吧。因为我就是那个一般人。基础真的很简单,是个人稍微认点真都能懂,这就是好多人说的python简单、易懂、好学,然后就是一顿浮夸的言论,误导那些小白,再然后那些小白也就跟着浮夸。这里我就给那些轻浮的人泼一桶冷水,懂跟学会是一码事吗?先来说哈python这个就业哈,我现在生活在祖国的肚皮上–成都,(嗯,有想了解川西迷你小环线的在下面留言哦),下面亲身经历,我喃,

    2022年9月3日
    2
  • python图像多层小波分解_Python中图像小波分解与重构以及灰度图加噪

    python图像多层小波分解_Python中图像小波分解与重构以及灰度图加噪Python中图像小波分解与重构以及灰度图加噪Python中图像小波分解与重构以及灰度图加噪最近需要做小波分解相关的东西,博客这里做一个简单的记录灰度图的小波分解与重构:fromPILimportImageimportmatplotlib.pyplotaspltfrommatplotlib.pyplotimportimshowimportnumpyasnp#小波库impo…

    2022年10月21日
    0
  • 数学分析 反常积分(第11章)

    数学分析 反常积分(第11章)一.反常积分的概念相对于普通的定积分(称为正常积分),下面提出2类反常积分1.无穷积分的提出:2.瑕积分的提出:二.无穷积分1.定义:2.性质三.瑕积分1.定义:

    2025年5月27日
    0

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号