一.库的概念
库是写好的现有的,成熟的,可以复用的代码。
本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。库有两种:静态库(.a、.lib)和动态库(.so、.dll)。
静态链接: 库随着程序一起编译,成为可执行程序的一部分,空间开销大,运行速度快
动态链接: 库只有在程序执行时被用到时才会被加入到内存中,空间开销小,运行较慢
二.Linux环境下静态库的制作及使用
首先使用-c选项,将c文件编译为二进制文件(.o),然后使用ar工具将二进制文件制作为静态库。
ar rcs [libXXXX.a] [所用到的.o文件]
其中,库文件名字XXXX可随意编写,但必须以lib为开头,.a为结尾
使用静态库的过程中,使用gcc编译c文件时,需要加上静态库的存储目录以及名称。
静态库使用,是将静态库实际编译至可执行文件。
如 : gcc test.c ./lib/libtest.a
注意:
源文件中必须要有函数的定义才可正常编译,若在源文件中没有包含库文件中的函数定义,系统会为其自动隐式定义,可正常执行,但会报错,使用-Wall选项可以查看有报错信息
所以在使用静态库的时候,一般都会在源文件中包含一个头文件,内部是所有的静态库函数定义。
在头文件中,要包含一个头文件守卫,防止重定义。
总结使用过程:
gcc [c文件] [库文件目录及名称] -I [头文件目录] -o [可执行文件命名]
如:gcc test.c ./a/libmymath.a -I ./b/ -o test.exe
gcc中选项:
-I 指定头文件目录
-o 指定文件名称
-Wall 查看报错信息
-g 增加调试语句
-c 只做预处理,编译,汇编操作,得到二进制文件,不进行连接操作。
三.Linux环境下动态库的制作及使用
第一步:
与静态库制作不同,在生成二进制文件(.o)时,需要生成与位置无关的代码,使用选项**-fPIC来实现。
原因:因为动态库是在程序运行时加载进入内存,其内部函数的地址绑定时间晚于源文件函数,只有动态库加载进入内存时,这些函数才有确切的地址(地址回填),而源文件中定义的函数在链接后就已经绑定地址。
第二步:
制作动态库,使用gcc -shared -o [库名] [二进制文件]**
第三步:
使用动态库时,与静态链接不同,不会把动态库编译进入可执行文件,但需要指定库
-l 指定库名(去掉前缀lib 和 后缀so)
-L 指定目录
gcc [执行文件] -l [库名] -L [目录地址]
第四步:
此时的可执行文件还不可运行,这是因为动态链接器还无法工作,动态链接器需要找到库的存放目录,通过配置环境变量LD_LIBRARY_PATH 来指定。
执行一次环境变量的配置,在下次启动终端时不在生效,也就是临时生效。
为了解决这个问题,可修改.bashrc配置文件,加入如下语句,可以实现启动时自动读取库目录。
四.链接过程中的地址回填和数据段合并
1.什么是地址回填?
上文讲动态库链接时,动态库函数的地址会晚于源文件中函数的地址绑定,它们是在动态库加载进入内存后才进行绑定的,在加载入内存后,进行动态链接时会进行这个操作,被称为地址回填。
2.什么是数据段合并
其目的就是为了减少内存开销。
在Linux系统中,内存分页管理,一个页面多数为4K或8K
如下内存图中的可改数据段和不可改数据段,如果它们被分配的页面大小超过它们本身的所需要存储大小,就会降低内存的利用率。于是可以将某些数据段合并,用于增大内存利用率。这就是数据段合并。
GCC编译过程:
程序中头文件与源文件不在同一目录下,需要使用gcc -I 指定头文件目录位置
使用C++ 11新标准时,需要加 -std=c++11