一口君中学阶段最喜欢的一首歌。 窦仙才华横溢、潇洒不羁、与世无争,酷的外焦里嫩! 让我们随着窦仙高昂的声音来学习什么是(酷)库吧! 一、什么是库? 在windows平台和linux平台下都大量存在着库。一般是软件作者为了发布方便、替换方便或二次开发目的,而发布的一组可以单独与应用程序进行compile time或runtime链接的二进制可重定位目标码文件。 本质上来说库是一种可执行代码的二进制形式,这个文件可以在编译时由编译器直接链接到可执行程序中,也可以在运行时由操作系统的runtime enviroment根据需要动态加载到内存中。 一组库,就形成了一个发布包,当然,具体发布多少个库,完全由库提供商自己决定。 由于windows和linux的本质不同,因此二者库的二进制是不兼容的。 现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。 共享库的好处是,不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例。 本文仅讨论linux下的库。 二、库的分类 库有两种:静态库和共享库(动态库)。 win32平台下,静态库通常后缀为.lib,动态库为.dll ;linux平台下,静态库通常后缀为.a,动态库为.so 。 从本质上来说,由同一段程序编译出来的静态库和动态库,在功能上是没有区别的。不同之处仅仅在于其名字上,也就是“静态”和“动态”。 二者均以文件的形式存在,其本质上是一种可执行代码的二进制格式,可以被载入内存中执行。无论是动态链接库还是静态链接库,它们无非是向其调用者提供变量、函数和类。 1. 静态库 所谓静态库,就是在静态编译时由编译器到指定目录寻找并且进行链接,一旦链接完成,最终的可执行程序中就包含了该库文件中的所有有用信息,包括代码段、数据段等。 2. 动态库 所谓动态库,就是在应用程序运行时,由操作系统根据应用程序的请求,动态到指定目录下寻找并装载入内存中,同时需要进行地址重定向。 3. 区别 我们以编译链接、载入时刻两点来讨论静态库和动态库的区别。 编译链接 静态链接库在程序编译时会被链接到目标代码中,目标程序运行时将不再需要改动态库,移植方便,体积较大,浪费控件和资源,因为所有相关的对象文件与牵涉到库都被链接合成一个可执行文件,这样导致可执行文件的体积较大。 动态库在程序编译时并不会被链接到目标代码中,而是在程序运行时才被载入,因为可执行文件体积较小。有了动态库,程序的升级会相对比较简单,比如某个动态库升级了,只需要更换这个动态库的文件,而不需要去更换可执行文件。但要注意的是,可执行程序在运行时需要能找到动态库文件。可执行文件时动态库的调用者。 程序代码和库 载入时刻 二者的不同点在于代码被载入的时刻不同。静态库的代码在编译过程中已经被载入可执行程序,因此体积较大。共享库的代码是在可执行程序运行时才载入内存的,在编译过程中仅简单的引用,因此代码体积较小。 4. 优缺点 相对于动态库,静态库的优点在于直接被链接进可执行程序中,之后,该可执行程序就不再依赖于运行环境的设置了(当然仍然会依赖于 CPU指令集和操作系统支持的可执行文件格式等硬性限制)。 而动态库的优点在于,用户甚至可以在程序运行时随时替换该动态库,这就构成了动态插件系统的基础。具体使用静态库和动态库,由程序员根据需要自己决定。 三、库文件的制作 1. 库文件命名 静态库的名字一般为libxxxx.a,其中xxxx是该lib的名称;动态库的名字一般为libxxxx.so.x.y.z,含义如下图所示: 2. 制作库文件常用参数 首先需要了解gcc编译库要用到一些参数,很重要。 参数 含义 -shared 指定生成动态链接库。 -static 指定生成静态链接库。 -fPIC 表示编译为位置独立的代码,用于编译共享库。目标文件需要创建成位置无关码,概念上就是在可执行程序装载它们的时候,它们可以放在可执行程序的内存里的任何地方。 -L 表示要连接的库在当前目录中。 -l 指定链接时需要的动态库。编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.so来确定库的名称。 -Wall 生成所有警告信息。 -ggdb 此选项将尽可能的生成gdb的可以使用的调试信息。 -g 编译器在编译的时候产生调试信息。 -c 只激活预处理、编译和汇编,也就是把程序做成目标文件(.o文件)。 -Wl,options 把参数(options)传递给链接器ld。如果options中间有逗号,就将options分成多个选项,然后传递给链接程序。 3. 库源文件 假定我们要将以下两个文件制作成库文件 add.c int add(int x,int y){ return x+y;}int sub(int x,int y){ return x-y;} add.h int add(int x,int y);int sub(int x,int y); 4. 制作静态库并使用 需要把 add.c 编译成.o文件 gcc -c add.c 使用 ar 命令生成静态库libadd.a ar -rc libadd.a add.o 遵循静态库命名的规则 lib + 名字 + .a 使用静态库 要是用静态库libadd.a,只需要包含add.h,就可以使用函数add()、sub()。 #include #include "add.h"void main(){ printf("add(5,4) is %d\n",add(5,4)); printf("sub(5,4) is %d\n",sub(5,4));} 静态库的文件可以放在任意的位置,编译时只需要找到该库文件即可。 gcc test.c -o run libadd.a 库和头文件如果在其他目录下 使用以下命令编译: gcc -c -I /home/xxxx/include test.c //假设test.c要使用对应的静态库gcc -o test -L /home/xxxxx/lib test.o libadd.a 或者 gcc -c -I /home/xxxx/include -L /home/xxxxx/lib libadd.a test.c 1). 通过-I(是大i)指定对应的头文件 2). 通过-L制定库文件的路径,libadd.a就是要用的静态库。3). 在test.c中要包含静态库的头文件。 5. 制作动态库并使用 把add.c编译成动态链接库libadd.so gcc -fPIC -o libadd.o -c add.cgcc -shared -o libadd.so libadd.o 也可以直接使用一条命令 gcc -fPIC -shared -o libadd.so add.c 动态库的安装 通常动态库拷贝到/lib下即可: sudo cp libadd.so /lib 使用动态库 #include #include "add.h"void main(){ printf("add(5,4) is %d\n",add(5,4)); printf("sub(5,4) is %d\n",sub(5,4));} 编译动态库: gcc static -o run -ladd 注意观察编译时动态库的名字与库文件对应关系 libadd.so<--------->-ladd 去掉 .so,…