【Linux】静态库与动态库

news2024/11/15 21:02:10

前言

对于C/C++的学习者,我们经常听到C/C++的标准库,我们也经常使用它们,但是我们在使用的时候经常只包含一下头文件,然后就使用了,我们从来没有认真的研究过C/C++的标准库,而且C/C++的头文件中只有声明并没有声明的内容的具体实现,为什么我们只包含头文件就能使用库呢?本篇文章我们就来一起探讨一下。

静态库与动态库

  • 一、什么是库
  • 二、怎么制作一个库
    • 1、静态库
    • 2、静态库的使用
    • 3、 动态库
    • 4、动态库的使用
  • 三、动静态库的加载
    • 1、静态库的加载
    • 2、动态库的加载
  • 四、一些其他结论

一、什么是库

简单来说:库是一些可重定向的二进制文件,这些文件在链接时可以与其他的可重定向的二进制文件一起链接形成可执行程序。

一般来说库被分为静态库动态库,他们是有不同的后缀来进行区分的。

系统平台静态库动态库
Windows.lib.dll
Linux.a.so

另外对于C/C++来说其库的名称也是有规范要求的,例如在Linux下:一般要求是lib + 库的真实名称 +(版本号)+ .so /.a + (版本号),版本号是可以省略不写的。

例如这两个标准库 :
libstdc++.so.6    真实名称是 c++
libc-2.17.so     真实名称是 c

头文件与库的关系

  1. 头文件提供方法说明,库提供方法的实现,头和库是有对应关系的,是要组合在一起使用的
  2. 头文件是在预处理阶段就引入的,程序在链接时链接的本质其实就是链接库!

有了上面的一点基础知识以后我们就能够去见一见库了,Linux系统在安装时已经为我们预装了C&C++的头文件和库文件。

对于C/C++头文件在Linux里面一般在/usr/include目录下面存放我们的头文件

在这里插入图片描述

对于C/C++的库文件,一般在/usr/lib64/lib64里面,/lib64里面给的是root和内核所需so或者a之类的库文件,而/usr/lib64是普通用户能够使用的。

在这里插入图片描述

在这里插入图片描述

到这里我们也能够理解一些现象了:

  1. 我们在使用像vs2019这样的编译器时要下载并安装开发环境,这其中是在下载什么?

    答案是:安装编译器软件,安装要开发的语言配套的库和头文件。

  2. 我们在使用编译器,都会有语法的自动提醒功能,但是都需要先包含头文件,这时为什么呢?

    答案是:语法提醒本质是编译器或者编辑器,它会自动的将用户输入的内容,不断的在被包含的头文件中进行搜索,自动提醒功能是依赖头文件而来的!

  3. 我们在写代码的时候,我们的环境怎么知道我们的代码中有哪些地方有语法报错,哪些地方定义变量有问题?

    答案是:不要小看编译器,编译器有命令行的模式,还有其他自动化的模式,编辑器或集成开发环境可以在后台不断的帮我们调用编译器检查语法而不生成可执行文件,从而达到语法检查的效果。

二、怎么制作一个库

库的使用能够提高我们的开发效率,接下来我们来制作一个库!

1、静态库

静态库:程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库,当然这也会导致我们编译出来的可执行程序会变大。

看下面一段代码来演示库:

头文件:
在这里插入图片描述

源文件:

在这里插入图片描述

主程序:

在这里插入图片描述

文件目录结构:

在这里插入图片描述

可以看到我们将库的头文件与库的实现文件放在了mylib文件夹里面了,将main.c 放在了otherPerson里面,此时mian.c与库的头文件以及实现文件不在一起,此时编译会报错。

在这里插入图片描述

提示我们找不到头文件,就算我们将头文件移过去也会有链接错误,如果我们不想将源码给otherPerson,但是我们又想要main.c能完成编译形成可执行程序,我们就要对我们的库文件的实现进行编译但不生成最终的可执行程序。

在这里插入图片描述

然后我们将我们的头文件和可重定向的二进制文件进行拷贝到otherPerson里面
再进行编译链接,我们就形成了可执行程序test

在这里插入图片描述

运行test,程序执行成功

在这里插入图片描述

上面的整个过程就是我们制作静态库的基本流程,当然这样的制作其实还是有缺陷的,当我们的项目文件过于庞大时,我们要给一个.c文件十几个这样的.o文件,而且文件过于分散了,不利于管理,于是我们就需要将多个这样的.o文件打成一个包,我们将这个包直接给别人,别人就能直接使用了。

打包的命令是:ar -rc命令

ar命令用于建立或修改备存文件,或是从备存文件中抽取文件。可集合许多文件,成为单一的备存文件,在备存文件中,所有成员文件皆保有原来的属性与权限。

