静态库和动态库

1、编译过程

1.预处理:解释并展开源程序当中的所有的预处理指令,此时生成 *.i 文件。
2.编译:词法和语法的分析,生成对应硬件平台的汇编语言文件,此时生成 *.s 文件。
3.汇编:将汇编语言文件翻译为对应处理器的二进制机器码,此时生成 *.o 文件。
4.链接:将多个 *.o 文件合并成一个不带后缀的可执行文件。

gec@ubuntu:~$ gcc hello.c -o hello.i -E
gec@ubuntu:~$ gcc hello.i -o hello.s -S
gec@ubuntu:~$ gcc hello.s -o hello.o -c
gec@ubuntu:~$ gcc hello.o -o hello -lcgcc hello.c  -o  hello

2.ELF格式

2.1概述

对于上述编译过程,重点关注最后一步库文件的链接(gcc hello.o -o hello -lc):链接实际上是将多个.o文件合并在一起的过程。这些 *.o 文件合并前是 ELF 格式,合并后也是 ELF 格式。
ELF全称是 Executable and Linkable Format,即可执行可链接格式。ELF文件由多个不同的段(section)组成,如下图所示:
在这里插入图片描述
ELF格式的合并,实际上就是将多个文件中各自对应的段合并在一起,形成一个统一的ELF文件。在此过程中,必然需要对各个 *.o 文件中的静态数据(包括常量)、函数入口的地址做统一分配和管理,这个过程就叫做 重定位,因此未经链接的单独的 *.o 文件又被称为可重定位文件,经过链接处理合并了相同的段的文件称为可执行文件。
库的本意是library图书馆,库文件就是一个由很多 *.o 文件堆积起来的集合。

2.2 相关命令

(1) readelf 可以用来查看 ELF 格式文件的具体细节:

# 查看文件格式头部信息
gec@ubuntu:~$ readelf -h a.out# 查看各个section信息
gec@ubuntu:~$ readelf -S a.out# 查看符号表
gec@ubuntu:~$ readelf -s a.out

3.库文件

3.1概述

库的本意是library图书馆,库文件就是一个由很多 *.o 文件堆积起来的集合。本质上来说库是一种可执行代码的二进制形式,这个文件可以在编译时由编译器直接链接到可执行程序中,也可以在运行时由操作系统的runtime enviroment根据需要动态加载到内存中。

3.2分类

库文件分为两类:静态库和动态库。如:

win32平台下,静态库通常后缀为.lib,动态库为.dll ;
linux平台下,静态库通常后缀为.a,动态库为.so 。静态库:libx.a
动态库:liby.so

库文件的名称遵循这样的规范:
lib库名.后缀
其中,lib是任何库文件都必须有的前缀,库名就是库文件真正的名称,比如上述例子中两个库文件分别叫x和y,在链接它们的时候写成 -lx 和 -ly ,后缀根据静态库和动态库,可以是 .a 或者 .so:

  • 静态库的后缀:.a (archive,意即档案)
  • 动态库的后缀:.so (share object,意即共享对象)
    注意:不管是静态库,还是动态库,都是可重定位文件 *.o 的集合。

3.3目的

  • 模块化:库文件将功能模块化,使得程序结构更加清晰,易于管理和维护。
  • 简化部署:使用库文件可以简化软件的部署过程,因为它们可以在不同的程序之间共享,而不需要重复包含相同的代码。
  • 动态链接:动态库文件允许在程序运行时才链接,这样可以在不重新编译程序的情况下更新库,提供了更大的灵活性。
  • 减少内存占用:使用动态库时,由于多个程序可以共享同一份库文件,因此可以减少每个程序的内存占用。
  • 易于更新和维护:库文件的更新只需要替换原有文件,而不需要重新编译使用该库的所有程序,简化了维护工作。
  • 跨平台兼容性:库文件可以被设计为跨平台使用,增加了软件的可移植性。
    总的来说,库文件的使用是为了提高软件开发的效率、灵活性和可维护性,同时减少资源的重复占用。

4、静态库

