LINUX——动/静态库

加油加油~

目录:

动/静态库是什么?

.o文件是什么?

以gcc编译器为例,查看xxx.i  xxx.s xxx.o文件

生成test.i文件(预处理)

生成test.s文件(编译)

生成test.o文件(汇编)

生成可执行程序(链接):

小结:

 如何使用动/静态库

使用动态库:

使用静态库:

动态库与静态库有什么区别?

动态库:

静态库:

验证:

1.动态库占空间较小,静态库栈空间较大

2.以动态链接形成的程序依赖动态库;以静态链接形成的程序不依赖库

动态库依赖关系:

静态库依赖关系:

如何自己创建动/静态库?

创建静态库:

1.创建源文件以及头文件

2.创建.o文件并将.o文件导入静态库

3.打包头文件与库文件

使用静态库:

1.创建main.c

2.编译链接,形成可执行程序

问题:为什么编译时要加 -I -L -l?

扩展:头文件需要被放进默认搜索路径吗?

原因:

验证:

创建动态库:

1.创建源文件以及头文件(与创建静态库的文件内容一致,这里就不费篇幅了)

2.创建.o文件并将.o文件导入动态库:

3.打包头文件和库文件

使用静态库:

1.创建main.c(与前文一致)

2.编译链接,形成可执行程序

一些问题、一些现象


动/静态库是什么?

简单来说,二者是一个装了许多.o文件的仓库

.o文件是什么?

一个源文件要最终形成一个可执行程序,要经过以下4步:

  1. 1.预处理--完成头文件展开、去注释、宏替换、条件编译,形成.i文件
  2. 2.编译--完成词法分析、语法分析、语义分析、符号汇总等,检查无误后将代码翻译成汇编指令,形成.s文件
  3. 3.汇编--将汇编指令转换成二进制指令,形成.o文件
  4. 4.链接--将各个.o文件链接起来,形成一个可执行程序

以gcc编译器为例,查看xxx.i  xxx.s xxx.o文件

源文件:(test.c)

生成test.i文件(预处理)

gcc -E test.c -o test.i

  1. -E:只执行预处理动作
  2. -o:生成.i文件(-o用于生成-o后指定的文件 必须得加)

test.i文件内容

  1. 头文件被展开了
  2. 注释被删掉了
  3. 宏被替换了

生成test.s文件(编译)

gcc -S test.i -o test.s

  1. -S:执行到编译动作就停止
  2. -o:生成.s文件

test.s文件内容:

可以看到,c代码已经被转换成汇编代码了

生成test.o文件(汇编)

gcc -c test.s -o test.o

  1. -c(注意是小写):编译器执行到汇编就停止
  2. -o:生成.o文件

文件内容:

此时.o文件内存储的是二进制代码,但无法被识别

生成可执行程序(链接):

gcc test.o -o test

什么选项都不加代表着直接生成可执行程序

链接就是将所有的.o文件链接到一起,形成可执行程序,换言之,可执行程序是(>=1)的.o文件的集合

小结:

  1. gcc编译选项后的ESc分别对应着预处理(生成.i文件)、编译(生成.s文件)、汇编(生成.o文件)
  2. 不加选项意味着生成可执行程序,记得生成对应的文件时要加-o。
  3. 注意:其实xxx.i xxx.s xxx.o这些文件后缀无关紧要,重要的是它里面的文件内容,如果你乐意,你也可以将预处理后生成的文件写为xxx.p
  4. 经过上述分析,我们可以得出结论,动/静态库上存放了许多源文件的.o文件,也就是说动静态库上的.o文件是"链接即用"的,我们只要将他们与自己的.o文件链接(编译时链接/运行时链接),就可以形成想要的可执行程序了,库的出现提高了我们的编程效率。

 如何使用动/静态库

使用动态库:

不用做特殊处理,因为编译器默认就是用动态链接的方式链接.o文件使之成为可执行程序的

使用静态库:

gcc -static test.c -o test-s

-static:使用静态链接

动态库与静态库有什么区别?

动态库:

  1. 动态库的内容会被加载到共享区当中,当可执行程序在运行时,会在虚拟地址中的共享区寻找对应的库内容并链接到程序当中,因此该可执行程序所占空间较小;
  2. 动态库是可以供多份程序共用的;如何实现的(虚拟地址空间--页表--物理内存)
  3. 但是,由于程序是在运行时才去库中寻找内容,所以它与库具有关联性,不能独自运行;

