库的简介
库函数是将函数放到库中,供别人使用的一种方式。一般库中存放的代码都是经过优化稳定性很好的代码。这样做的目的是为了让程序员减少编写代码的时间,从而将时间用于如何取解决问题而不是如何编写代码。库分为两种:(1)静态库、(2)动态库。
静态库.a(win系统下为.lib)和动态库.so(win系统下为.dll)。根据在链接阶段如何处理库,将库分为静态库和动态库。
静态库:指在程序编译时将库同时编译进目标代码中,一旦编译完成,如何改静态库是否还存在程序都可以执行。即如果在链接时以静态链接的方式处理库,那么程序编译完成后将不再需要静态库。因为需要将库编译入代码中,所以这种方式生成的文件还会比较大。
静态库的特点:
(1)静态库对函数库的链接是在编译时完成的。
(2)编译完成后就不需要静态库了。
(3)生成的文件体积大。
(4)更新困难。因为静态库时直接编译进入程序的,所以当库的发生变化时,就需要将程序重新编译。
动态库:指在程序编译链接时将库以动态链接的方式处理,这样处理的库不会被链接到代码中,而时会生成一个动态库表,当程序需要执行时,从表中找到对应的库,然后继续执行。这样生成的文件会比较小。
动态库特点:
(1)将一些库函数的链接载入推迟到了程序运行时期。
(2)可以是实现进程间资源的共享。
(3)更新简单。只要函数的接口不发生变化,无论函数的内容如何变化都不会影响动态库的使用。
Linux系统下静态库和动态库的制作
(1)“素材”准备
编写一个简单的小程序,实现整数的加、减、乘、除。将四个函数功能分别写入4个文件中,将函数的声明写在calc.h中。主函数写在main.c中。
//main.c
#include <stdio.h>
#include "calc.h"
int main()
{
printf("加法:%d\n",add(5,5));
printf("减法:%d\n",sub(5,5));
printf("乘法:%d\n",mul(5,5));
printf("除法:%d\n",div(5,5));
return 0;
}
//add.c
#include "calc.h"
int add(int m,int n)
{
return m+n;
}
//div.c
#include "calc.h"
int div(int m,int n)
{
return m/n;
}
//mul.c
#include "calc.h"
int mul(int m,int n)
{
return m*n;
}
//sub.c
#include "calc.h"
int sub(int m,int n)
{
return m-n;
}
//calc.h
int add(int m,int n);
int sub(int m,int n);
int mul(int m,int n);
int div(int m,int n);
(2)静态库的制作
将需要使用的.c/.cpp文件编译生成.o文件。
gcc -c -fPIC *.c //*通配符,即所以以.c结尾的文件
参数说明:
-fPIC:生的.o文件时和位置无关的(地址采用偏移量)。
将.o文件打包问库文件。
ar rcs libcalc.a *.o
libcalc为生成库的名字。一般静态库的命名是以li开头.a结尾的。
我们新建一个目录将所以的库文件放入改目录。
静态库的使用
gcc -Icalc main.c lib/libcalc.a -o sCalc
(3)动态库的制作
步骤是一样的都需要将.c文件编译生成.o文件。然后将.o文件打包。
gcc -shared -Wl,-soname,libcalc.so.1 -o libcalc.so.1.10 *o
一般动态库的命名为lib起头.so结尾。后面的数字一会解释。
将生成的文件移动lib目录中。
为libcalc.so.1.10生成一个名为libcalc.so的符号链接。
ln -s libcalc.so.1.10 libcalc.so
编译动态库
gcc -Icalc main.c lib/libcalc.so -o tCalc
将库文件的目录加入/etc/ld.so.conf,让系统可以知道在什么地方可以找到动态库。
ldconfig -v //更新配置文件
之后就可以使用刚才生成的文件了。
文件目录树
动态库命名时的后面数字(版本号)
一般动态库会有三个名字分别是realname(真实姓名)、soname(库姓名)、linkername(链接姓名)。
那一个库为什么要起三个姓名那?
一般库的命名需要遵循一下的规则:即将库名命名为libname.so.x.y.z的格式。最前面使用前缀”lib”,中间是库的名字和后缀”.so”,最后三个数字是版本号。x是主版本号(Major Version Number),y是次版本号(Minor Version Number),z是发布版本号(Release Version Number)。
在程序执行时一般记录的是soname,通过soname去找真正的库函数。通过上面说明,我们可以得知soname记录的库函数的主版本。即如果库函数的主版本不变,无论次版本如何变那么程序都时可以执行(要符合规则的变,不能乱变)。如果一旦主版本号发生变化,那么程序就不能运行了。举个列子:
你的程序在编译时使用的动态库。版本为1.10,那么如果其他人的电脑上安装的版本时1.20,1.30,这样以1开头的版本,那么这个程序在这些电脑上都是可以运行的,但如果有个人安装了2.10版本,那么程序在他的电脑上就不能运行。
既然是这样,那要soname和realname不就可以了吗,为社么还要linkername那?
linkername主要是为了适应makefiles(管理代码的),通常为多人共用的。这样可能每个人的库的版本都不太一样,那么就产生了麻烦,所以就产生了linkername,消除这样的麻烦。
需要明白的是无论怎样命名最后执行的都是reaname文件。linkername和soname都是其的软链接。