1所谓静态库,就是在静态编译时由编译器到指定目录寻找并且进行链接,一旦链接完成,最终的可执行程序中就包含了该库文件中的所有有用信息,包括代码段、数据段等。
静态链接库在程序编译时会被链接到目标代码中,目标程序运行时将不再需要改动态库,移植方便,体积较大,浪费空间和资源,因为所有相关的对象文件与牵涉到库都被链接合成一个可执行文件,这样导致可执行文件的体积较大。

2.静态库的制作
假设功能文件 a.c、b.c 包含了一些通用的程序模块,可以被其他程序复用,那么可以将它们制作成静态库,具体的步骤是:
第一步,制作 *.o 原材料
gec@ubuntu:~$ gcc a.c -o a.o -c
gec@ubuntu:~$ gcc b.c -o b.o -c

第二步,将 *.o 合并成一个静态库
gec@ubuntu:~$ ar crs libx.a a.o b.o

可见制作静态库非常简单,制作完成之后,可以用命令 ar 查看库中所包含的 *.o 文件:
gec@ubuntu:~$ ar -t libx.a

  1. 静态库的常见操作
    3.1 查看静态库中的 .o 列表
    gec@ubuntu:~$ ar t libx.a #(t意即table,以列表方式列出
    .o文件)
    a.o
    b.o

3.2 删除静态库中的 .o 文件
gec@ubuntu:~$ ar d libx.a b.o #(d意即delte,删除掉指定的
.o文件)
gec@ubuntu:~$ ar t libx.a
a.o

3.3 向静态库增加 .o 文件
gec@ubuntu:~$ ar r libx.a b.o #(r意即replace,添加或替换(重名时)指定的
.o文件)
gec@ubuntu:~$ ar t libx.a
a.o
b.o

3.4 提取静态库中的 .o 文件
gec@ubuntu:~$ ar x libx.a #(x意即extract,将库中所有的
.o文件释放出来)
gec@ubuntu:~$ ar x libx.a a.o #(指定释放库中的a.o文件)

4.静态库的使用
库文件最大的价值,在于代码复用。假设在上述库文件所包含的 *.o 文件中,已经包含了若干函数接口,那么只要能链接这个库,就无需再重复编写这些接口,直接链接即可。

使用静态库 要是用静态库libadd.a,只需要包含add.h,就可以使用函数add()、sub()。

#include <stdio.h>
#include “add.h”
void main(){
printf(“add(5,4) is %d\n”,add(5,4));
printf(“sub(5,4) is %d\n”,sub(5,4));
}
静态库的文件可以放在任意的位置,编译时只需要找到该库文件即可。
gcc -c -I /home/xxxx/include -L /home/xxxxx/lib libadd.a test.c
1). 通过-I(是大i)指定对应的头文件
2). 通过-L制定库文件的路径,libadd.a就是要用的静态库。
3). 在test.c中要包含静态库的头文件。

总结:

编译时:gcc a.c liba.a -o project
相当于liba.a 代替了b.c c.c参与编译

5、动态库

1.概述
不管是动态库还是静态库,它们都是 *.o 文件的集合。动态库指的是以.so后缀的库文件。动态库在程序编译时并不会被链接到目标代码中,而是在程序运行时才被载入,因为可执行文件体积较小。有了动态库,程序的升级会相对比较简单,比如某个动态库升级了,只需要更换这个动态库的文件,而不需要去更换可执行文件。但要注意的是,可执行程序在运行时需要能找到动态库文件。可执行文件时动态库的调用者。
在实际应用中,动态库应用场合要远多于静态库,因为虽然动态库的运行时装载特性会使得程序性能有略微的下降,但换来的是不仅仅节省了大量的存储空间,更重要的是使得主程序和库松耦合,不互相捆绑,当库升级的时候,应用程序无需任何改动即可获得新版库文件的功能,这极大地提高了程序的灵活性。