静态库:

  1. 编译器会直接将库的内容拷贝到程序当中,因此程序所占空间较大;
  2. 但这保证了程序与库之间的独立性,即程序编译好后不再依赖库;

验证:

1.动态库占空间较小,静态库栈空间较大

2.以动态链接形成的程序依赖动态库;以静态链接形成的程序不依赖库

使用ldd+可执行程序名可以查看程序与库的依赖关系

动态库依赖关系:

可以 看到,test依赖的是libc.so.6这个库

但libc.so.6实际上是一个软链接

所以test依赖的实际上是c-2.17这个动态库

注:在Linux中,去掉lib与.so及其后面的版本号就是该库的名称,.so后缀表示这是动态库,.a后缀表示这是静态库

在Windows中,.dll后缀表示动态库,.lib表示静态库

静态库依赖关系:

非动态执行程序,换言之,静态可执行程序不依赖库

//库文件(实现)与头文件(声明)是不同的

如何自己创建动/静态库?

创建静态库:

由于库是.o文件的集合,所以我们要先自己创造一批.o文件,(.o文件是由源文件生成的),除此之外,我们还需要头文件,为将调用的库函数做声明。

注:头文件是用于在编译阶段,让编译器知道,我有这个函数的实现方法,使编译通过

库文件是在链接时要使用到的具体实现方法。

1.创建源文件以及头文件

头文件add.h

#pragma onceextern int my_add(int x, int y);

源文件add.c

int my_add(int x, int y)
{return x + y;
}

头文件sub.h

#pragma onceextern int my_sub(int x, int y);

源文件sub.c

int my_sub(int x, int y)
{return x - y;
}

2.创建.o文件并将.o文件导入静态库

创建.o文件

导入静态库

ar -rc libcal.a add.o sub.o

  1. ar:归档工具,用于生成静态库
  2. -rc:指令选项
  3. r:replace 若静态库文件当中的目标文件有更新,则用新的目标文件替换旧的目标文件。
  4. c:create 创建库
  5. libcal.a 生成的库名称
  6. add.o/sub.o 导入库中的目标文件

3.打包头文件与库文件

为了方便用户使用,我们可以创建一个目录,下设两个文件夹,分别存储头文件和库文件

至此就算完成了创建静态库,但想要使用,还要继续做如下工作。

使用静态库:

1.创建main.c

#include"add.h"
#include"sub.h"
#include<stdio.h>
int main()
{int addnum=my_add(1,2);int subnum=my_sub(1,2);printf("addnum:%d,subnum:%d\n",addnum,subnum);return 0;
}

2.编译链接,形成可执行程序

gcc main.c -I./mymathlib/include -L./mymathlib/lib -lcal

  1. -l:指定头文件路径
  2. -L:指定库文件路径
  3. -l:指定库名称

大家可以这样记忆:

  1. I 即Include --头文件都是用#include<xxx.h>表示,所以-I表示头文件路径
  2. L即Library--意思是库,所以-L表示库文件路径
  3. l即library--是Library的小写形式,所以-l表示具体的库名称

运行结果:

问题:为什么编译时要加 -I -L -l?

平常我们自己生成可执行程序,可没有加什么 -I -L -l选项,为什么现在却要加呢?显然,是由于我们使用了自己创建的静态库。gcc编译器默认会到指定的路径下去寻找程序所需要的头文件、库文件,所以我们才要指定路径,让编译器找得到对应文件。

所以只要我们将我们的头文件路径与库文件路径添加到编译器默认的搜索路径当中,在编译时也可以不再添加选项

系统中存放头文件搜索的路径是/usr/include

sudo cp ./mymathlib/include /usr/include

系统中存放库文件搜索的路径是/lib64

 sudo cp ./mymathlib/lib/libcal.a /lib64/

扩展:头文件需要被放进默认搜索路径吗?

笔者为什么会想到这个问题呢?因为从前我们在写程序时,也包含了自己写的头文件,但是我们也没有将它放进默认搜索路径当中去,程序一样正常运行。以我们上面写的代码为例(main.c),我们不用将头文件放进默认搜索路径当中 。

原因:

头文件分为两种:#include<xxx>和#include"xxx.h",也就是<>引用头文件和""引用头文件。前者一般是系统头文件,后者一般是我们自己写的头文件。编译器针对这二者,在搜索时路径会有所不同:

系统头文件:(按先后顺序进行搜索)

  • -I 参数指定的路径;
  • 环境变量 C_INCLUDE_PATHCPLUS_INCLUDE_PATH 包含的路径;
  • 内定路径。

用户头文件:(按先后顺序进行搜索)

  • 头文件所在目录路径;
  • -I 参数指定的路径;
  • 环境变量 C_INCLUDE_PATHCPLUS_INCLUDE_PATH 包含的路径;
  • 内定路径。

这也就解释了之前我们不将自己写的头文件放进默认搜索路径,编译一样没问题。

验证:

不指定-I 搜索路径,程序编译链接无误,且结果正确

创建动态库:

1.创建源文件以及头文件(与创建静态库的文件内容一致,这里就不费篇幅了)

2.创建.o文件并将.o文件导入动态库:

创建.o文件:

gcc -fPIC -c add.c ; gcc -fPIC -c sub.c

-fPIC:产生与位置无关码,这个编译选项很重要,是实现动态链接的关键

以下解释转载自:Linux动态库和静态库_libc-2.17.so-CSDN博客

-fPIC作用于编译阶段,告诉编译器产生与位置无关的代码,此时产生的代码中没有绝对地址,全部都使用相对地址,从而代码可以被加载器加载到内存的任意位置都可以正确的执行。这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的。

创建动态库并导入.o文件:

gcc -shared -o libcal.so add.o sub.o

  1. -shared:gcc编译选项,用于创建动态库
  2. -o:生成动态库名称
  3. add.o sub.o:导入动态库的目标文件

3.打包头文件和库文件

使用静态库:

1.创建main.c(与前文一致)

#include"add.h"
#include"sub.h"
#include<stdio.h>
int main()
{int addnum=my_add(1,2);int subnum=my_sub(1,2);printf("addnum:%d,subnum:%d\n",addnum,subnum);return 0;
}

2.编译链接,形成可执行程序

运行结果:

加载共享库时发生错误,找不到libcal.so这个共享库

这是由于:

执行程序的加载器也是要在默认路径下搜索库文件的,加载器并不知道我们的库文件存在哪个路径。(静态编译的文件就不一样了,因为它所需要的库文件都已经写进程序了)

解决方法:(让加载器找得到我们的库文件)

以下方法转载自:Linux动态库和静态库_libc-2.17.so-CSDN博客

1.将我们的库存放到默认搜索路径

sudo cp mlib/lib/libcal.so /lib64

2.更改LD_LIBRARY_PATH

LD_LIBRARY_PATH是程序运行动态查找库时所要搜索的路径,我们只需将动态库所在的目录路径添加到LD_LIBRARY_PATH环境变量当中即可。

运行结果:

3.配置/etc/ld.so.conf.d/

/etc/ld.so.conf.d/路径下存放的全部都是以.conf为后缀的配置文件,而这些配置文件当中存放的都是路径,系统会自动在/etc/ld.so.conf.d/路径下找所有配置文件里面的路径,之后就会在每个路径下查找你所需要的库。

一些问题、一些现象

问题:

Q:为什么基于静态库和使用动态库生成的两个可执行程序大小差不多?

依赖静态库生成的可执行文件

依赖动态库生成的可执行文件

A:因为编译器默认使用的动态链接

现象:

1.对依赖静态库的可执行文件使用动态链接,该文件可以正常运行,此时它并没有依赖静态库

  1. 静态库--动态链接:在链接时,库的属性信息会被嵌入到可执行文件当中,因此可执行文件在运行时不再依赖外部库文件。
  2. 动态库--静态链接:编译时错误

我们最好不要将它们混用,可能会出现意想不到的问题。

 2.将库留在程序所在目录与不将库留在当前目录的区别

  1. 对依赖静态库的程序不产生影响--都正常运行
  2. 对依赖动态库的程序产生影响

不留库:

程序找不到动态库,无法运行

留库:

程序能找到动态库,且能正常运行

尾言:如若文章有任何问题,欢迎各位在评价区留言指出,不胜感激~

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

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

相关文章

Git 基础指令

