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

news2024/11/17 3:51:49

文章目录

  • 前言
  • 实验代码样例
  • 静态库
    • 生成一个静态库
    • 归档工具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.coloradmin.cn/o/1611045.html

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

相关文章

隧道代理的优势与劣势分析

“随着互联网的快速发展&#xff0c;网络安全已经成为一个重要的议题。为了保护个人和组织的数据&#xff0c;隧道代理技术逐渐成为网络安全的重要工具。隧道代理通过在客户端和服务器之间建立安全通道&#xff0c;加密和保护数据的传输&#xff0c;有效地防止黑客入侵和信息泄…

docker安装并跑通QQ机器人实践(4)-bs-cqhttp搭建

go-cqhttp&#xff0c;基于 Mirai 以及 MiraiGo 的 OneBot Golang 原生实现&#xff0c;只需简单的配置, 就可以基于 go-cqhttp 使用框架开发&#xff0c;具有轻量, 原生, 高并发, 低占用, 跨平台等特点。 1 go-cqhttp 官网及可执行文件下载链接 go-cqhttp 官网&#xff1a;ht…

如何通过MSTSC连接Ubuntu的远程桌面?

正文共&#xff1a;666 字 12 图&#xff0c;预估阅读时间&#xff1a;1 分钟 前面我们介绍了如何通过VNC连接Ubuntu 18.04的远程桌面&#xff08;Ubuntu 18.04开启远程桌面连接&#xff09;&#xff0c;非常简单。但是有小伙伴咨询如何使用微软的远程桌面连接MSTSC&#xff08…

二维码门楼牌管理应用平台建设:取保候审的智能化监管

文章目录 前言一、取保候审的传统监管困境二、二维码门楼牌管理应用平台的优势三、取保候审备案信息的智能化处理四、保障居民合法权益五、展望未来 前言 随着信息技术的飞速发展&#xff0c;二维码门楼牌管理应用平台已成为现代社区治理的重要工具。本文重点探讨如何借助该平…

保姆级教程!QRCNN-BiLSTM一键实现多变量回归区间预测!区间预测全家桶再更新!

​ 声明&#xff1a;文章是从本人公众号中复制而来&#xff0c;因此&#xff0c;想最新最快了解各类智能优化算法及其改进的朋友&#xff0c;可关注我的公众号&#xff1a;强盛机器学习&#xff0c;不定期会有很多免费代码分享~ 今天对我们之前推出的区间预测全家桶进行…

最邻近插值和线性插值

最邻近插值 在图像分割任务中&#xff1a;原图的缩放一般采用双线性插值&#xff0c;用于上采样或下采样&#xff1b;而标注图像的缩放有特定的规则&#xff0c;需使用最临近插值&#xff0c;不用于上采样或下采样。 自定义函数 这个是通过输入原始图像和一个缩放因子来对图像…

JVM类加载基本流程及双亲委派模型

1.JVM内存区域划分 一个运行起来的Java进程就是一个JVM虚拟机&#xff0c;这就需要从操作系统中申请一片内存区域。JVM申请到内存之后&#xff0c;会把这个内存划分为几个区域&#xff0c;每个区域都有各自的作用。 一般会把内存划分为四个区域&#xff1a;方法区(也称 "…

力扣287. 寻找重复数

Problem: 287. 寻找重复数 文章目录 题目描述思路解题方法复杂度Code 题目描述 思路 利用二分查找搜索1 ~ n中重复的元素&#xff0c;我们每次取出当前二分查找的区间的中间元素mid并在元始的数组nums中统计小于mid的元素的个数count&#xff1a; 若count > mid则说明重复的…

大数据学习的第三天

文章目录 学习大数据命令的方式查看文件拷贝文件的方式添加数据的方式 出现了问题移动文件 hadoop工作流程和工作机制的方式namenodedatanodesecondarynamenode(主节点) 学习大数据命令的方式 查看文件 hadoop fs -cat /test/2.txt下载文件 hadoop fs -get -f /test/2.txt-f …

通俗说字解词:什么是道理?常说讲道理,李秘书讲写作这节就给你讲“道理”!