r :如果打包好的xxx.a库中没有 xxx.o 那么就会把模块 xxx.o 添加到库的末尾,如果有的话就会替换之(位置还是原来的位置)。

c :建立备存文件。

于是我们就尝试将原来的.o文件进行打包,不过要注意的是打包好的库要遵循库的命名规范。

在这里插入图片描述

此时我们的当前目录里面就有了静态库了,当我们删除静态库以后,我们用静态库生成的可执行程序还能够正常运行,这就是静态库的特点!!!

2、静态库的使用

指明路径

在我们实际使用库时,我们一般将头文件放在一个目录里面,将库放到另外一个文件里面,这样便于我们进行分类管理。我们也按照这种标准化的做法,来整理一下我们的目录结构。

在这里插入图片描述

我们libmymath.a静态库不是C的标准库,所以gcc不会在进行编译时去链接我们自己写的静态库,所以我们还要给gcc添加一些参数用来指明我们要链接的静态库。

在这里插入图片描述

(其中 -I - L -l ,其后面传递的内容可以加空格进行分割,也可以不加空格)

-I: 指明我们要包含的头文件路径
-L :指明我们包含的库的路径
-l:指明我们要包含的库文件名(这里的库文件名是指真实名称)

./test运行我们的程序,发现程序可以正常启动。

在这里插入图片描述

转移到系统的默认搜索路径中

对于C/C++的头文件Linux系统默认搜索路径是 :/usr/include
对于C/C++的库文件Linux系统默认的搜索路径是:/usr/lib64/lib64

我们将文件移动到对应的默认搜索路径中:
在这里插入图片描述

此时我们再进行编译我们的mian.c
在这里插入图片描述

这时gcc编译器报链接错误,提示我们找不到库,这时因为我们使用的是第三方库,编译时无论如何都要指明文件名!

在这里插入图片描述
指明路径以后,我们就能够正常编译了!

总结:第三方库的使用

  1. 需要指定的头文件,和库文件
  2. 如果没有默认安装到系统gccg++默认的搜索路径下,用户必须指明对应的选项,告知编译器: a.头文件在哪里 b.库文件在哪里 c.库文件具体是谁
  3. 将我们下载下来的库和头文件,拷贝到系统默认路径下,在Linux下就是安装库! 那么卸载呢?对任何软件而言,安装和卸载的本质就是拷贝到系统特定的路径下!
  4. 如果我们安装的库是第三方的库,我们要正常使用,即便是已经全部安装到了系统中,gcc g++必须用-l指明具体库的名称!

3、 动态库

动态库:程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。如果我们删除动态库,则使用动态库生成的可执行程序都将无法运行!

我们制作动态库时不再需要ar命令,我们需要一下两个步骤:

  1. gcc形成二进制文件时加上-fPIC参数,这样产生的可重定向二进制文件就会形成与位置无关码。
  2. 然后用gcc加上-shared参数,把所有有与位置无关码的可重定向二进制文件进行打包形成一个动态库。

先看一下我们的目录结构:

在这里插入图片描述
形成与位置无关码:

在这里插入图片描述

打成动态库:
在这里插入图片描述

当我们有了动态库以后,我们是可以删除可重定向的二进制文件的,但是动态库不能够删除,动态库删除的话,依赖此动态库的程序也将不能够运行!

下面我们尝试用动态库去链接形成可执行程序:

在这里插入图片描述

注意:我们自己写的库是属于第三方库,我们要编译时要指明:头文件路径,库文件路径,库文件名(真实名称)。

在这里插入图片描述

4、动态库的使用

可以看到我们已经使用动态库编译成功了,下面我们来运行一下我们的程序。

在这里插入图片描述
发生了错误,系统提示我们程序运行时,没有办法找到动态库,这是为什么呢?

这就和动态库的特性有关了,由于采用动态库的程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码,所以运行的程序必须要知道去哪里链接我们的库,即对于动态库在编译期间我们要告诉编译器去哪里链接库进行编译,在运行期间要告诉操作系统去哪里链接库进行运行。

静态库不需要链接是因为:静态库在编译链接期间将用户使用的二进制代码直接拷贝到目标可执行程序中,编译后的程序是一个完整的程序,不需要再运行时再使用静态库了。

解决操作系统找不到动态库的方法有三种:

环境变量

在我们Linux下有一个环境变量:LD_LIBRARY_PATH,操作系统会去这个环境变量下的路径去搜索动态库,我们可以将我们的第三方库加入到这个环境变量中,然后我们再运行我们的可执行程序就能成功了。

在这里插入图片描述
执行我们的程序:

在这里插入图片描述
程序执行成功!

但是我们都知道,环境变量只在一次登录内有效,如果我们采用环境变量的方法我们下次登录时依旧无法运行,所以这种方法具有临时性。

软连接

