【基础IO】谈谈动静态库(怒肝7000字)

文章目录

  • 前言
  • 实验代码样例
  • 静态库
    • 生成一个静态库
    • 归档工具ar
    • 静态库的链接
  • 动态库
    • 创建动态库
    • 加载动态库
  • 动静态链接
    • 静态链接
    • 动态链接
    • 动静态链接的优缺点

前言

在软件开发中,库(Library)是一种方式,可以将代码打包成可重用的格式,供其他程序调用。库可以分为静态库Static
Libraries)和动态库Dynamic Libraries 或 Shared Libraries)。这两种类型的库在链接和执行时有各自的特点和用途。本篇文章将围绕动静态库的原理及其使用展开讲解。

实验代码样例

/add.h/#ifndef __ADD_H__#define __ADD_H__ int add(int a, int b); #endif // __ADD_H__/add.c/#include "add.h"int add(int a, int b){return a + b;}/sub.h/#ifndef __SUB_H__#define __SUB_H__ int sub(int a, int b); #endif // __SUB_H__/sub.c/#include "add.h"int sub(int a, int b){return a - b;}///main.c#include <stdio.h>#include "add.h"#include "sub.h"int main( void ){int a = 10;int b = 20;printf("add(%d, %d)=%d\n", a, b, add(a, b));a = 100;b = 20;printf("sub(%d,%d)=%d\n", a, b, sub(a, b));}

解释以上代码:
该代码分为三个部分,一个是main函数,一个是add函数的声明与定义文件,一个是sub函数的声明与定义文件。

静态库

静态库是一种在程序编译时就被整合到可执行文件中的代码和数据集合。一般来说,无论是动态库还是静态库,其库中的内容都是一些被编译过但是还未被链接目标文件(以.o或者.obj结尾的二进制文件)。

为什么要将这些目标文件打包成库呢?

  1. 首先就是便于代码复用。将常用的功能打包成库使得这些代码可以轻松的在多个项目中重用。这大大节省了开发的时间,也有助于提高代码的一致性
  2. 使项目设计变得更加模块化。需要什么功能就添加什么库,将整个项目分解为更小更容易管理的模块。每个模块的库执行特定的功能,通过提供接口与其它模块交互。这有助于提高代码的维护性与可读性
  3. 版本控制和兼容性。库可以独立于使用它们的应用程序进行版本控制。开发者可以对库进行更新和改进。对于我们使用者来说,只需要更新一下就能使用最新的库了。
  4. 团队协作库允许不同的开发者或小组专注于特定的功能领域。例如,一个团队可以负责数据库交互的库,而另一个团队则可以专注于用户界面的组件。这样的分工促进了专业化,可以提高开发效率和产品质量。

生成一个静态库

根据库的特点,我们需要先将add.c文件和sub.c文件编译成目标文件,使用带-c选项的gcc指令:
在这里插入图片描述
接下来使用归档工具 ar 命令将一组对象文件打包成一个库文件。具体使用方式如下:
ar -rc libmymath.a add.o sub.o
其中,-rc是参数,表示替换、创建。libmymath.a表示要生成的库文件(一般静态库是以.a为后缀)。add.osub.o则是输入的对象文件。
在这里插入图片描述
用指令ar -tv查看库中的目录列表
在这里插入图片描述
选项t表示列出静态库中的文件,v表示详细信息。

归档工具ar

ar是一个用于创建和管理归档文件的工具,通常用于创建静态库。生成的归档文件是一个单独的文件,用来存储多个其它的文件和目录,常常在编译链接阶段用于组织静态库中的对象文件(.o文件)。ar来源于archive(归档),其主要功能就是把多个文件合并成一个文件,以便于管理。

虽然我们可以用-tv选项查看归档文件中的目录,但它本身并不是一个目录,只是看起来像而已!此外,ar的功能其实与zip类似,但是ar对于处理这种目标文件是非常有效的。

给出ar指令的选项功能:

  • r: 插入文件到归档中(如果归档已存在,这个选项会替换或添加文件)。
  • c: 创建归档文件,如果它不存在。
  • s: 创建一个对象文件索引(符号表),这对于链接器加速访问归档中的目标文件很重要。
  • t: 列出归档内容。
  • x: 从归档中提取文件。
  • d: 从归档中删除文件。
  • u: 只有当被添加的文件比归档中现有的同名文件更新时才添加文件。