2.库文件命名
静态库的名字一般为libxxxx.a,其中xxxx是该lib的名称;动态库的名字一般为libxxxx.so.x.y.z,含义如下图所示:
此处,符号链接的作用不是“快捷方式”,而是为了可以让动态库在升级版本的时候更加方便地向前兼容。一般而言,完整的动态库文件名称是:
lib库名.so.主版本号.次版本号.修订版本号
比如: libx.so.1.3.1
当动态库迭代升级时,其版本号会发生相应的改变。比如下面的版本更迭:
2021年3月08日发布:libx.so.1.0.0
2021年4月02日发布:libx.so.1.0.1
2021年4月23日发布:libx.so.1.0.2
2021年5月18日发布:libx.so.1.0.3
2021年8月09日发布:libx.so.1.1.0
2021年9月12日发布:libx.so.1.1.1
可以看到,修订版本号的更迭会比较频繁,次版本号次之,主版本号再次之。为了避免每次版本号的修改而重新编译,动态库一般会用一个只带主版本号的符号链接来链接程序,如:

gec@ubuntu:~$ ls -l
lrwxrwxrwx 1 root root    15 Jan 16 2020 libbsd.so.0 -> libbsd.so.0.8.7
-rw-r--r-- 1 root root 80104 Jan 16 2020 libbsd.so.0.8.7
gec@ubuntu:~$

这样一来,未来不管版本号如何变迁,只要主版本号不变,那么用户链接的库名永远都是 libbsd.so.0,而无需关心具体某个版本。而如果连主版本号都发生了改变,这一般是因为库不再向前兼容,比如删除了某些原有的接口,这种情况下,用户就需要重新编译程序。

3.制作库文件常用的参数
首先需要了解gcc编译库要用到一些参数,很重要。
在这里插入图片描述
4.制作动态库
不管是静态库还是动态库,都是用来被其他程序链接的一个个功能模块。与静态库一致,制作动态库的步骤如下:
将 *.c 编译生成 *.o
将 *.o 编译成动态库

把add.c编译成动态链接库libadd.so
#第一步:将源码编译为 *.o
gcc -fPIC -o libadd.o -c add.c
#第二步:将 *.o 编译为动态库
gcc -shared -o libadd.so libadd.o

也可以直接使用一条命令
gcc -fPIC -shared -o libadd.so add.c

5.动态库的使用
动态库的编译跟静态库并无二致,如:
gec@ubuntu:~$ pwd
/home/gec
gec@ubuntu:~$ ls lib/
libx.so
gec@ubuntu:~$ gcc main.c -o main -L./lib -lx
说明:
-L 选项后面跟着动态库所在的路径。
-l 选项后面跟着动态库的名称。
运行时链接
动态库的最大特征,就是编译链接后程序并不包含动态库的代码,这些程序会在每次运行时,动态地去寻找并定位其所依赖的库文件中的模块,这是他们为什么被称为动态库的原因。
也就是说,如果程序运行时找不到动态库,运行就会失败,例如:
gec@ubuntu:~$ ./main
报错
出现上述错误的原因,就是因为运行程序 main 时,无法找到其所依赖的动态库 libx.so,解决这个问题,有三种办法:

1.编译时预告:
gec@ubuntu:~$ gcc main.c -o main -L. -lx -Wl,-rpath=/home/gec/lib
2.设置环境变量:
gec@ubuntu:~$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/gec/lib
3.将库文件拷贝到根目录下的/lib里面

总结

可以通过动态库(也称为共享库或共享对象文件)再构建一个动态库。在链接过程中,一个动态库可以依赖于其他动态库或静态库。

当你使用编译器(如gcc或clang)来构建动态库时,你可以指定其他动态库作为链接时的依赖。这些依赖的库在运行时会被动态加载。

以下是一个简单的例子,展示了如何使用gcc来从一个动态库(libA.so)构建一个依赖于它的新动态库(libB.so):

编译和链接第一个动态库(libA.so)
假设你有一个源文件a.c,你可以这样编译和链接它:

gcc -shared -o libA.so a.c

编译和链接第二个动态库(libB.so),它依赖于libA.so
假设你有一个源文件b.c,它调用了在libA.so中定义的函数。为了构建libB.so,你需要链接到libA.so:

gcc -shared -o libB.so b.c -L. -lA

注意-L.选项告诉链接器在当前目录(.表示当前目录)中查找库,而-lA选项告诉链接器链接到名为libA.so的库(注意,在-l选项后,库名通常不包含前缀lib和后缀.so)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/36282.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

压缩pdf文件大小的方法,如何压缩pdf格式的大小

pdf太大怎么压缩&#xff1f;当你需要通过电子邮件发送一个PDF文件&#xff0c;却发现文件太大无法成功发出时&#xff0c;这些情况下&#xff0c;我们都需要找到一种方法来压缩PDF文件&#xff0c;以便更便捷地进行分享和传输。PDF文件的大小通常与其中包含的图片、图形和文本…

入门JavaWeb之 Response 下载文件

web 服务器接收到客户端的 http 请求 针对这个请求&#xff0c;分别创建一个代表请求的 HttpServletRequest 对象&#xff0c;代表响应的 HttpServletResponse 对象 获取客户端请求过来的参数&#xff1a;HttpServletRequest 给客户端响应一些信息&#xff1a;HttpServletRe…

面试相关-接口测试常问的问题

1.为什么要做接口测试 (1)现在大多系统都是前后端分离的项目,前端和后端的进度可能不一样,那为了尽早的进入测试,前端界面没有开发完成的情况下,只要后端的接口开发完了,就可以提前做接口测试了; (2)基于安全考虑,只依赖前端进行限制,已经完全不满足系统的安全性…

电商卖家怎么快速采集复制1688全店宝贝到自己店铺?淘/猫/拼/抖都适用!

1688上面的货源品类丰富&#xff0c;很多卖家都是在这里找厂家&#xff0c;当我们找好厂家后&#xff0c;怎么将厂家店铺里所有宝贝都复制到自己店铺呢&#xff1f; 虽然1688平台本身支持铺货到其他平台&#xff0c;但一个个铺货太耗费时间了。 阿里巴巴中国站获得1688商品详…

【AI大模型RAG】深入探索检索增强生成(RAG)技术

目录 1. 引言2. RAG技术概述2.1 RAG技术的定义2.2 RAG技术的工作原理2.3 RAG技术的优势2.4 RAG技术的应用场景 3. RAG的工作流程3.1 输入处理3.2 索引建立3.3 信息检索3.4 文档生成3.5 融合与优化 4. RAG范式的演变4.1 初级 RAG 模型4.2 高级 RAG 模型4.3 模块化 RAG 模型优化技…

会计报表分析

目录 一. 会计报表的种类 \quad 一. 会计报表的种类 \quad 反应财务状况的是资产负债表 反应经营成果的是利润表 有时间点的就是静态表 动态表就是有一个区间的, 比如一年, 一个季度等

探索这些有趣的API,让你的应用与众不同

在这个由数据驱动的时代&#xff0c;我们每天都在与各种应用程序和服务互动&#xff0c;却很少意识到它们背后的技术奇迹。API&#xff0c;作为这些互动的幕后英雄&#xff0c;不仅简化了开发过程&#xff0c;还扩展了技术的边界。有趣的API&#xff0c;特别是那些能够激发创新…

HTTP协议和Nginx

一、HTTP协议和Nginx 1.套接字Socket 套接字Socket是进程间通信IPC的一种实现&#xff0c;允许位于不同主机&#xff08;或同一主机&#xff09;上不同进程之间进行通信和数据交换&#xff0c;SocketAPI出现于1983年BSD4.2实现在建立通信连接的每一端&#xff0c;进程间的传输…

理解MySQL核心技术:外键的概念、作用和应用实例

引言 在数据库管理系统&#xff08;DBMS&#xff09;中&#xff0c;外键&#xff08;Foreign Key&#xff09;是维持数据一致性和实现数据完整性的重要工具。本文将详细介绍MySQL外键的基本概念、作用&#xff0c;以及相关的操作指南和应用实例&#xff0c;帮助读者掌握并灵活…

