1. 什么是库
库是写好的现有的,成熟的,可以复⽤的代码。现实中每个程序都要依赖很多基础的底层库,不可能 每个⼈的代码都从零开始,因此库的存在意义⾮同寻常。 本质上来说库是⼀种可执⾏代码的⼆进制形式,可以被操作系统载⼊内存执⾏。库有两种:
• 静态库 .a[Linux]、.lib[windows]
• 动态库 .so[Linux]、.dll[windows]
2. 静态库
在当前目录下,有以上两个我们写的.h文件和对应的.c文件。在test文件当中包含了我们使用的头文件,如果我们希望test文件编译链接形成可执行文件,直接gcc的话则会报错!
我们先把所有的.c文件编译形成.o文件,然后再链接所有的.o文件就可以形成可执行文件了。
所以我们可以得出结论结论:所有的库【动态库、静态库】其本质都是源文件对应的.o文件!
不过,我们写的库终究还是要给别人使用,如果我们每次都是把许多.o文件给别人,让别人链接这么多的.o文件,那这也太麻烦了,所以在Linux中,我们有一个转门针对这个问题的打包命令ar【archive 、r 是 gnu 归档⼯具, rc 表⽰ (replace and create)】 静态库的本质就是.o打了一个包。
假设在张三目录下,我们需要用到我们设计的库,那我们可以我们的库打包拷贝给他,并同时把我们的头文件拷贝给他,这样张三就可以更方便的使用了。
需要解释一下指令gcc -o test test.c -L . -l myc中的选项,-L表示在哪个路径下找库【编译器默认在路径/usr/lib64查找库】,-l表示我们要找的库的名字。对于库名libmyc.a来说前缀lib和后缀.a是固定的,myc表示真实有效的命名。如果我们要连接所有的非C/C++标准库【包括外部或我们自己写的】,都需要指明-L和-l。不过,如果我们将这些库提前安装【也就是拷贝】到系统当中,其实我们也不需要指明路径了。其实,还有一个-I选项来指明我们需要的头文件,只不过这里默认在当前路径下找就不需要特殊指明了。
3. 动态库
下面简单制作了一个Makefile文件方便动态库的生成打包和垃圾文件的清理。
可以看到,在操作方面和静态库相比,不同的地方在于,我们都是使用gcc编译器,形成.o文件时,我们需要增加一个选项-fPIC,在打包文件时,我们需要增加一个选项-shared【动态库也叫共享库】。
• shared:表⽰⽣成共享库格式
• fPIC:产⽣位置⽆关码(position independent code),这里先不解释。
• 库名规则:libxxx.so
下面我已经把打包好的动态库和头文件拷贝给张三目录下,我们编译链接动态库可以成功生成可执行文件test,但是在执行该文件时却发生了问题:找不到动态库!
test所依赖的libmyc.so库果然没有找到。
解释:当我们在编译形成可执行文件时,我们只告诉了编译器gcc我们的动态库在哪里,叫什么,而并没有让系统知道我们的动态库在哪里叫什么!而可执行文件在运行时,需要知道动态库的路径和名字。
那这里就有一个问题了:为什么静态库在编译链接形成可执行后,就可以直接运行了,没有找不到库的问题???
那是因为,静态库在编译链接时就把静态库文件拷贝到了可执行文件当中,当我们运行时就不再依赖库了,程序直接加载到内存当中就可以运行。而动态库在形成程序加载到内存当中运行时,依然需要找到对应的动态库!
解决办法:
>系统会默认在指定路径下【一般来说在这个路径/lib64/】寻找对应的库文件,所以最简单粗暴的方法就是把我们的动态库拷贝到对应的路径【系统安装动态库】下。
>第二种方法就是给我们的库在系统路径下建立软链接,让软链接执向我们的库。
>第三种做法就是导环境变量的方法:系统不仅会在默认的路径下寻找动态库,还会根据环境变量中的LD_LIBRARY_PATH路径下寻找动态库,所以我们可以把我们库的路径导入到这个环境变量中。
>第四种做法就是修改相关的配置文件,先看操作:
我们将动态库所在的路径拷贝到文件/etc/ld.so.conf.d/mylib.conf中,其中,文件mylib.conf是我们自己随便新建的。
接下来,我们完成这个命令即可:【要⽣效,这里要执行ldconfig,重新加载库搜索路径】
4. 一些小问题
上面,我们初步了解了我们什么是动静态库以及它们的生成和使用的相关操作,下面有一些小问题。
>如果我们为程序同时提供动静态库,编译器会怎么做呢?
答:gcc/g++编译器会默认使用动态库!
>那如果,我们非要使用静态库链接,如何办到呢?
答:在编译时带上-static选项!不过,如果没有静态库,编译时则会报错!