总之,区别于我们用gcc一个一个链接目标文件,这种打包成库的方式简化了链接和构建的过程,显得非常的方便且灵活。

静态库的链接

当创建可执行文件时,如果程序依赖于某个静态库,链接器(linker)会将静态库中的相关对象文件整合到最终的可执行文件中。回到上面的代码样例。我们的main.c文件依赖于add与sub函数的实现,如果不链接库。按照我们之前的方式,只能一个一个链接源文件:
在这里插入图片描述
而现在我们已经将add.o与sub.o打包成了静态库libmymath.a,该怎么使用呢?
考虑使用以下指令:
gcc main.o -L/path/to/library -lexample -o main

  • 其中 -L/path/to/library 告诉链接器在哪个目录下查找库文件。如果库文件在标准库路径下例如 /usr/lib/ 或 /usr/local/lib/,可以省略这个选项。
  • -lexample 指定链接器使用名为 libexample.a 的库。注意这里的使用命名规则,并不是直接将库静态库的全名加上去,而是要进行一些“处理”。因为链接器会自动寻找以lib开头,.a结尾的文件。我们只需要提供去掉lib和.a的部分。-l选项表示指定添加库。

库搜索路径:

  • 从左到右搜索-L指定的路径
  • 由环境变量指定的目录 (LIBRARY_PATH)
  • 标准库路径 例如 /usr/lib/ 或 /usr/local/lib/

所以针对样例代码,我们可以这样链接静态库:
在这里插入图片描述
这样我们就成功的链接了一个静态库。
值得注意的是,一旦我们成功链接了某个静态库之后,该静态库中的所有数据和代码就存在可执行文件中了。所以我们之后即使把静态库删除也不会影响到程序的执行。这是一个一次性的过程。

动态库

动态库(Dynamic Libraries)是现代软件开发中常用的一种资源共享模块化技术。动态库能够使多个程序共享同一份库代码,而不需要将这些代码复制到每个程序的可执行文件中,从而节省系统资源并便于维护和更新。

与静态库不同的是,动态库被链接后存在于进程的共享区域内,该区域内的数据和代码可以供多个进程使用。而且是用时访问,即程序在运行阶段会不断地访问。也就意味着,如果我们在生成可执行文件之后,将动态库删除,程序便会报链接错误。这是和使用静态库不一样的地方。

我们可以观察动态库在程序的内存分布:
在这里插入图片描述
其中动态库在内存的数据共享区,该区域于其它进程共享数据。而静态库中的数据被链接之后就成为了代码段与数据段的一部分。

创建动态库

创建静态库使用了归档工具ar,而创建一个动态库gcc工具就可以。
考虑以下指令:
gcc -fPIC -c sub.c add.c
gcc -shared -o libmymath.so sub.o add.o

首先解析第一条指令:
选项-fPIC表示生成位置无关码。
什么叫位置无关码?

位置无关码的意思就是,生成的代码在内存中可以被加载到任何位置,而不是某个固定的地址。这种特性对于动态库尤其重要,因为动态库需要能够被多个不同的程序共享,并且每个程序可能将库加载到不同的地址空间。

位置无关码的寻址方式为相对寻址,可以将代码中的所有指针认为是一个个偏移量,不同的程序给与它不同的初始地址,这样就能灵活的将库加载到其它的地方。

生成位置无关码是创建动态库的标准做法,因为它确保库能在不同的应用程序和不同的运行实例中被正确地共享和使用。如果我们不用-fPIC选项生成位置无关码,程序在运行时就会报错。

在这里插入图片描述

所以第一条指令的意思就是将sub.c和add.c文件分别经过编译生成目标文件。
接下来就是将这些目标文件打包成库。

分析第二条指令:
选项-shared表示 链接器 生成一个动态库而不是默认的可执行文件。生成的动态库文件一般以.so结尾。

于是呢,经过这两条指令我们就得到了一个libmymath.so动态库.
在这里插入图片描述

加载动态库

当我们已经通过gcc编译器得到了一个动态库,该如何使用这个动态库呢?
如果我们直接像使用静态库那样直接链接,确实能编译通过,但是一旦我们尝试运行,就会得到以下报错:
在这里插入图片描述
为什么报错显示找不到这个动态库呢?我们不是在链接动态库的时候告诉了编译器动态库的路径了吗?

