目录
制作静态库
ar命令
使用makefile制作静态库
如何发布自己的静态库给别人呢?
如何使用静态库呢?
方法一
方法二
制作动态库
使用动态库遇到的问题
上面的问题如何解决呢?(如何使用动态库)
1.方法一
2.方法二
3.方法三
4.方法四
搜索优先级
Linux默认头文件的搜索顺序
Linux默认库文件的搜索顺序
链接动态库和静态库的顺序
为什么要有库?
制作静态库
ar命令
问题:如果得到别人的.o文件,我们是可以使用这个文件里的方法的,但如果.o文件过多,比如有1000个,那Linux中使用gcc命令链接这些二进制文件的时候岂不是要将1000个文件的名称都写出来?更何况文件多了之后还容易漏掉个别文件,所以这种方法不可取,那该怎么办呢?
答案:使用ar命令将这些.o文件打包(正式的说法叫归档),这个归档的过程就叫制作静态库。将所有 .o文件(即目标二进制文件)归档即可制作一个静态库出来。可以得到一个结论,静态库中全是.o文件,没有.h文件。
注意事项:
1.归档后的静态库的名称前3个字符必须是lib,文件后缀名必须是.a,其他的自定义即可,如下图的libhello.a。
2. ar -rc,这里的r表示replace(替换),c表示create(创建)。
使用makefile制作静态库
如何发布自己的静态库给别人呢?
创建一个发布目录文件,里面包含目录文件include和目录文件lib。include文件里包含了库的所有头文件,lib文件中包含了所有的静态库文件,如下图中的 libhello.a文件,并且因为 lib目录中包含了静态库文件,那么一堆.o文件也就不需要了。最后将这个发布目录文件交给别人,这就是发布的过程。注意只要是库,头文件也必须给别人,不然谁知道可以使用哪些方法,所以发布目录文件里存在include目录。
如下图就是模拟发布的过程(创建发布目录文件的过程)
代码如下
运行结果如下
如何使用静态库呢?
方法一
1.首先将include目录的所有文件拷贝到系统寻找头文件的默认路径中,如下图。
2.然后将静态库文件拷贝到系统寻找静态库文件的默认路径中,如下图(注意系统寻找静态库文件的默认路径就是/lib64,而不是/usr/lib64。/是根目录,lib64和usr一样,都是根目录中的一个目录文件)。
遇到的问题:经过前面的步骤后,使用gcc编译下面代码失败,为什么呢?
代码如下
运行结果如下
答案如下:
- 如果libhello.a是系统库,为了保证程序能在系统下正常运行,则gcc会自动链接libhello.a库,那么此时自然可以通过编译并运行;如果是C语言提供的静态库,由于gcc是C语言编写的,所以编译时会自动帮我们链接C语言提供的静态库,所以上图代码自然也可以通过编译并运行。但问题在于libhello.a这个静态库既不是系统提供的,也不是语言提供的,是我们自己编写并新增到OS中的,属于第三方库,所以不满足条件,所以无法通过编译并运行。
- 然后要知道gcc在进行链接时,并不会将所有可见的静态库都进行链接,否则会造成代码冗余。现在的情况是:虽然经过我们手动添加,libhello.a已经在系统寻找静态库的默认路径中,但libhello.a毕竟不是系统库,也不是C语言库,即不是一个必须存在的库,所以如果没有指定gcc链接libhello.a静态库,则gcc为了避免代码冗余的可能性存在,是不会将该库进行链接的,所以说到这里结果已经显而易见,编译失败是因为没有告诉gcc需要链接哪个静态库,所以都没有链接到libhello.a,此时使用静态库libhello.a里的方法自然是不行的。那么如何链接libhello.a呢?请往下看。
3.然后链接想要使用的静态库。如下图,gcc后面加上 -l 选项和静态库的名称。注意这里文件的名称需要去掉前缀 lib 和后缀 .a才行,如下图将静态库libhello.a拆解成了hello。
4.最后成功地使用了静态库libhello.a编译出可执行文件a.out。(由于编译时没有带生成文件的名称,所以生成文件的名称为a.out)
总结一下:在本种方法中是靠将库文件和头文件的路径加载进系统的默认搜索路径中实现了让OS和gcc找到该库文件和头文件;是靠在gcc命令中加了-l(小写的L)选项(如上图2中的gcc main.c -lhello)实现了让gcc知道该链接哪个库文件(因为库文件的路径被加载进系统默认的搜索路径中,所以gcc能找到库文件的位置,所以能链接成功)
上面的操作将我们自己写的库拷贝到了系统搜索库的默认路径中,这就叫做库的安装。最好不要将自己写的库拷贝进OS寻找库的默认路径中,会造成系统的污染,所以想要使用静态库还有更好的方法。
方法二
直接在编译时带上头文件所在的路径和静态库所在的路径即可,如下图。 注意 -I(为大写的i,不是小写的L)表示include,-L表示lib(library)。
注意 -I
和 -L
选项分别用于指定头文件的搜索路径和库文件的搜索路径,但它们并不直接告诉编译器在链接时要链接哪个库,这些选项只是告诉编译器在指定路径中查找头文件和库文件,指定路径中的库文件可能有多个,真正告诉编译器在链接时要链接哪个库的是 -l 选项(为小写的 L,而不是大写的 i ),如下图的 -lhello。
错误示例:
1.下图红框处没有带具体的静态库的名称,所以编译出错。因为./hello/lib路径下可能有很多个静态库文件,如不加入具体的名称,会不知道链接哪一个。
2.下图中只有头文件的路径,没有静态库的路径和名称。
3.gcc在编译时,根据上文的搜索文件的顺序,编译器搜索头文件和静态库所在的路径的顺序先是当前路径,但此时当前路径中不存在main.c里包的头文件,也不存在包含main.c里使用的方法的静态库,所以编译失败。
总结一下:在本种方法中是靠【将头文件和库文件的路径通过-I(大写的i)和-L选项告诉了gcc】实现了让gcc找到该库文件和头文件;是靠在gcc命令中加了-l(小写的L)选项实现了让gcc知道该链接哪个库文件(因为库文件的路径在使用gcc命令时通过-L选项告诉了gcc,所以gcc能找到库文件的位置,所以能链接成功)。
制作动态库
1.首先生成若干个需要的 .o 文件,注意有选项-fPIC,如下图。
2.然后生成动态库即可,注意有选项-shared,如果没有这个选项,编译器则会认为你是要生成可执行文件,而不是动态库,然后生成失败,因为我们写的动态库文件中没有main函数,生成不了可执行文件。
如下图是使用makefile同时编译动态库和静态库
使用动态库遇到的问题
问题:首先创建一个包含动态库的发布目录文件,如上图的output文件。由于gcc默认采用动态链接,所以当动态库和静态库同名并同时存在时,gcc会链接动态库。gcc编译成功后生成了下图的可执行文件a.out(也就是编译成功了),但当运行a.out的时候会发生报错(也就是运行失败了),错误信息是找不到该动态库,所以无法将动态库加载进内存,为什么会这样呢?
答案:动态库libhello.so是一个独立的文件,和可执行文件a.out是通过分批加载的方式加载进内存,所以运行失败的原因就是因为此时OS不知道动态库libhello.so在哪,导致OS无法将该动态库加载进内存。这又是为什么呢?或者换句话说,我不是已经通过在gcc命令处加上了-L选项告诉了OS动态库文件的路径吗,为什么OS会不知道动态库libhello.so在哪呢?说一下,在gcc命令中添加-I
和 -L
选项只会告诉编译器在哪里查找头文件和库文件,这些选项只在编译阶段起作用,而不会在运行时起作用,这两个选项不会自动将库文件路径添加到系统的动态链接库的默认搜索路径中,所以OS就不知道动态库libhello.so在哪,所以OS就没法将动态库加载进内存。在编译阶段,编译器使用 -I
和 -L
选项来查找头文件和库文件的位置,以便能够正确编译你的代码。但在运行时,当你的程序试图加载动态库时,操作系统需要知道动态库文件的位置,否则就无法将其加载进内存。
问题:为什么运行链接静态库的可执行程序时不会出现上述的问题呢?
答案:因为静态库的代码是以拷贝的形式填充进了可执行程序,所以不会分批加载,所以不会产生上述问题。
上面的问题如何解决呢?(如何使用动态库)
1.方法一
既然上面问题出现的原因是因为OS不知道动态库文件的所在路径,找不到动态库文件,那么解决的方法也就可以和使用静态库的方法一相同,直接将动态库文件和头文件添加到系统搜索库文件和头文件的默认路径中,之后运行可执行文件a.out时,需要的动态库文件自然就可以被系统找到并加载进内存中了。但依然不建议使用此方法,容易污染系统的生态。
总结一下:因为在上面说过,gcc中的-I(大写的i)和-L选项只能让gcc知道头文件和动态库文件在哪,无法让OS知道头文件和动态库文件在哪,所以在本种方法中是靠将动态库文件和头文件的路径加载进系统的默认搜索路径中实现了让OS能够找到该库文件和头文件,以此在运行通过这些动态库实现的可执行程序时让OS能够将动态库分批加载进内存;是靠在gcc命令中加了-I(大写的i)和-L选项让gcc知道头文件和动态库文件在哪;是靠在gcc命令中加了-l(是小写的L)选项实现了让gcc知道该链接哪个库文件(因为库文件的路径在使用gcc命令时通过-L选项告诉了gcc,所以gcc能找到库文件的位置,所以能链接成功)
然后说一下,其实这里方法1中有一步是多余的:如果直接将动态库文件和头文件添加到系统搜索库文件和头文件的默认路径中,那么此时gcc和OS就都能找到该动态库文件和头文件,所以使用gcc指令进行链接时就不需要再加上-I(大写的i)和-L选项让gcc知道该动态库文件和头文件在哪了,因为gcc直接在系统搜索库文件和头文件的默认路径中就能找到它们。但注意,虽然不需要加上-I(大写的i)和-L选项让gcc知道该动态库文件和头文件在哪,但一定是需要加上-l(小写的L)选项告诉gcc需要进行链接的动态库文件是哪一个的。举个例子,假如有个动态库文件叫libhello.so,那么使用该动态库时,只要将该动态库文件和头文件添加到系统搜索库文件和头文件的默认路径中后,在使用gcc编译时,直接gcc xxx.c -o xxx -lhello就可以将包含libhello.so动态库文件的可执行程序xxx给生成出来并且能成功使用了。
2.方法二
修改环境变量LD_LIBRARY_PATH,LD表示load(加载),LIBRARY表示lib(库)。
如上图,在冒号( : )的后面加入库所在的路径即可,路径最后不需要带库本身的名称。注意不要将之前的环境变量覆盖掉了,所以上图中等于号(=)的后面是 $LD_LIBRARY_PATH:,而不是直接的一条路径。
此种方式有一个缺点:环境变量是内存级的变量,所以每次登录时,环境变量都会从某个配置文件中读取数据,所以退出登录后再次登录,之前用户在shell中设置的环境变量就失效了。
总结一下:因为在上面说过,gcc中的-I(大写的i)和-L选项只能让gcc知道头文件和动态库文件在哪,无法让OS知道头文件和动态库文件在哪,所以在本种方法中是靠将动态库文件的路径加载进环境变量LD_LIBRARY_PATH中实现了让OS能够找到该动态库文件,以此在运行通过这些动态库实现的可执行程序时让OS能够将动态库分批加载进内存;是靠在gcc命令中加了-I(大写的i)和-L选项让gcc知道头文件和动态库文件在哪;是靠在gcc命令中加了-l(是小写的L)选项实现了让gcc知道该链接哪个库文件(因为库文件的路径在使用gcc命令时通过-L选项告诉了gcc,所以gcc能找到库文件的位置,所以能链接成功)
3.方法三
改进方法二的弊端,本种方法就是修改配置文件。如果觉得自己写的库很重要,但又不想加载到系统搜索库文件的默认路径中,防止污染系统的生态,就可以使用本种方法。
首先在上图的路径中创建一个普通文件,文件名无所谓,但后缀名必须是.conf,如下图。
然后用vim编辑器,将动态库的路径写进105.conf,注意路径中不需要动态库本身的名称。(前面不一定非要使用vim编辑器,只要你能把路径写进配置文件就行)
配置文件编写完毕后,最后在shell命令行中使用ldconfig指令,让配置文件105.conf生效即可,如下图。注意修改配置文件一般需要较高权限,所以加sudo。
上面操作完成后,此时就算环境变量LD_LIBRARY_PATH中不存在动态库所在的路径,也可以正常运行可执行程序。
容易陷入的误区:
有时候发现删除了配置文件105.conf,但需要105.conf文件里写的路径上的动态库的可执行程序依然可以正常运行,这是因为有缓存的存在,此时再次sudo ldconfig将缓存更新,需要对应动态库的可执行程序就运行不了了。
总结一下:因为在上面说过,gcc中的-I(大写的i)和-L选项只能让gcc知道头文件和动态库文件在哪,无法让OS知道头文件和动态库文件在哪,所以在本种方法中是靠将动态库文件的路径加载进配置文件中实现了让OS能够找到该动态库文件,以此在运行通过这些动态库实现的可执行程序时让OS能够将动态库分批加载进内存;是靠在gcc命令中加了-I(大写的i)和-L选项让gcc知道头文件和动态库文件在哪;是靠在gcc命令中加了-l(是小写的L)选项实现了让gcc知道该链接哪个库文件(因为库文件的路径在使用gcc命令时通过-L选项告诉了gcc,所以gcc能找到库文件的位置,所以能链接成功)
4.方法四
软连接方案,感兴趣自行探索。
搜索优先级
Linux默认头文件的搜索顺序
(如果阅读完上文,这里是很容易理解的)
在Linux系统中,头文件的搜索顺序通常遵循以下规则:
-
当前目录(
.
):首先,编译器会搜索当前工作目录中的头文件。这通常是你正在编写代码的目录。 -
系统默认的标准头文件目录:编译器会搜索系统默认的标准头文件目录,这些目录通常包含C标准库和C++标准库的头文件。具体的目录可能会因Linux发行版和编译器而异,但通常就是
/usr/include
或者/usr/local/include
。 -
使用
-I
选项指定的目录:如果你在编译时使用-I
选项明确指定了头文件的搜索路径,编译器将搜索这些指定的目录。 -
环境变量:编译器还会考虑环境变量
C_INCLUDE_PATH
和CPLUS_INCLUDE_PATH
中指定的目录。这些环境变量可以用来指定额外的头文件搜索路径。 -
编译器内置路径:编译器通常有一些内置的默认搜索路径,这些路径是编译器的默认设置,通常包括一些常见的头文件和库文件位置。
注意,上述搜索顺序是通常情况下的规则,具体实现可能会因不同的编译器和Linux发行版而有所不同。你可以使用 -v
选项来查看编译器的详细搜索路径。例如,使用 gcc -v
来查看GCC编译器的搜索路径。
总之,在编写代码时,确保你的头文件能够被编译器找到,可以使用 -I
选项指定自定义的头文件搜索路径,或者将头文件放在系统默认的标准头文件目录中。
Linux默认库文件的搜索顺序
(如果阅读完上文,这里是很容易理解的)
在Linux系统中,库文件的搜索顺序通常遵循一定的规则,以确保动态链接器能够找到所需的库文件。默认的库文件搜索顺序通常如下:
-
编译目标代码时指定的动态库搜索路径:如果在gcc或者g++编译代码时使用了
-L
选项来指定库文件的搜索路径,编译器会优先搜索这些指定的路径。 -
环境变量
LD_LIBRARY_PATH
指定的动态库搜索路径:你可以使用LD_LIBRARY_PATH
环境变量来指定额外的动态库搜索路径。这个环境变量允许你在运行时指定库文件的搜索路径,但它通常被认为是一个不太安全的方式,不推荐在生产环境中使用。 -
配置文件
/etc/ld.so.conf
中指定的动态库搜索路径:在这个文件中,系统管理员可以为系统指定默认的动态库搜索路径。如果你的库文件位于这些路径下,动态链接器会自动找到它们。 -
默认的系统库路径:如果以上步骤都没有找到所需的库文件,动态链接器会搜索默认的系统库路径,即
/lib64
(注意/是根目录)。
这些是Linux系统中库文件的默认搜索顺序。你可以通过它们来更改库文件的搜索路径,以满足你的特定需求。在编译时,一般可以使用 -L
选项来指定自定义的库文件搜索路径。
链接动态库和静态库的顺序
情景1如下
如上图,当动态库和静态库同时存在并且同名时,gcc编译时默认使用动态库。
情景2如下
如上图,当动态库和静态库同时存在并且同名时,如果非要使用静态库进行链接,那么必须加-static选项,-static表示摒弃编译器默认优先使用动态库的原则,并直接使用静态库。
情景3如下
如上图,即使编译器不带-static选项进行编译时默认链接动态库,但当只有静态库时,也只能以静态链接的方式将静态库拷贝进程序中,程序需要的其它库文件如果是动态库,则以动态方式链接。此时发现ldd命令列出的动态库依赖关系中并没有hello这个静态库文件,事实上不必担心,a.out这个文件一定使用了hello这个静态库的,因为a.out可以成功运行。文章来源:https://www.toymoban.com/news/detail-769770.html
为什么要有库?
因为简单便利和安全。安全是指如果想把方法给别人使用,但不想泄漏代码,就可以通过只把库给别人。文章来源地址https://www.toymoban.com/news/detail-769770.html
到了这里,关于制作静态库和动态库的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!