Git 基础指令 本章涵盖了我们在使用 Git 完成各种操作时将会用到的各种基本命令。 在学习完本章之后&#xff0c;我们应该能够配置并初始化一个仓库&#xff08;repository&#xff09;、开始或停止跟踪&#xff08;track&#xff09;文件、暂存&#xff08;stage&#xff09;…

模拟数字转换器

本节主要介绍以下内容&#xff1a; ADC简介 ADC功能框图详解 参考资料:《零死角玩转STM32》“ADC—电压采集”章节 一、ADC简介 ADC &#xff1a;Analog to Digital&#xff0c;模拟数字转换器 三个独立的ADC 1 / 2 / 3分辨率为12位每个ADC具有18个通道&#xff0c;其中…

代码随想录刷题笔记(DAY 10)

今日总结&#xff1a;快要期末考试了&#xff0c;现在在疯狂速成&#xff0c;今天稍微缓和了一点&#xff0c;应该能保证继续每天刷题&#xff0c;欠下的那些寒假补上。 Day 10 01. 用栈实现队列&#xff08;No. 232&#xff09; 题目链接 代码随想录题解 1.1 题目 请你仅…

AcWing1210-连号区间

文章目录 题目输入格式输出格式数据范围样例输入样例1输出样例1输入样例2输出样例2样例解释 思路代码 题目 输入格式 输出格式 数据范围 样例 输入样例1 4 3 2 4 1 输出样例1 7 输入样例2 5 3 4 2 5 1 输出样例2 9 样例解释 思路 固定L&#xff0c;遍历R在[L,R]区域中找到最大…

参数小,性能强!开源多模态模型—TinyGPT-V

安徽工程大学、南洋理工大学和理海大学的研究人员开源了多模态大模型——TinyGPT-V。 TinyGPT-V以微软开源的Phi-2作为基础大语言模型&#xff0c;同时使用了视觉模型EVA实现多模态能力。尽管TinyGPT-V只有28亿参数&#xff0c;但其性能可以媲美上百亿参数的模型。 此外&…

仿蓝奏云网盘 /file/list SQL注入漏洞复现

0x01 产品简介 仿蓝奏网盘是一种类似于百度网盘的文件存储和共享解决方案。它为用户提供了一个便捷的平台,可以上传、存储和分享各种类型的文件,方便用户在不同设备之间进行文件传输和访问。 0x02 漏洞概述 仿蓝奏云网盘 /file/list接口处存在SQL注入漏洞,登录后台的攻击…

Spring Boot - Application Events 的发布顺序_ApplicationEnvironmentPreparedEvent

文章目录 Pre概述Code源码分析 Pre Spring Boot - Application Events 的发布顺序_ApplicationEnvironmentPreparedEvent 概述 Spring Boot 的广播机制是基于观察者模式实现的&#xff0c;它允许在 Spring 应用程序中发布和监听事件。这种机制的主要目的是为了实现解耦&#…

RabbitMQ入门到实战——高级篇

消息的可靠性 生产者的可靠性&#xff08;确保消息一定到达MQ&#xff09; 生产者重连 这⾥除了enabled是false外&#xff0c;其他 initial-interval 等默认都是⼀样的值。 生产者确认 生产者确认代码实现 application中增加配置&#xff1a;&#xff08;publisher-returns…

《MySQL系列-InnoDB引擎06》MySQL锁介绍

文章目录 第六章 锁1 什么是锁2 lock与latch3 InnoDB存储引擎中的锁3.1 锁的类型3.2 一致性非锁定读3.3 一致性锁定读3.4 自增长与锁3.5 外键和锁 4 锁的算法4.1 行锁的三种算法4.2 解决Phantom Problem 5 锁问题5.1 脏读5.2 不可重复读5.3 丢失更新 6 阻塞7 死锁 第六章 锁 开…

深度解析Cron表达式:精确控制任务调度的艺术

深度解析Cron表达式&#xff1a;精确控制任务调度的艺术 希望我们都可以满怀期待的路过每一个转角 去遇见 那个属于自己故事的开始 去追寻那个最真实的自己 去放下 去拿起 安然&#xff0c;自得&#xff0c;不受世俗牵绊… 导言 在计算机科学领域&#xff0c;任务调度是一项关…

【PyQt5设计】:自动点击神器 - 解决重复性的点击和输入操作