深入了解PHP的If...Else语句

PHP是目前最流行的服务器端编程语言之一&#xff0c;用于开发动态和交互式网站。在PHP编程中&#xff0c;控制结构是非常重要的概念&#xff0c;它们决定了代码的执行流程。其中&#xff0c;if…else语句是最常用的控制结构之一。本文将深入介绍PHP中的if…else语句&#xff0c…

算子级血缘和血缘查询管理

数据链路 血缘关系 应用场景&#xff1a;数据资产&#xff0c;数据开发&#xff0c;数据治理&#xff0c;数据安全等等 &#xff08;绿色箭头上面是数据治理&#xff09; 场景&#xff1a; 数据链路的高效盘点与理解 数仓模型的长效优化机制 风险影响的及时全面分析 重复…

Android Studio无法正确引入包内存在的类

Android Studio 无法识别同一个 package 里的类&#xff0c;显示为红色&#xff0c;但是 compile 没有问题。 重启&#xff0c;rebuild,clean都没有用。 多半是因为 Android Studio 之前发生了错误&#xff0c;某些 setting 出了问题。 解决方法如下&#xff1a; 点击菜单中的…

6月27日-四象限法则

四象限法则&#xff0c;又称为艾森豪威尔矩阵&#xff08;Eisenhower Matrix&#xff09;&#xff0c;是一种时间管理和任务优先级排序的方法。它将任务分为四个象限&#xff0c;帮助个人识别哪些任务最重要&#xff0c;哪些可以推迟或委托&#xff0c;以及哪些可以完全忽略。以…

【等保2.0超详细解读,收藏这一篇就够了!】

网络安全等级保护是指对国家、法人、其他组织、个人的重要信息&#xff0c;对信息的存储、传输、处理等过程进行的保障。分级保护的基本思路是“分级、按标准、结合技术和管理”&#xff0c;用安全保护和监测预警的方法&#xff0c;对潜在的安全风险进行检测和处理&#xff0c;…

Spring中的InitializingBean接口

使用方法 Slf4j Component public class MyBean implements InitializingBean {public MyBean() {log.info("> 构造方法");}Overridepublic void afterPropertiesSet() throws Exception {log.info("> afterPropertiesSet方法");} }Spring中的Bean注…

生命在于学习——Python人工智能原理(2.4.2)

四、Python的程序结构与函数 4.4 函数 函数能将代码划分为若干模块&#xff0c;每一个模块可以相对独立的实现某一个功能&#xff0c;函数有两个主要功能&#xff0c;分别是降低编程难度和实现代码复用&#xff0c;函数是一种功能抽象&#xff0c;复用它可以将一个复杂的大问…

使用函数open()的例子

代码&#xff1a; #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> int main(void) {int fd-1;char filename[]"test.txt";fdopen(filename,O_RDWR);if(-1fd){printf("Open file %s failure!,fd…

PyCharm左侧项目区域出现淡黄色背景如何解决

PyCharm左侧项目区域出现淡黄色背景如何解决 解决方法&#xff1a; 1、打开pycharm 文件 - > Setting-> 项目 -> 项目结构 2、添加内容根 为 你的项目根目录即可恢复

什么是港股通?港股通碎股如何进行交易佣金最低万0.8?

港股通是一种投资渠道&#xff0c;它允许符合条件的内地投资者通过内地的证券账户&#xff0c;间接地买卖在香港联合交易所上市的股票。这一机制是沪港通和深港通计划的一部分&#xff0c;旨在促进内地与香港资本市场的互联互通。 ### 港股通的特点包括&#xff1a; - 交易范…

无痛接入FastText算法进行文本分类(附代码)

AI应用开发相关目录 本专栏包括AI应用开发相关内容分享&#xff0c;包括不限于AI算法部署实施细节、AI应用后端分析服务相关概念及开发技巧、AI应用后端应用服务相关概念及开发技巧、AI应用前端实现路径及开发技巧 适用于具备一定算法及Python使用基础的人群 AI应用开发流程概…