这一点非常容易理解。

我们确实将动态库路径告诉了gcc编译器,也确实编译成功了,得到了一个可执行文件。但是一个动态库是需要在运行时被访问的编译器能找到这个动态库并不表示操作系统能找到这个动态库

那为啥静态库这样运行就没问题呢?这是因为静态库是一次性工程,在链接阶段就把所有的代码和数据拷贝到程序的内部了!往后这个静态库文件在哪已经无所谓了。

所以,对于一个动态库而已,链接一个动态库的时候要告诉编译器库在哪,运行的时候就要告诉操作系统在哪。

解决方法:

运行时,操作系统会默认在/lib(不同的操作系统名字可能不一样)这个目录下去找动态库
在这里插入图片描述

1.将我们自己的动态库拷贝到/lib里面,就能成功运行了:
在这里插入图片描述
在这里插入图片描述

2.在/lib目录下创建一个动态库的软链接:
在这里插入图片描述
这样操作系统也能通过软链接找到我们的动态库
3.修改动态库默认路径的环境变量LD_LIBRARY_PATH:
考虑以下指令:
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
其中/usr/local/lib表示库路径
在这里插入图片描述
但是这样设置的环境变量在下一次登录就失效了,要想永久生效就得修改配置文件.bashrc:
在这里插入图片描述

找到LD_LIBRARY_PATH配置项,并在其路径下添加库的路径,这样每次登录都会自动生效。
如果你发现你的./bashrc没有LD_LIBRARY_PATH那你可以自己手动加一个环境变量LD_LIBRARY_PATH过去:export LD_LIBRARY_PATH=/path/to/your/libs:$LD_LIBRARY_PATH

动静态链接

静态链接

静态链接是在程序编译时将所有需要的库文件(通常是 .a 或 .lib 文件)内容直接复制到最终的可执行文件中的过程。这样,程序在运行时不再需要任何外部库。注意与静态库的区别,链接是一个动作,而库是一个名称。我们常把链接静态库的过程称为静态链接。

值得注意的是,gcc默认是动态链接。我们可以用指令file观察到这一点。
在这里插入图片描述
那如何使gcc静态链接目标文件呢?
使用-static选项:gcc -o main main.c -L. -lmth -static
在这里插入图片描述
再使用file观察,发现提示该可执行文件不是一个动态链接文件
在这里插入图片描述
这里可能会有人有疑问,为什么之前我们用的是静态库,默认是动态链接,还显示是动态链接呢?
如果不显示使用-static选项,系统会只将声明的这个库进行静态链接,其它的库就还是动态链接。
比如我们之前的gcc -o main main.c -L. -lmth,假如这个mth.a是一个静态库,那么就只会静态链接这个库,其它库都是动态链接。

动态链接

动态链接是编译过程中,程序被构建为在运行时加载外部共享库(如 .so 或 .dll 文件)的过程。这意味着程序在运行时依赖于这些库文件。动态链接一般用来链接动态库。
gcc默认就是动态链接,因此无需其它选项。值得注意的是,如果我们声明的库路径下面包含静态库和动态库,即同名的库,只是后缀不一样。gcc还是会优先考虑链接动态

动静态链接的优缺点

优点:

  • 静态链接生成的可执行文件包含了所有必要的代码,不依赖于外部的库文件,这使得部署更简单,只需要分发单一的可执行文件。
  • 静态链接性能快。在某些情况下,静态链接的程序启动速度比动态链接的程序快,因为它们在启动时不需要加载外部库。
  • 动态链接节省空间:多个程序可以共享同一个库的单一物理副本,这样可以显著减少系统的总体占用空间。
  • 动态链接更新方便,不需要重新编译,只需要替换库文件即可。

缺点:

  • 静态链接的可执行文件通常比动态链接的文件大,因为它包含了所有必要的库代码。
  • 静态链接更新比较麻烦,需要重新编译整个程序。
  • 动态链接过于依赖外部的动态库,一旦外部的库出现问题,导致很多程序都运行不了。
  • 动态链接的性能开销会比较大,因为需要加载外部的库。

总的来说,动态链接是用时间换空间,静态链接是用空间换时间,如何选择哪种链接方式取决于具体的环境。

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

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

相关文章

014Node.js时间格式包silly-datetime安装与使用

