动静态库
-
静态库(Static Library):
- 静态库是编译链接时的一种库文件形式,也称为静态链接库或静态目标文件。它是将一组目标文件(编译后的二进制文件)打包成一个单一的文件。静态库的文件扩展名通常为
.a
(在Windows上为.lib
)。 - 静态库的特点是在编译时被链接到应用程序中,成为应用程序的一部分。链接过程将库的代码和数据复制到最终生成的可执行文件中。
- 静态库的优点是使用简单,可以在不依赖外部库的情况下,将所有必需的代码和数据打包到应用程序中,使得应用程序更加独立和可移植。但是,静态库会增加可执行文件的大小,并且如果多个应用程序使用相同的静态库,会导致代码的冗余。
- 在使用静态库时,需要在编译和链接命令中显式指定静态库文件。
- 静态库是编译链接时的一种库文件形式,也称为静态链接库或静态目标文件。它是将一组目标文件(编译后的二进制文件)打包成一个单一的文件。静态库的文件扩展名通常为
-
动态库(Dynamic Library):
- 动态库是在运行时加载的库文件,也称为共享库或动态链接库(DLL)。它是一个独立的二进制文件,可以被多个应用程序共享使用。动态库的文件扩展名通常为
.so
(在Windows上为.dll
)。 - 动态库的特点是在应用程序运行时被动态加载到内存中。多个应用程序可以共享同一个动态库的实例,减少了内存占用,并且可以实现库的升级和更新,而无需重新编译和链接应用程序。
- 动态库的优点是节省内存,减少可执行文件的大小,方便库的更新和维护。但是,使用动态库需要确保库文件在运行时可访问,并且需要依赖正确版本的动态库。
- 在使用动态库时,需要在编译时指定库的头文件路径,在链接时指定动态库文件,运行时需要确保动态库文件位于正确的路径中。
- 动态库是在运行时加载的库文件,也称为共享库或动态链接库(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可以找到静态库呢?因为你的程序生成了的时候,就已经将有关静态库的二进制代码拷贝到你的程序之中了,而静态库没有
-
环境变量
Linux下有一个环境变量,查找可执行程序的动态库会在这个环境变量的路径下查找,我们只要将我们的库的路径添加进去就可以了,但是这是一种临时的方法,重新登录环境变量会恢复回去
将我们动态库所在的路径加进去了,ldd查询也可以找到,也可以运行
-
建立软链接
我们可以在OS默认搜索动态库的路径下建立一个软链接文件,指向我们的库,这样我们的库就可以使用了
(划红线的命令忽略,不要被其影响了)现在我们ldd可以找到库,也可以运行我们的程序了 -
配置文件
ls /etc/ld.so.conf.d/
在这里存放着很多配置文件,配置文件里面都是存放着路径
我们直接在这个路径下建立一个.conf配置文件,文件里面放我们的动态库所在的路径,看看可不可以使用我们的动态库了
按理说是可以直接使用了,但是其实还差一步
我们需要更新配置文件sudo ldconfig
这下我们的动态库就可以使用了
动静态库的加载
我在之前的博客程序地址空间中讲过,在栈区和堆区之间有一个共享区,这个共享区就是用来加载动态库的代码的
动态库中的方法都是用的是相对地址,就是偏移量 (生成动态库代的选项fPIC[与位置无关码],就是让动态库的地址,都变成偏移量的)
动态库是要给多个进程使用的,一个进程也是要使用多个动态库的,而每个进程所加载的动态库的数量、顺序等不同,所以不可能用绝对地址
不可能给每个动态库划分一个位置,每次调用这个动态库都要在这个位置
因此使用的动态库的方法用的是相对地址,在进程运行的时候,动态库映射到进程的共享区的时候才可以确定起始地址
我们的程序在链接完之后,用到的有关动态库的方法会被替换成相对地址,就是偏移量
等到运行的时候,我们的程序会形成进程,之后将我们的进程中用到的动态库里的方法的代码会加载到内存
当内存中进程用到的动态库通过页表映射到进程的共享区的时候,进程才能真正的确定库的起始地址,再加上链接得到的偏移量,就可以在进程的pcb的共享区找到库中对应的方法的实现
这个起始地址就是动态库的在进程的pcb中的共享区的地址
现在我们这里同时有静态库和动态库,并且我们已经有了配置文件,我们运行看看代码会链接动态库还是静态库
静态库和动态库同时存在,系统默认选择链接动态库
只要在编译的时候带上-static
选项就可以使用静态库编译
现在我们不提供动态库,只有静态库,然后编译的时候不带上-static
选项,让他动态链接
成功了,生成了可执行程序,可以运行,并且大小比动态链接的大一点,比静态链接小,查文件属性也可以看到是动态链接,ldd查不到依赖的静态库
因为如果不提供动态库只有静态库,没办法只能静态链接这个静态库,但是因为程序不只依赖这一个库,还依赖了其他的动态库,所有其实只是这个静态库静态链接,其他动态库动态链接