学过C语言我们知道,C语言有标准库和自定义库,这些方便了我们的实际开发,提供了已经实现好的函数接口,我们使用的时候,只需要引入头文件即可,那具体的实现过程又是怎么样的呢?我们又该如何实现我们自己的库呢?这就是我们本节需要讲解的。
目录
一、什么是库文件
二、为什么需要库
三、静态库的生成与使用
3.1 自定义静态库的生成
3.2 自定义静态库的使用
3.3 将自定义的静态库移动到系统库目录下使用
3.4 静态库删除后是否可以使用
四、共享库的生成与使用
4.1 自定义共享库的生成
4.2 自定义共享库的使用
4.2.1 解决办法1
4.2.2 解决办法2
4.3 共享库删除后是否可以使用
五、静态库和共享库的区别
一、什么是库文件
库是一组预先编译好的方法/函数的集合。 在 64 位的系统上有些库也可能被存储在/usr/lib64 下。 库有两种,一种是静态库,其命令规则为 libxxx.a,一种是共享库,其命令规则为 libxxx.so。如下图所示:
- Linux系统存储库的头文件(函数的声明)一般会被存储在 /usr/include 下
- Linux系统存储的库(函数的具体实现)的位置一般在:/lib 和 /usr/lib
- Linux系统存储的二进制可执行程序的位置在:/bin和/usr/bin
二、为什么需要库
因为在引用外部方法时,不需要改动直接用就行,但给的是.c文件的话,使用要编译链接,很麻烦,所以直接生成.o文件,放到库里,使用的时候直接链接就好。库的隐蔽性较好,要让对方使用方法,但又不想让对象知道方法是如何让实现的,就可以用库。当同时有静态库和共享库时,优先使用共享库。
三、静态库的生成与使用
这里展示实现自定义的静态库,以及如何使用,需要个3文件:add.c max.c foo.h,其中“foo.h”中是函数的声明,“add.c”和“max.c” 是函数的定义/具体实现
创建一个头文件,包含所有自定义函数的声明,后面再使用直接引|入自己创建的头文件即可!
3.1 自定义静态库的生成
第一步:先将需要生成库文件的所有“.c“文件编译成“.o”文件
第二步:使用 ar 命令将第一步编译的所有”.o”文件生成静态库,其中:c 是创建库 r 是将方法添加到库中 v 显示过程。
至此,静态库已经生成啦,是不是非常简单!注意:此时生成的静态库在当前目录下,并不是系统库文件位置,我们在下面使用的时候,要用双引号引入头文件即:#include '' foo. h'' 。
3.2 自定义静态库的使用
上一小节,我们讲解了如何生成静态库,那么,我们又该如何使用呢?
静态库的使用也很简单,只需要在主函数文件里面引入我们自定义的静态库头文件,然后编译链接的时候,指定链接的静态库即可!
第一步:需要使用静态库的地方引入静态库对应的头文件
第二步:编译链接的时候指定链接库的位置
3.3 将自定义的静态库移动到系统库目录下使用
当我们测试完自己的库没有问题后,这时候便可以将自定义的静态库移动到Linux系统存储的库的位置:/lib 和 /usr/lib,移动完之后,它就成了标准库的一部分,后续我们的使用非常简单,在头文件的引入使用尖括号也可以了<>,此外,编译链接的时候,也不需要指定静态库的位置了,因为它首先回会到标准库位置下找,此时就可以找到。
3.4 静态库删除后是否可以使用
四、共享库的生成与使用
这里展示实现自定义的共享库,以及如何使用,需要个3文件:add.c max.c foo.h,其中“foo.h”中是函数的声明,“add.c”和“max.c” 是函数的定义/具体实现
创建一个头文件,包含所有自定义函数的声明,后面再使用直接引|入自己创建的头文件即可!
4.1 自定义共享库的生成
第一步:先将需要生成库文件的所有“.c“文件编译成“.o”文件
第二步:使用 gcc 命令将第一步编译的所有”.o”文件生成共享库
在共享库的创建过程中, -fPIC是一个编译选项,用于生成位置无关的代码。它告诉编译器生成与位置无关的代码,这对于共享库特别重要。共享库(也称为动态链接库)是一种在多个程序之间共享的库。当程序加载时,操作系统会将共享库映射到进程的地址空间中,以便程序可以调用库中的函数和访问库中的变量。位置无关的代码是一种在不同内存位置上执行时都能正常工作的代码。这对于共享库尤其重要,因为它们可能会在不同的内存地址空间中加载。-fPIC 选项使得编译器生成的代码能够在内存中的任何位置执行,而不受具体加载地址的限制。它通常用于创建共享库,以确保库能够在不同的进程中重定位到不同的内存地址,而不会发生冲突或错误。
至此,共享库已经生成啦,是不是非常简单!注意:此时生成的共享库在当前目录下,并不是系统库文件位置,我们在下面使用的时候,要用双引号引入头文件即:#include '' foo. h'' 。
4.2 自定义共享库的使用
上一小节,我们讲解了如何生成共享库,那么,我们又该如何使用呢?
共享库该如何使用呢?我们先学习下静态库的使用,然后观察是否可以使用,在主函数文件里面引入我们自定义的共享库头文件,然后编译链接的时候,指定链接的共享库的位置和库名。
第一步:需要使用静态库的地方引入静态库对应的头文件
第二步:编译链接的时候指定链接库的位置
以下是使用共享库“libfoo.so”和“main.c”生成可执行文件的过程,其中 -L 指定库的 存储路径, -l 指定库的名称(不需要前面的‘lib’和扩展名‘.so’), 如果在库的存储路径有同名的共享库和静态库,gcc 默认使用共享库。
与静态库的使用对比,为什么这里会出现问题呢?又该如何解决呢?
这是因为:静态库和共享库在加载库时的行为是不同的
1.静态库:静态库在链接时会被整体地复制到可执行文件中,因此可执行文件本身包含了静态库的代码和数据。当程序运行时,静态库的代码和数据已经被包含在可执行文件中,因此程序会直接调用其中的函数或访问其中的变量,而不需要额外加载库文件。因此,静态库在运行时不会去查找库文件,而是直接使用可执行文件中已经包含的库代码和数据。2.共享库:共享库在程序运行时才会被加载到内存中。程序在启动时会查找共享库,并将其加载到内存中的地址空间。通常,操作系统会预定义一些标准位置用于查找共享库,比去存储库的标准位置(/lib 或/usr/lib 等)加载。程序在运行时会根据操作系统的规则去这些标准位置下查找共享库。如果共享库不存在于这些位置,那么程序可能会因为找不到库而报错。
总的来说,静态库在链接时就已经被包含在可执行文件中,程序运行时不需要额外查找库文件;而共享库在运行时才会被加载到内存中,程序会按照操作系统的规则去标准位置下查找共享库。
4.2.1 解决办法1
既然,共享库会去标准位置下找对应的库,那我们就可以将共享库移动到标准库位置下,这样就不会报错了。
4.2.2 解决办法2
如果库不在标准位置下,我们放在当前位置下,也可以通过设置环境变量”LD_LIBRARY_PATH”来指定加载库的路径,这样也就不会报错了。
ldd命令可以查看当前可执行程序依赖哪些库
4.3 共享库删除后是否可以使用
共享库只有在运行的时候,才会去标准库位置下去找对应的库,不会进行拷贝,因此删除后,可执行程序无法运行,共享库只有一份,静态库会被拷贝到可执行程序里面,因此删除后还可以使用。
五、静态库和共享库的区别
静态库在链接时将用到的方法包含到最终生成的可执行程序中,成为可执行程序的一部分,而共享库不包含,也就是说可执行程序里面是没有实现的方法的,只做标记,在运行程序时,才动态加载。
静态库:
用静态库编译出来的程序,相当于是把静态库里的.o文件拷贝了一份到可执行程序里,因此即使将库里的文件删除,可执行程序依然能执行。如果库里的数据需要更新,但原来库里的.o文件已经成为了可执行程序的一部分,此时要用更新的方法,就要把可执行程序删除,重新编译链接一个新文件。因此,静态库删除后,可执行程序仍然可以正常使用
共享库:
用共享库编译出来的程序,相当于是标记了所需方法,但可执行程序里没有所需方法的实现,因此当所需方法在库里删除后,可执行程序找不到所需方法,文件就运行不了。
共享库里的数据更新,可执行程序,在运行时直接寻找共享库,就直接能用更新后的.o文件。因此,共享库删除后,可执行程序无法正常使用!
以上就是全部内容!请务必掌握,这是后续学习的基础,欢迎大家点赞加关注评论,您的支持是我前进最大的动力!下期再见!