下载&#xff1a; https://www.npmjs.com/网站上下载silly-datetime 安装 npm i silly-datetime --save var sd require(silly-datetime);console.log(new Date()); //2024-04-18T04:40:38.505Zvar dsd.format(new Date(), YYYY-MM-DD HH:mm);console.log(d); //2024…

【1569】jsp学生学籍管理系统Myeclipse开发sqlserver数据库web结构jsp编程计算机网页项目

一、源码特点 jsp 学生学籍管理系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为sqlserver2…

李廉洋:4.20国际黄金,原油本周行情分析及下周一走势分析。

荷兰国际银行表示&#xff0c;所谓的美国国债期限溢价的回升&#xff0c;将为10年期国债收益率重返5%的关键水平铺平道路。从理论上来说&#xff0c;可将10年期美债收益率拆解为未来短端利率的期望期限溢价(term premium)。所谓期限溢价&#xff0c;是对投资者持有长期债券的风…

《机器学习by周志华》学习笔记-线性模型-02

1、对数几率回归 1.1、背景 上一节我们考虑了线性模型的回归学习,但是想要做分类任务就需要用到上文中的广义线性模型。 当联系函数连续且充分光滑,考虑单调可微函数,令: 1.2、概念 找一个单调可谓函数,将分类任务的真实标记与线性回归模型的预测值联系起来,也叫做「…

【php开发工程师系统性教学】——Laravel框架(验证码)的配置和使用的保姆式教程

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

OpenHarmony多媒体-mp3agic

简介 mp3agic 用于读取 mp3 文件和读取/操作 ID3 标签&#xff08;ID3v1 和 ID3v2.2 到 ID3v2.4&#xff09;,协助开发者处理繁琐的文件操作相关&#xff0c;多用于操作文件场景的业务应用。 效果展示&#xff1a; 下载安装 ohpm install ohos/mp3agicOpenHarmony ohpm环境配…

OpenHarmony网络协议通信—kcp

kcp 是一种 ARQ 协议,可解决在网络拥堵情况下 tcp 协议的网络速度慢的问题 下载安装 直接在 OpenHarmony-SIG 仓中搜索 kcp 并下载。 使用说明 准备一套完整的 OpenHarmony 3.1 Beta 代码 库代码存放路径&#xff1a;./third_party/kcp 修改添加依赖的编译脚本 在/develo…

SpringBoot-餐饮业供应商管理系统-94116

SpringBoot餐饮业供应商管理系统 摘 要 随着餐饮业竞争的加剧&#xff0c;不仅需要有吸引力的菜肴&#xff0c;还需要先进的管理手段&#xff0c;才能在餐饮业站稳脚跟。通过完善的餐饮业供应商管理系统&#xff0c;不仅可以帮助餐饮企业在物流配送、商品管理等方面有所改进&a…

【办公类-22-07】20240420 UIBOT下载高级保育员题目668题

作品展示&#xff1a; 背景需求 为了育婴师三级考试&#xff0c;使用UIBOT下载了题库里的三类题目&#xff0c; 【办公类-21-16】 20240410三级育婴师 344多选题&#xff08;题目与答案合并word&#xff09;-CSDN博客文章浏览阅读764次&#xff0c;点赞22次&#xff0c;收藏15…

kali下设置root权限(包含很多技巧)

默认是没有密码的&#xff0c;后期使用超级管理员root操作kali系统&#xff0c;我们需要设置 Ctrlshift再加上“”号可以调节字体大小 此时提示符为#

Nginx内存池相关源码剖析(一)总览

剖析nginx的内存池源码&#xff0c;讲解原理实现以及该内存池设计的应用场景 介绍 Nginx内存池是Nginx为了优化内存管理而引入的一种机制。在Nginx中&#xff0c;每个层级&#xff08;如模板、TCP连接、HTTP请求等&#xff09;都会创建一个内存池进行内存管理。当这些层级的…

ElasticSearch查询时修改打分

原生的ES打分基于BM25算法&#xff0c;相比于TF-IDF已经有了较大的改进&#xff0c;但是在实际场景中往往最终的排序效果还是需要进行调整。由于直接修改索引的权重往往代价较大&#xff0c;比较经济的方式还是在查询时即时修改得分以实现排序控制。 注&#xff1a;案例测试数据…