文章目录 自动点击神器介绍测试窗口介绍自动点击神器的使用教程资源领取注意事项 自动点击神器介绍 本次使用PyQt5设计的【自动点击神器】旨在解决重复性的点击工作&#xff0c;解放双手&#xff0c;具有及时性和准确性&#xff0c;可选择坐标位置或图片两种方式实现鼠标的定位…

横版动作闯关游戏:幽灵之歌 GHOST SONG 中文版

在洛里安荒凉的卫星上&#xff0c;一件长期休眠的死亡服从沉睡中醒来。踏上发现自我、古老谜团和宇宙骇物的氛围2D冒险之旅。探索蜿蜒的洞穴&#xff0c;获得新的能力来揭开这个外星世界埋藏已久的秘密。 游戏特点 发现地下之物 探索这个广阔而美丽如画&#xff0c;充满密室和诡…

一个常用的项目架构图

给大家分享一个常用的架构图&#xff0c;需要使用的可以免费那走&#xff1a; 用户通过公网IP或者域名访问&#xff0c;穿过防火墙后&#xff0c;映射到nginx组件&#xff0c;在反向代理到各个模块&#xff1b;资源放在Nas盘&#xff0c;数据放在各个中间件&#xff1b;各个模块…

Android基于Matrix绘制PaintDrawable设置BitmapShader,以手指触点为中心显示原图像圆图,Kotlin

Android基于Matrix绘制PaintDrawable设置BitmapShader&#xff0c;以手指触点为中心显示原图像圆图&#xff0c;Kotlin 手指在上面的图上移动&#xff0c;“剪切”出上面图中以手指触点为中心的图&#xff08;半径图&#xff09;&#xff0c;然后在下面的ImageView显示。 impor…

中国大学生计算机设计大赛—人工智能实践赛赛道—赛后感想

1.比赛介绍 中国大学生计算机设计大赛是我国高校面向本科生最早的赛事之一&#xff0c;是全国普通高校大学生竞赛排行榜榜单赛事之一。自2008年开赛至2019年&#xff0c;一直由教育部高校与计算机相关教指委等或独立或联合主办。大赛的目的是以赛促学、以赛促教、以赛促创&…

【技能拾遗】——如何寻找/制作电子书

&#x1f4d6; 前言&#xff1a;无纸化学习已经越来越流行了&#xff0c;尤其是这几年大家上网课&#xff0c;把厚厚的课本和笔记装进pad里面&#xff0c;其便利性想必大家都深有感受。但是还有不少同学不知道该如何去找正规教材的PDF版本&#xff0c;下面就以我这两年的无纸化…

RTL编码(1)——概述

一、RTL级描述 RTL&#xff08;Register Transfer Level&#xff09;级&#xff1a;寄存器&#xff0b;组合逻辑&#xff0c;其功能与时序用Verilog HDL&#xff08;以下简称Verilog&#xff09;或VHDL代码描述。 RTL描述包含了同步数字电路最重要的三个特征&#xff1a;组合逻…

外贸自建站新手教程指南?海洋建站的技巧?

外贸自建站怎么做比较好&#xff1f;搭建外贸网站的参数有哪些&#xff1f; 外贸自建站成为许多企业迈向国际市场的重要一步。随着全球经济的不断发展&#xff0c;搭建一个专业而有效的外贸自建站对企业而言至关重要。海洋建站将为您提供一份详尽的新手教程指南&#xff0c;帮…

通过Studio 3T对Mongodb进行 创建数据库/集合 增删查改集合文档操作

首先 你需要安装Studio 3T 以及启动 Mongodb服务 具体可以参考我的文章 Studio 3T客户端连接Mongodb数据库服务 我们之前 通过 use 数据库名随便输切换是可以的 但除了这里能看到的 它们都仅存在于内存 我们右键顶部菜单 选择 添加数据库/创建数据库 这里 我们输入数据库名称…

HarmonyOS自定义组件生命周期函数介绍

aboutToAppear 在创建自定义组件的新实例后&#xff0c;在执行其build()函数之前执行。允许在aboutToAppear函数中改变状态变量&#xff0c;更改将在后续执行build()函数中生效。 aboutToDisappear 在自定义组件析构销毁之前执行。不允许在aboutToDisappear函数中改变状态变…