静态库和动态库

动静态库

动静态库

  1. 静态库(Static Library):

    • 静态库是编译链接时的一种库文件形式,也称为静态链接库或静态目标文件。它是将一组目标文件(编译后的二进制文件)打包成一个单一的文件。静态库的文件扩展名通常为.a(在Windows上为.lib)。
    • 静态库的特点是在编译时被链接到应用程序中,成为应用程序的一部分。链接过程将库的代码和数据复制到最终生成的可执行文件中。
    • 静态库的优点是使用简单,可以在不依赖外部库的情况下,将所有必需的代码和数据打包到应用程序中,使得应用程序更加独立和可移植。但是,静态库会增加可执行文件的大小,并且如果多个应用程序使用相同的静态库,会导致代码的冗余。
    • 在使用静态库时,需要在编译和链接命令中显式指定静态库文件。
  2. 动态库(Dynamic Library):

    • 动态库是在运行时加载的库文件,也称为共享库或动态链接库(DLL)。它是一个独立的二进制文件,可以被多个应用程序共享使用。动态库的文件扩展名通常为.so(在Windows上为.dll)。
    • 动态库的特点是在应用程序运行时被动态加载到内存中。多个应用程序可以共享同一个动态库的实例,减少了内存占用,并且可以实现库的升级和更新,而无需重新编译和链接应用程序。
    • 动态库的优点是节省内存,减少可执行文件的大小,方便库的更新和维护。但是,使用动态库需要确保库文件在运行时可访问,并且需要依赖正确版本的动态库。
    • 在使用动态库时,需要在编译时指定库的头文件路径,在链接时指定动态库文件,运行时需要确保动态库文件位于正确的路径中。

动态库和静态库是两种常见的库文件形式,用于组织和共享代码。静态库在编译链接时被复制到应用程序中,使得应用程序更加独立和可移植,但会增加可执行文件的大小。动态库在运行时动态加载到内存中,多个应用程序可以共享同一个动态库的实例,减少内存占用,并方便库的更新和维护。使用时需要注意静态库和动态库的文件扩展名、编译链接命令和库的依赖关系。

在这里插入图片描述
在我们的Linux的服务器下就配置了很多的动静态库,我们平时都在使用c/c++的库,只要包一下头文件就可以使用,头文件提供方法的说明,库提供方法的实现,头文件和库要组合在一起使用,预处理阶段引入头文件,在链接阶段链接库

语法提醒和语法错误

  • 我们在使用编译器的时候,只要包了头文件,就会有语法提醒功能,这是因为编译器会不断的将我们输入的内容在头文件中搜索匹配

  • 我们输入的内容有一些语法错误的时候,经常会看见编译器用红色的波浪号提醒,这是因为编译器会将我们的代码去编译,编译的时候就会有报错,然后编译器就返回给我们

动静态库命名

  • 动态库命名:以lib开头,从左到右第一个.后面带上so,最后面的是版本号

在这里插入图片描述
去掉前缀,版本号和.so,库c++的真实名称为stdc++

  • 静态库命名:以lib开头,从做到右第一个.后面带上.a,最后面时版本号
    去掉前缀,版本号和.a,就是库的真实名称

动静态库的打包和使用

  • 静态库的打包:ar -rc lib文件名.a 所有.o文件即可打包成静态库
  • 查看静态库中包含的.o文件:ar -tv libmath.a名字要加上版本号,前缀和.a
  • 静态库的使用:gcc 程序名 -I头文件所在路径 -L库所在路径 -l库名

直接发.c文件和头文件

我们通过一个例子来引出动静态库的打包和使用

我们做了一个加和减的函数功能,假如我们想将的们的函数给被人使用,该怎么做呢?

创建文件
在这里插入图片描述
加法函数的声明和定义,左边的是myadd.c,右边myadd.h
在这里插入图片描述
减法函数的声明和定义
在这里插入图片描述
测试代码
加粗样式
成功运行
在这里插入图片描述

