0.引言
当进行C语言的编写时,想要查看标准库的某个函数是怎样实现的,比如 stdio.h 里的 printf 函数,跳转后只能看见函数声明,而无法跳转到函数的定义处,为什么无法查看函数的定义,这就涉及到库文件使用。
1.库文件是什么
C程序由 .c 文件最终变成可执行文件要经历预编译,编译,汇编和链接过程,在 Linux 下不同的过程分别生成如下文件 .i,.s,.o 和最终的可执行文件。而库文件就是一组预先编译好的函数的集合,它由 .o 文件生成,而 .o 是一个二进制文件,里面的内容是无法直接理解的,这也是为什么查看不了库中函数的定义,不能看,只能用。Linux 中库的位置一般在 /lib,/usr/lib 和 /usr/lib64 下,而库的头文件一般放在 /usr/include 下。库有两种,一种是静态库,命名形式是 libxx.a,一种是动态库(又叫共享库),命名形式是 libxx.so
2.静态库的生成与使用
2.1.静态库的生成
创建两个 .c 文件和一个 .h 文件,其中 add.c 和 mul.c 文件中包含函数的定义,foo.h 文件中包含函数的声明,具体内容如下。
首先将需要生成库文件的所有 .c 文件编译生成 .o 文件。
利用 ar 命令将所有 .o 文件生成静态库,需要三个参数:c 创建库,r 将方法添加到库中,v 显示过程。
其中,libfoo.a 就是所生成的静态库。
2.2.静态库的使用
编写一个 main.c 文件,其中调用 add 和 mul 两个函数。
直接利用 libfoo.a 和 main.c 文件生成可执行文件,涉及到两个参数:-L 指定所使用的静态库的位置,-l 指定静态库的名字(去掉静态库名字里的前缀 lib 和后缀 .a)。
3.动态库的生成与使用
3.1.动态库的生成
同样利用 add.c 和 mul.c 来生成动态库,其中 foo.h 中是函数的声明,add.c 和 mul.c 中是函数的定义。
首先将需要生成库文件的所有 .c 文件编译成 .o 文件。
使用 gcc 命令和所有 .o 文件来生成动态库。
其中,libfoo.so 就是所生成的动态库。
3.2.动态库的使用
编写一个 main.c 文件,其中调用 add 和 mul 两个函数。
直接利用 libfoo.so 和 main.c 文件生成可执行文件,涉及到两个参数:-L 指定所使用的动态库的位置,-l 指定动态库的名字(去掉动态库名字里的前缀 lib 和后缀 .so)。需要注意的一点是,如果库的存储路径下存在同名的静态库和动态库,gcc 默认使用动态库。
gcc 命令执行完之后会生成可执行程序 main,执行 main 会出现报错。
原因是程序运行时加载动态库,找不到对应的动态库文件 libfoo.so,这是由于虽然用参数 -L 指定了路径,但系统仍然会按照默认方式去存储库的标准位置(/lib 或者 /usr/lib)加载动态库,而不会在当前位置中查找,解决该问题有两种方式:1.将生成的动态库拷贝到 /usr/lib 下,再执行 gcc 命令;2.修改环境变量 LD_LIBRARY_PATH 来指定加载动态库的路径。
方式一:
后面两条命令将动态库 libfoo.so 移出来,并检查 /usr/lib 路径下不存在 libfoo.h。
方式二:
修改环境变量 LD_LIBRARY_PATH。
其中主要涉及到的命令有
export LD_LIBRARY_PATH=. //添加环境变量(赋值运算符两边不要有空格)
echo $LD_LIBRARY_PATH //查看环境变量的值
unset LD_LIBRARY_PATH //删除环境变量
4.静态库与动态库的区别
首先利用相同的程序 add.c mul.c 生成 .o 文件,分别按之前的步骤生成静态库 libfoo1.a 和动态库 libfoo2.so,并将动态库 libfoo2.so 移动到 /usr/lib 下。然后,使用相同的 main.c 文件,利用 gcc 命令生成可执行文件 main1 (使用静态库 libfoo1.a 生成) main2 (使用动态库 libfoo2.so 生成),最后查看两个可执行文件的大小可以发现由静态库生成的可执行文件要比由动态库生成的可执行文件大。
原因是,静态库在链接时将用到的方法包含到最终的可执行文件中,而动态库不包含,只做标记,在程序运行时才动态加载,所以生成的可执行文件更小。由于上面这个原因,每个使用静态库的应用程序都需要拷贝静态库到可执行文件中,所以用静态库生成的可执行程序比较大,一旦链接完成,在执行程序的时候就不需要原来的静态库;而动态库在链接的时候并没有被拷贝到可执行文件中,只是做了标记,程序运行时才动态加载,因此可执行程序比较小,然而,程序运行时需要运行环境提供相应的动态库。
动态库相比于静态库的优点,使用动态库更有利于程序的更新与升级,不必重新编译整个可执行文件,只需要用新版本的动态库替换掉旧版本的动态库即可。