我们知道Linux中C/C++的默认库路径是/usr/lib64/lib64,这也是系统搜索库的默认路径,我们可以将我们的第三方库在这个目录下面建立一个软连接(不推荐直接将第三方库拷贝到默认库路径/usr/lib64/lib64下面),这样我们也能够正常使用了。

在这里插入图片描述

执行我们的程序,正常运行,软链接一个比较好的寻找库目录方法。

在这里插入图片描述

修改配置文件

在我们的Linux系统中有一个配置文件目录/etc/ld.so.conf.d,在这个目录里面我们可以创建一个文件,文件里面写上动态库的路径,这样我们系统在搜索动态库时也会搜索到该路径。

在这里插入图片描述
创建文件并填上我们的路径:
在这里插入图片描述

这样我们是配置文件就修改完毕了,但是我们还要让配置文件立即生效,我们可以使用ldconfig命令。

在这里插入图片描述

一切准备好以后我们就可以运行我们的程序了!

在这里插入图片描述

三、动静态库的加载

1、静态库的加载

在形成可执行程序的链接期间,静态库中的代码会被直接拷贝一份进入可执行程序内。所以在程序运行期间静态库可以理解为不会被加载,或者说静态库和程序一起被加载。

但是由于是静态库,当多个进程包含相同的静态库时这会导致内存中存在大量的重复代码,导致内存资源的浪费。

2、动态库的加载

采用动态库的程序在使用库中的方法时,会在使用的地方留下一个标记,在程序运行以后进行动态链接时,会将这个标记替换为动态库中的地址。

当一个使用了动态库的进程A运行起来以后在需要动态库a时,操作系统会先在内存中搜寻a,是否存在,如果存在,就直接将a通过页表进行映射进进程A的进程地址空间中的共享区中,如果不存在就会将磁盘中的动态库a加载进入内存,然后再通过页表进行映射。

我们知道被编译好的程序内部是有地址的!动态库内部的地址并不是绝对地址,而是偏移量!(相对地址)

因为不同的进程,运行程度不同,需要使用的第三库是不同的注定了,每一个进程的共享空间中空闲位置也是不确定的!如果采用了绝对编址,在一个进程使用了多个库时就有可能照成地址冲突!

当一个动态库,真正的被映射进地址空间的时候,它的起始地址才能真正确定! 此时动态库中的方法的地址就等于库的地址加上自己在库中的偏移量。通过这种设计方式,动态库在进程的地址空间中,可以随便加载,我们都能够找到库中的方法,也不会与其他库产生冲突了! 这就是与位置无关码

四、一些其他结论

  1. 动态库和静态库同时存在,系统默认采用动态链接
  2. 一般来说可执行程序在生成时,会对多个库进行链接,我们可以使用ldd命令查看我们的程序链接了那些库,可执行程序在连接时也可以选择部分采用动态库部分采用静态库。
  3. 如果我们对gcc加上了-static参数,gcc就会默认帮我们全部采用静态链接的方式链接库。

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

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

相关文章

文件上传,解析漏洞编译器安全(23)文件上传为什么加空格和修改为其他符号(例如换行符)问题

apache低版本解析漏洞 这个网站目录里有两个文件,一个是正常的php文件,另一个xx.php.xxx,源码是php源码,命名的文件,而访问中xxx的文件依旧可以执行出php代码的结果,而xxx就能当php文件解析,这…

uvc摄像头驱动uvc设备的注册分析

uvc摄像头驱动uvc设备的注册分析 文章目录 uvc摄像头驱动uvc设备的注册分析uvc_inituvc_probeuvc_register_videouvc_register_chainsuvc_register_termsuvc_register_video uvc_ioctl_opsuvc_fops uvc_init /driver/media/usb/uvc/uvc_driver.c /** UVC 驱动结构体*/ struct…

每日学术速递5.19

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.On the Hidden Mystery of OCR in Large Multimodal Models 标题:论大型多模态模型中 OCR 的隐藏奥秘 作者:Yuliang Liu, Zhang Li, Hongliang Li, Wenwen…

计算机图形学-GAMES101-11

显式几何的表示方法 Point Cloud 使用一系列点表示物体的表面。理论上可以表达空间中任何物体。如物体扫描会得到空间中一系列点,但在建模时我们要提取出大量多边形的面。点云对密度要求很高,因此不常使用。 Polygon Mesh 使用三角形表示物体。涉及三…

备份树莓派SD卡 — 保姆级教学

在我们树莓派项目开发的过程中,经常遇到以下问题: 1.自己辛辛苦苦开发出来的项目,害怕内存卡损坏,系统被破坏掉,想做一个备份。 2.自己的树莓派内存卡内存不够了,想将原来卡上的内容放在新的大容量内存卡…

Arm微架构分析系列3——Arm的X计划