我们可以直接将源代码码也就是头文件和.c文件打包直接发给别人,别人就可以直接用了,但是这等于将我们的源代码公开了

发.o文件和头文件

我们不想给别人源代码,但是又不会打包成库,那么该怎么做呢?

我们可以将我们的.c文件预处理编译汇编成.o文件,然后在将.o文件和头文件发给别人,因为.o文件都是二进制,这样就不用给别人源码别人也可以用了

我们先将我们的函数声明和定义先放到另一个目录里
在这里插入图片描述

预处理编译汇编形成.o文件
在这里插入图片描述

再将.o文件和头文件发给要使用的人,之后别人只要将他的.c文件也形成.o文件就可以使用了
在这里插入图片描述

打包静态库

我们打包成库,在编译器看来都是第三方库,我们使用第三方库时,要告知编译器头文件在哪里(-I头文件所在路径),库名是什么(-l库名),库在哪里(-L库所在路径)

直接发库和头文件

我们上述直接发源代码,发.o文件,都是太麻烦了,我们可以打包形成库,然后发给别人

通过命令ar -rc lib文件名.a 所有.o文件即可打包成静态库

ar是gnu归档工具,rc表示(replace and create)

在这里插入图片描述
我们还可以查看静态库中有哪些.o文件ar -tv libmath.a

t:列出静态库中的文件,v:verbose 详细信息

在这里插入图片描述

我们现在有了静态库,头文件,我们直接编译看看
在这里插入图片描述
编译不了,报了链接错误

我们的静态库和头文件,text.c文件都在同一路径下,怎么会报链接错误呢?

这是因为我们的静态库属于第三方库,gcc是不认识的,我们要显示的去链接我们的库

-l 库名或者-l库名

-l后面有没有空格都可以(l就是link链接)

在这里插入图片描述
gcc还是找不到库,是因为没有指明路径,gcc不知道在哪里找我们的库

-L路径

L也就是link,.代表当前路径

在这里插入图片描述
现在就可以成功使用库了

现在我们再来理解一下-L. -lmath - > -l库名,是告诉编译器去链接的库叫什么,-L路径,是告诉编译器去哪里找库

头文件和库打包再发

我们一般并不是将库和头文件直接发给别人,我们直接将头文件放在一个目录下,库放在另一个目录下,然后将这两个目录打包之后发给别人

在这里插入图片描述
打包发给别人,就可以解压了

在这里插入图片描述
好,我们再编译
在这里插入图片描述

现在出现了找不到头文件错误,这是我们刚刚没有遇到的

-I路径我们加上这个指令

I就是include,这个指令就是告诉编译器头文件的位置

在这里插入图片描述
这下就成功的将打包的库运行起来了

我们解读一下我们的指令-I./include -L./lib -lmath -> -I路径,就是告诉编译器去哪里找头文件,-L路径就是告诉编译器去哪找库,-l库名,就是告诉编译器要用到的库的名字

我们实际在下载好第三方库的时候其实也不是这样的使用,这样使用起来太麻烦了
我们都是将头文件放在系统搜索头文件时默认的搜索路径,将库放在系统搜索库的搜索路径,这样编译器就会能找到头文件和库,就不用我们自己指明头文件和库所在路径,我们使用库就直接带上-l库名就可以使用第三方库了

打包成动态库

打包动态库和打包静态库还是有一些不一样的

首先,在形成.o文件时,我们要加一个选项-fPIC

PIC(position independent code)就是产生与位置无关码

在这里插入图片描述
做成动态库直接用gcc的命令即可gcc -shared -o lib库名.so 库包含的所有.o文件
在这里插入图片描述
然后头文件放入一个目录,库放入另一个目录,在打包
在这里插入图片描述
好,接下来,解压并使用库
在这里插入图片描述

我们照着使用静态库的方法确实是生成了可执行程序,可是却运行不了
在这里插入图片描述
ldd 可执行程序查看了我们的可执行程序,确实没有找到我们的动态库

