目录
引言
在Linux系统的编程领域,库(Library)扮演着至关重要的角色,它们为开发者提供了可重用的代码片段,从而提高开发效率,减少重复劳动。库分为两种类型:动态库(Dynamic Library)和静态库(Static Library)。这两种库在程序的构建和运行过程中发挥着不同的作用,各有其特点和适用场景。接下来,让我们深入探讨Linux环境下的动态库与静态库,了解它们的定义、区别以及在实际开发中的应用,从而为高效编程奠定坚实的基础。
本文将从:库的制作者、库的使用者与库的加载三个角度去解读库文件
静态库
将我们提供的方法给别人用有如下方式:1.将源文件直接暴露 2.将源代码打包成库 + .h的形式
显然第二种方式是更好的,这样可以保护源文件的安全性,也可以提供更便捷的使用方式
流程:
静态库的制作
写好源文件与头文件。第二步是库的打包
建立一个lib变量
.a依赖.o .o依赖.cpp
cr表示创建与替换(不存在创建,存在就替换)
当然,库写好了需要发布
放在写好的文件中。
当别人想用它的时候,只需要获得lib文件就可以。
使用
由于我们打开的.cpp文件与lib在同一目录下,所以直接lib开头就可以。
但是这种书写方式才是我们更喜欢的书写。
在我们编译进行头文件的展开时,前提是先找到头文件。默认路径:系统默认目录/usr/include
虽然我们包含了头文件,但是发现编译失败,1.找不到头文件(这个math.h头文件能找到的原因是:优先去本地查找,本地找不到用官方库) 2.找不到对应的函数
解决:
1.-I指令找到头文件(I就是iclude)
2.-L指令找到库的目录(L就是lib)
这是链接时报错
3. -l点名哪个库(l就是link)
点名库时,需要去掉lib开头,.a结尾,紧连在-l指令后
这样就得到了我们需要的结果。
意料之外的结果
myerrno并没有得到想要的结果,这是因为形参的实例化是从右到左进行的。
所以尽量不要在打印语句中进行结果的计算。
总结1
1.g++编译的时候,查找库和头文件只会去默认的路径中查找
在Ubuntu系统中,C++的库文件(也称为共享库或动态链接库)通常位于以下目录之一
/usr/lib/x86_64-linux-gnu/
(对于64位系统)
使用第三方库,必须得
-I包含头文件路径
-L包含库文件路径(不含库文件本身)
-l包含库文件(并去掉多余的格式)
2.上述的编译默认是进行动态链接,但是由于只存在静态库,所以只能进行静态链接
更简单的使用第三方静态库
1.可以将静态库的头文件与库文件安装(只需要安装头、库文件即可)
库文件:/usr/lib/一般在这个里面
头文件:/usr/include/一般的话头文件是在这个地方
对于C++的第三方库一般来说,需要.a文件安装到/usr/lib,把.h安装到/usr/include
这其实就是安装
就算是我们安装好第三方库之后,虽然可以省略-I -L指令,但是只要是第三方库,就得用-l点名需要链接哪个库
2.建立软链接
软链接就相当于是快捷方式,将头文件的目录(不包含头文件)安装到本地
这样使用快捷方式 + 头文件就可以完成查找
其实系统在查找的时候,如果不指明 -I选项,那么会默认进行 默认路径(如:/usr/include) + 头文件的方式进行查找
这样会自动替换为/usr/include/myinc/math.h,其中/usr/include/myinc/会被替换为原来的地址
库文件建立软链接,库文件建立软链接的时候,需要将具体到某个库进行链接。
这样我们-lmymath的时候,才能去默认路径/lib64查找libmymath.a
但是不推荐软链接,不如直接进行安装
动态库(只需编译器指令)
静态库的后缀是.a,动态库的后缀是.so(shared o)
为了得到动态库,同样是先得到.o,然后得到.so
-fPIC -c指令得到.o
当然,我们可以-o指定名称,否则就是默认的名称 + .o
-shared -o 得到.so
-o后面跟上库的名称
将各个.o文件打包为.so文件
动静态库的联合发布
为了一次性发布,可以建立伪目标all,依赖两个库。
编译
使用动态库,仍然需要-I -L -l指令
在我们举出的例子中
g++编译时,如果2个头文件不在相同的目录下,需要写2个-I……
执行
当执行的时候,发现找不到共享库,libDY.so发现找不到。
原因:编译阶段,告诉了编译器库在哪里。但是动态库在执行阶段也需要知道在哪里(加载器)
方法一:库安装到路径
这样就可以直接找到动态库的位置(不需要重新编译,因为编译器的工作已经完成,编译器与加载器是分开的)
这样就可以直接执行了。
并且安装到路径也是最常用的做法。
方法二:软链接
同样的,让默认路径下/lib64/
不需要重新编译(编译一次就可以)
方法三:环境变量
load library path
这个是默认没有安装的,这个环境变量是专门给用户提供用户自定义的库路径的(加载器使用)
环境变量是shell启动时配置的,我们也可以通过编写shell脚本,将环境变量添加到这里面,这样就不是内存级的了。
环境变量是通过这个文件导入的
方法四:修改配置文件
这里面存放着一些配置文件
这些配置文件内部就是一些路径,加载器加载程序时,如果需要动态库,那么就去会这些路径内查找。
但是需要注意的是ubuntu禁止root直接登录,而只有root才有权限去管理(root不受权限的限制)。
用root登录之后,只需要
1.新建一个.conf文件
2.把动态库的路径放进去
3.重新加载一下配置(config)
这样就可以看到动态库的路径了(永久有效)
总结2
动静态库在链接阶段怎么使用的
在软件开发中,链接器是编译过程的一个阶段,它将一个或多个编译后的目标文件(.o或.obj文件)与所需的库文件相连接,生成可执行文件。动静态库在链接阶段的使用方式有所不同:
静态库(.a或.lib文件)
静态库在链接阶段的使用通常遵循以下步骤:
识别:链接器需要知道要链接哪些静态库。这通常通过编译器命令行参数(如gcc的-l和-L选项)指定。
解析符号:当链接器处理目标文件时,它会寻找未定义的符号(函数和全局变量)。链接器会检查静态库中是否有这些符号的定义。
包含:如果静态库中包含了所需的符号定义,链接器会将这些符号的实现从库中复制到最终的可执行文件中。这意味着每个使用静态库的可执行文件都有一份库代码的副本。
重定位:链接器会对复制到可执行文件中的代码进行重定位,确保地址引用正确。
生成:最后,链接器生成完整的、独立运行的可执行文件。
动态库(.so或.dll文件)
动态库在链接阶段的使用通常遵循以下步骤:
识别:和静态库一样,链接器需要知道要链接哪些动态库,这通常也是通过编译器命令行参数指定的。
解析符号:链接器检查目标文件中的未定义符号,并查找动态库中的符号定义。
记录引用:与静态库不同,链接器不会将动态库中的代码复制到可执行文件中。相反,它会记录下这些符号在哪个动态库中定义,并在生成的可执行文件的动态链接表中创建相应的条目。
延迟绑定:动态链接的符号实际上是在程序运行时(而不是链接时)解析的。这个过程称为动态加载,由操作系统的动态链接器(如Linux的ld-linux.so或Windows的ntdll.dll)在程序启动时进行。
生成:链接器生成可执行文件,这个文件在运行时需要动态库的支持。
总结3
静态库:在链接阶段,库的代码被直接包含进最终的可执行文件中。
动态库:在链接阶段,只记录对动态库的引用,实际链接发生在程序运行时。
使用哪种库取决于多种因素,包括程序的需求、部署环境、以及是否希望减小可执行文件的大小等。静态库会增加可执行文件的大小,但不需要担心运行时库的版本问题;动态库可以减少可执行文件的大小,并允许库的共享和更新,但可能带来运行时依赖的问题。