通俗说字解词&#xff1a;什么是道理&#xff1f;常说讲道理&#xff0c;李秘书讲写作这节就给你讲“道理”&#xff01; 说到“道理”&#xff0c;这可真是个有意思的词。它由“道”和“理”两个部分组成&#xff0c;就像一碗好吃的面&#xff0c;有汤有料&#xff0c;缺一不可…

xilinx cpri ip 开发记录

CPRI是无线通信里的一个标准协议&#xff0c;连接REC和RE的通信。 Xilinx有提供CPRI IP核。 区别于其它通信协议&#xff0c;如以太网等&#xff0c;CPRI是一个同步系统。 这就意味着两端的Master和Slave应当是同源时钟的&#xff0c;两边不存在频差&#xff0c;并且内部延时…

ikigai极简3p模型:想、能、有

ikigai模型简化为3p模型&#xff1a; - passion 想要、想做 - professional 能要、能做 - profit 有益、有利 根据三角形不可能定律&#xff0c;三者满足两个就很不容易了。又想做又能做的未必有钱&#xff0c;又能做又有钱的未必想做&#xff0c;又想做又有钱的未必能做。 要实…

(C语言)sscanf 与 sprintf详解

目录 1.sprintf函数详解 2. sscanf函数详解 1.sprintf函数详解 头文件&#xff1a;stdio.h 作用&#xff1a;将格式化的数据写入字符串里&#xff0c;也就是将格式化的数据转变为字符串。 演示&#xff1a; #include <stdio.h> struct S {char name[10];int height;…

LeetCode---128双周赛

题目列表 3110. 字符串的分数 3111. 覆盖所有点的最少矩形数目 3112. 访问消失节点的最少时间 3113. 边界元素是最大值的子数组数目 一、字符串的分数 按照题目要求&#xff0c;直接模拟遍历即可&#xff0c;代码如下 class Solution { public:int scoreOfString(string …

如何通过通过钉钉发送信息????????

1、通过钉钉群添加一个机器人 2、代码实现 /*** 发钉钉审核.** param*/private void sendDingDing(String tableName) {String url "https://oapi.dingtalk.com/robot/send?access_token229c627d05a3157f79a5ef1942d29c4dfb4515bf5c0ad65e3c69423bc016f97c";JSONOb…

达梦数据库的AWR报告

达梦数据库的AWR报告 数据库快照是一个只读的静态的数据库。 DM 快照功能是基于数据库实现的&#xff0c;每个快照是基于数据库的只读镜像。通过检索快照&#xff0c;可以获取源数据库在快照创建时间点的相关数据信息。 为了方便管理自动工作集负载信息库 AWR&#xff08;Auto…

网络编程初步

协议&#xff1a; 一组规则 分层模型结构&#xff1a; OSI七层模型&#xff1a;物、数、网、传、会、表、应 TCP/IP 4层模型&#xff1a;网&#xff08;链路层/网络接口层)、网、传、应 应用层&#xff1a;http、 ftp、 nfs、 ssh、 telneto o .传输层:TCP、UDP 网络层&…

累加(C语言)

一、题目&#xff1b; 二、N-S流程图&#xff1b; 三、运行结果&#xff1b; 四、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>int main() {//初始化变量值&#xff1b;int i 0;int j 0;int n 5;int result 0;int sum 0;//运算&#…

笔试强训未触及题目(个人向)

NC398 腐烂的苹果 1.题目 2.解析 这是一个广度优先搜索问题&#xff0c;我们可以先找到所有的烂苹果&#xff0c;把它加入到队列中&#xff0c;然后再同时让这几个苹果向外面腐蚀&#xff0c;我们可以用一个boolean数组来表示是否被腐蚀&#xff0c;也可以直接在原数组中将这…

ThingsBoard通过规则链创建报警信息

什么是规则引擎? 典型实例 1、复制根规则链为报警规则链路 2、拖动Script Filter 规则节点放入链中并配置如下脚本: 3、配置名称为&#xff1a;高温报警&#xff0c;并执行下面的脚本 4、将Script于上一个节点进行关联 5、选择动作里面的create alarm节点信息并放入其中…