ldd可以查询可执行程序所依赖的库

在这里插入图片描述

这是因为我们gcc加上一连串选项告诉了编译器头文件在哪,库名是什么,库在哪,这才能顺利通过编译阶段生成可执行程序,可是我们并没有告诉OS库在哪,OS在运行的时候找不到库,所以有程序了但是运行不了

为什么静态库能成功运行,OS可以找到静态库呢?因为你的程序生成了的时候,就已经将有关静态库的二进制代码拷贝到你的程序之中了,而静态库没有

  1. 环境变量
    Linux下有一个环境变量,查找可执行程序的动态库会在这个环境变量的路径下查找,我们只要将我们的库的路径添加进去就可以了,但是这是一种临时的方法,重新登录环境变量会恢复回去

    在这里插入图片描述
    将我们动态库所在的路径加进去了,ldd查询也可以找到,也可以运行
    在这里插入图片描述

  2. 建立软链接
    我们可以在OS默认搜索动态库的路径下建立一个软链接文件,指向我们的库,这样我们的库就可以使用了
    在这里插入图片描述
    (划红线的命令忽略,不要被其影响了)现在我们ldd可以找到库,也可以运行我们的程序了

  3. 配置文件
    ls /etc/ld.so.conf.d/在这里存放着很多配置文件,配置文件里面都是存放着路径
    描述
    在这里插入图片描述
    在这里插入图片描述
    我们直接在这个路径下建立一个.conf配置文件,文件里面放我们的动态库所在的路径,看看可不可以使用我们的动态库了
    在这里插入图片描述
    在这里插入图片描述
    按理说是可以直接使用了,但是其实还差一步

    在这里插入图片描述
    我们需要更新配置文件sudo ldconfig
    在这里插入图片描述
    这下我们的动态库就可以使用了

动静态库的加载

我在之前的博客程序地址空间中讲过,在栈区和堆区之间有一个共享区,这个共享区就是用来加载动态库的代码的

动态库中的方法都是用的是相对地址,就是偏移量 (生成动态库代的选项fPIC[与位置无关码],就是让动态库的地址,都变成偏移量的)

动态库是要给多个进程使用的,一个进程也是要使用多个动态库的,而每个进程所加载的动态库的数量、顺序等不同,所以不可能用绝对地址
不可能给每个动态库划分一个位置,每次调用这个动态库都要在这个位置
因此使用的动态库的方法用的是相对地址,在进程运行的时候,动态库映射到进程的共享区的时候才可以确定起始地址

我们的程序在链接完之后,用到的有关动态库的方法会被替换成相对地址,就是偏移量

等到运行的时候,我们的程序会形成进程,之后将我们的进程中用到的动态库里的方法的代码会加载到内存

当内存中进程用到的动态库通过页表映射到进程的共享区的时候,进程才能真正的确定库的起始地址,再加上链接得到的偏移量,就可以在进程的pcb的共享区找到库中对应的方法的实现

这个起始地址就是动态库的在进程的pcb中的共享区的地址

现在我们这里同时有静态库和动态库,并且我们已经有了配置文件,我们运行看看代码会链接动态库还是静态库
在这里插入图片描述
静态库和动态库同时存在,系统默认选择链接动态库

在这里插入图片描述

只要在编译的时候带上-static选项就可以使用静态库编译
在这里插入图片描述

现在我们不提供动态库,只有静态库,然后编译的时候不带上-static选项,让他动态链接
在这里插入图片描述

成功了,生成了可执行程序,可以运行,并且大小比动态链接的大一点,比静态链接小,查文件属性也可以看到是动态链接,ldd查不到依赖的静态库

因为如果不提供动态库只有静态库,没办法只能静态链接这个静态库,但是因为程序不只依赖这一个库,还依赖了其他的动态库,所有其实只是这个静态库静态链接,其他动态库动态链接

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值