1. 引言 前文介绍了Arm公司近几年在移动处理器市场推出的Cortex-A系列处理器。Cortex-A系列处理器每年迭代,性能和能效不断提升,是一款非常成功的产品。但是,Arm并不满足于Cortex-A系列每年的架构小幅度升级,又推出了X计划&#x…

esp32CAM环境安装教程---串口驱动安装

前言 (1)本人安装好arduino 的ESP32环境之后, 发现一直下载不进去程序。一直说Cannot configure port, something went wrong. Original message: PermissionError。 (2)查阅了很多资料,用了各种办法&#…

怎么消除文法的左递归性

除文法的左递归性可以采用以下方法: 直接左递归转换为间接左递归消除间接左递归 举例说明: 直接左递归转换为间接左递归 原文法:A → Aα | β 转换后的文法:A → βA A → αA | ε 例如:S → Sabc | ε 转换后…

4. QT中的事件函数 --- 鼠标事件、键盘事件、定时器事件、绘图事件

1. 说明 在QT的控件或者窗口当中,如果对于当前鼠标或者键盘的功能需要自己定义,可以重写父类当中对应虚函数,主要包括以下几个: //键盘按键按下 virtual void keyPressEvent(QKeyEvent *event); //键盘按键抬起 virtual void ke…

11.1网络编程

多线程 一、基础知识概念相关API二、任务创建一个简单的本地客户端迭代服务器select系统调用并发服务器数据报三、总结四、问题一、基础知识 概念 网络编程中客户端和服务器指的是进程,而不是常提到的机器或者主机。注意三个概念:请求、响应、事务。 网络编程中客户端-服务器…

面向对象的三大特性之继承(C++)

文章目录 继承的概念和定义概念定义定义格式继承关系和访问限定符继承基类成员访问方式的变化 基类和派生类对象赋值转换继承中的作用域派生类的默认成员函数继承与友元继承与静态成员菱形继承与虚拟继承菱形继承虚拟继承 继承的总结与反思继承和组合 继承的概念和定义 概念 继…

微信小程序 nodejs+vue+uniapp付费自习室图书馆教室座位系统-

系统分为用户和管理员角色 管理员的主要功能有: 1.管理员输入账户登陆后台 2.个人中心:管理员修改密码和账户信息 3.用户管理:对注册的用户信息进行添加,删除,修改,查询 4.自习室管理:对系统的自…

由浅入深Netty协议设计与解析

目录 1 为什么需要协议?2 redis 协议举例3 http 协议举例4 自定义协议要素4.1 编解码器4.2 什么时候可以加 Sharable 1 为什么需要协议? TCP/IP 中消息传输基于流的方式,没有边界。 协议的目的就是划定消息的边界,制定通信双方要…

每日学术速递5.18

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.Make-A-Protagonist: Generic Video Editing with An Ensemble of Experts 标题:Make-A-Protagonist:与专家合奏的通用视频编辑 作者:Yuyang Z…

云端一体助力体验升级和业务创新

随着音视频和AI技术的发展,在满足用户基础体验和需求情况下,更极致的用户体验和更丰富的互动玩法,成为各个平台打造核心竞争力的关键。LiveVideoStackCon 2022 北京站邀请到火山引擎视频云华南区业务负责人——张培垒,基于节跳动音…

虚幻引擎4利用粒子系统实现物体轨迹描绘

虚幻引擎4利用粒子系统实现物体轨迹描绘 目录 虚幻引擎4利用粒子系统实现物体轨迹描绘前言粒子系统利用粒子系统实现物体轨迹描绘创建粒子系统将粒子系统的产生位置绑定到运动物体上 小结 前言 由于在物体运动时,想要观察其总的运动轨迹,以便对其控制做…

Java实现天气预报功能

如果要实现类似百度天气、手机App这样的天气预报功能该如何实现?首先想到的是百度... 背景: 最近公司做了一个项目,天气预报的功能也做上去了,不仅有实时天气、未来7天预报的功能、还有气象预警的功能。 天气包括基本天气、白天夜…

【K8s】什么是helm?helm的常用指令

文章目录 一、Helm介绍1、背景2、介绍3、核心概念4、chart的基本结构5、helm官网 二、部署Helm1、安装helm客户端2、安装Tiller 三、常用指令1、仓库相关 helm repo2、chart相关3、release相关 四、入门案例1、构建第一个chart2、将chart包发布到Repository3、在 Kubernetes 中…

Nacos之服务注册中心

1.Nacos之服务提供者注册 官方文档 1.1.前期工作 1.1.1.新建Module - api-commons POM <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSc…

区块链商业模式

1. 引言 web2 vs web3&#xff1a; 基于区块链的商业模式有&#xff1a; 1&#xff09;Token Economy-Utility Token商业模式2&#xff09;Blockchain As A Service&#xff08;Baas&#xff09;商业模式3&#xff09;Blockchain-Based Software Products商业模式4&#xf…