为什么使用库文件?
想将我实现的函数或功能提供给他人使用,但又不愿意公布源码,就可以将其制作成库文件,库文件是二进制文件,别人无法获取你的源码,却能够使用它。
认识库
静态库文件名为libxxx.a
动态库文件名为libxxx.so
xxx为库名
无论是静态库还是动态库,都有相对应的头文件,其头文件中描述该库中存在的函数声明等信息,只有库文件里才是实现。其实可以想象为.h与.cpp的关系,光有.h文件是用不了的,但只有.cpp文件也是不行的。所以一定不要忘记与其对应的头文件。
静态库特点
静态库文件在编译阶段将所有内容拷贝到目标文件中去,生成一个可执行文件,之后即使将静态库删掉也没关系了,因为代码已经全放在可执行文件里了!
动态库特点
动态库只需要在编译时,将一些关于动态库的必要信息加入到目标文件,生成的可执行文件,不过这个动态库可必须一直跟着可执行文件,不然可执行文件就无法正常运行,毕竟最后生成的文件只包含一些关于动态库的信息,但是动态库中的代码却没有像静态库一样全部拷贝进去,反而是由动态库自身保留。
静态库与动态库比较
静态库是所有内容拷贝到目标文件,因此生成程序是比动态库更大的。并且当多个程序都使用了同一个静态库a,他们运行时,事实上相同的静态库a的代码会在内存中存在多份,他们每人都会有一份。
动态库则是在程序运行时,将对应动态库代码放到内存中,即便有多个程序同时使用同一个动态库a,同时运行时,内存里只有一份动态库a的代码,他们将共享这份代码。
因此内存占用上来说动态库更小。
静态库由于将代码直接放入可执行文件中,因此当文件运行时,代码就进入了内存
动态库却是在文件使用到它的时候才会被调入到内存。
对比来说使用静态库的速度更快。
由于静态库每次都是将全部代码拷贝进可执行文件,因此如果库发生更新,那么对应的可执行文件又要重新生成。
而动态库一直单独存放,当动态库发生更新时,只需要更新相应的库文件,其余都不需要发生改变。
所以动态库在更新使用上更加方便。
生成静态库文件
不论静态库还是动态库文件,都是在编译阶段加入的,因此需要使用.o文件来生成库文件
例如现有文件main.c, a.c, b.c和一个包含他们函数声明的头文件head.h(a.c和b.c里都可以不用包含head.h头文件,但main.c需要),首先将其编译为目标文件.o
gcc -c a.c b.c
此时生成a.o 和 b.o文件,使用他们来生成libabc.a
ar rcs libabc.a a.o b.o
至此就生成了libabc.a,注意它的库名是abc,libabc.a是文件名
使用静态库文件
gcc 源文件 -o 可执行文件名 -I 头文件路径 -l 库名 -L 静态库路径
gcc main.c -o main -I 头文件路径 -l abc -L 静态库路径
生成main即是包含了静态库文件的可执行文件
生成动态库文件
同样的也需要.o文件来生成,但需要加一个-fpic/-FPIC的参数,生成与位置无关的.o文件
gcc -c –fpic a.c b.c
生成动态库 libabc.so
gcc -shared a.o b.o -o libabc.so
使用动态库文件
与静态库的使用方式一样
gcc 源文件 -o 可执行文件名 -I 头文件路径 -l 库名 -L 静态库路径
gcc main.c -o main -I 头文件路径 -l abc -L 静态库路径
生成main可执行文件,然而运行时却报错./main: error while loading shared libraries: libabc.so: cannot open shared object file: No such file or directory
可以使用ldd命令来查看文件所依赖的动态库
ldd ./main
在这里也能看到,它找不到libabc.so
由于系统不知道动态库的路径所以无法找到,所以需要告诉系统该动态库的路径
因此可以修改环境变量 LD_LIBRARY_PATH
修改环境变量:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:<动态库所在绝对路径>
此时main即可执行成功
但也需要注意这里直接export是临时添加环境变量,当终端关闭后则会消失
如果想要让其一直存在,则需要编辑.bashrc文件
vim ~/.bashrc
在最后添加
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:<动态库所在绝对路径>
关闭bashrc
source ~/.bashrc
此时配置的动态库文件路径将永久存在