一、首先,先引入为什么要有库,看下面的程序,也就是我们和一般写的程序差不多//结构树
tt
├── a.c
├── a.h
├── b.c
├── b.h
├── c.c
├── c.h
└── main.c
//a.h
#ifndef A_H
#define A_H
void a();
#endif
//a.c
#include "a.h"
#include "b.h"//其实这里不include的话,你直接编译这个子模块的话是没有错的,但是在链接的过程中就可能出错了,因为链接的时候是唯一确定符号的,而且不写的话,它就是一个int b();的函数了,但是与你定义的格式可能不一新,你定义的可能就是char *b(int i,int *p);
void a(){
b();
}
//b.h
#ifndef B_H
#define B_H
void b();
#endif
//b.c
#include "b.h"
#include "c.h"
void b(){
c();
}
//c.h
#ifndef C_H
#define C_H
void c();
#endif
//c.c
#include
#include "c.h"
void c(){
printf("hello world !\n");
}
//main.c
#include "a.h"
#include
int main(){
a();
}
[root@localhost tt]# ls
a.c a.h b.c b.h c.c c.h main.c
[root@localhost tt]# gcc a.c b.c c.c main.c -o main //这里用到什么*.c就是编译啦,笔者在这里犯一个错误,找了好久,最后才发现没有加上子功能函数~~
[root@localhost tt]# ls
a.c a.h b.c b.h c.c c.h main main.c
[root@localhost tt]# ./main
hello world !
[root@localhost tt]#
/*
1.一般我们写的程序都很小的,而且都是放在一个目录下面的,但是这样当项目大了就不好管理了而且当源程序太多了的话就会显得太乱了;
2.讲一下一般的C语言项目的规范吧,一般不同的人会写不同的程序来实现不同的模块和功能,也就是我们写的*.c,
3.但是就别人要调用我们的功能的时候怎么办呢,所以我们就把函数的功能和声明放在一个头文件里,里面写明了这个函数的功能和参数和返回值的意义,这就是*.h;
4.所以别人要用我们写的功能函数时候,我们直接把源程序编译好成*.o和头文件一起发过去即可(或者你会问为什么不发一个源代码呢,因为对方不一定和你在同一个地方,而且这也很可以),也就是说一般是一个xx.c对应一个xx.h,但是一般的啦,
5.然后项目负责人写一个main.c调用这样写好的*.c和include*.h即可,用到哪些就调用哪些和include哪些就好了,或者再写一个makefile来编译这些函数即可,又可以直接编译,例如上面的gcc a.c b.c c.c main.c -o main;
6.但是有个严重的问题,如果太多了功能子函数了,这样头文件也足够多了,假如有100个,1000个,那么gcc 后面写的话会写死人的,而且不能写错,所以就产生了库,用来方便管理这些子功能函数,同时会把打包到库的子功能函数的声明放在一个总的头文件里面,
7.而且项目的话一般会有不同的目录来管理不同的文件,一般至少有下面说到的三个文件夹,include,
lib,src--include一般就会放头文件,也就是全部子功能函数的说明都在放在里面,lib就是用来存放库的,src就是子功能函数的源代码和主函数,makefile一般放在与这些子目录的同一目录下面
8.无论是静态库还是动态库都是由*.o组成的,下面就介绍库吧!~
*/
二、库分为静态库和动态库
静态库名字格式:静态库一般以lib为前缀,.a为后缀,中间才是库的名字,eg:libm.a,libxx.a
优点:使用静态库函数编译成的文件比较大,因为整个函数库的所有数据都会被整合进目标代码中。编译后的执行程序就不需要外部的函数库支持,直接运行即可
缺点:静态函数库一旦改变了,那么程序就必须重新编译,而且编译出来较大
编译库:先把所有的*.c即子功能函数编译成*.o,然后用ar归档,最后直接使用即可,看下面的例子:libtest/
├── include
│ └── abc.h
├── lib
│ ├── a.o
│ ├── b.o
│ └── c.o
└── src
├── a.c
├── b.c
├── c.c
└── main.c
//只修改两个部分,main.c和abc.h,其它的放在相应的目录即可,不用修改,lib里面的内容
gcc -c a.c -o a.o;gcc -c b.c -o b.o;gcc -c c.c -o c.o生成即可
//include/abc.h-->把所有的子功能函数的声明和功能的说明放在这个文件中
#ifndef ABC_H
#define ABC_H
void a();
void b();
void c();
#endif
//main.c
#include "abc.h"
#include
int main(){
a();
}
[root@localhost lib]# pwd
/home/yyj/libtest/lib
[root@localhost lib]# ls
a.o b.o c.o
[root@localhost lib]# ar -rcs libmy_lib.a a.o b.o c.o
[root@localhost lib]# ar -rcs libmy_lib.o a.o b.o c.o
[root@localhost lib]# ar -rcs libmy_lib.xo a.o b.o c.o
[root@localhost lib]# ar -rcs libmy_lib.xo.a a.o b.o c.o
//rc命令来打包,c就是创建一个新的归档文件,r就是在这个归档文件中插入文件,s就是在归档文件中创建这些文件的索引
[root@localhost lib]# ls
a.o b.o c.o libmy_lib.a libmy_lib.o libmy_lib.xo libmy_lib.xo.a
[root@localhost lib]# rm a.o b.o c.o -f //已经制作成库了,*.o可以不要了
[root@localhost lib]# cd ../src;rm a.c b.c c.c -f //*.c也可以不要了,main.c不能删除
[root@localhost src]# pwd
/home/yyj/libtest/src
[root@localhost src]# ls
main.c
[root@localhost src]# gcc main.c -I../include/ -L../lib -lmy_lib -o main
[root@localhost src]# ./main
hello world ![root@localhost src]#
[root@localhost src]# ll
总用量 12
-rwxr-xr-x. 1 root root 4730 12月 29 20:06 main//因为把整个库都放都进来了,所以会比较大
-rw-r--r--. 1 root root 56 12月 29 19:46 main.c
//下面的实验就是证明:
1)链接静态的时候不能再使用-static,因为-static就是静态链接的意思
2)静态库的命名必须合符规范的,不然会错,前缀为lib,后缀为.a,不然会提示找不到
[root@localhost src]# gcc -static main.c -I../include -L../lib -lmy_lib -o main1/usr/bin/ld: cannot find -lc
collect2: ld 返回 1
//解释一下-I指定头文件 -L指定库的路径-l指定链接哪些库,需要去掉库前缀和后缀,因为头文件和库都不在Linux的默认路径下,详细查看博客的《Linux头文件与库的搜索路径》
[root@localhost src]# gcc main.c -I../include -L../lib -lmy_lib.xo -o main1
[root@localhost src]# ls
main main1 main.c
[root@localhost src]# ./main1
[root@localhost src]# LD_LIBRARY_PATH=../lib/
[root@localhost src]# gcc main.c -lmy_lib -I../include -o main4
[root@localhost src]# ./main4
hello world ![root@localhost src]#
/************************注意:无论是动态链接还是静态链接都是指是包括程序员自己本身要链接的库和系统的库********************************************/
//ldd就是用来查看当前的执行文件链接了哪些动态库的,最后一个/lib/ld-linux.so.2是动态加载器(是一个可执行程序),后面是是链接地址!
hello world ![root@localhost src]# ldd main
linux-gate.so.1 => (0x00c7e000)
libc.so.6 => /lib/libc.so.6 (0x00209000)
/lib/ld-linux.so.2 (0x00bec000)
[root@localhost src]# ldd main1
linux-gate.so.1 => (0x00bc8000)
libc.so.6 => /lib/libc.so.6 (0x00c12000)
/lib/ld-linux.so.2 (0x00bec000)
[root@localhost src]# ldd /bin/ln
linux-gate.so.1 => (0x00ca2000)
libc.so.6 => /lib/libc.so.6 (0x001ea000)
/lib/ld-linux.so.2 (0x00bec000)
[root@localhost src]#
/*动态库*/
三、动态库名字格式:动态库一般以lib为前缀,.so为后缀,中间才是库的名字,eg:libm.so,libxx.so
优点:使用动态库函数编译成的文件比较小,而且是在程序运行的时候才加载到内存里,可以与其它程序共享,以此来节省内存,
用别人的话就是:某个程序的在运行中要调用某个动态链接库函数的时候,操作系统首先会查看所有正在运行的程序,看在内存里是否已有此库函数的拷贝了。
如果有,则让其共享那一个拷贝;只有没有才链接载入。这样的模式虽然会带来一些“动态链接”额外的开销,却大大的节省了系统的内存资源。
C的标准库就是动态链接库,也就是说系统中所有运行的程序共享着同一个C标准库的代码段。
程序或者是软件升级比较方便,直接升级动态库即可,不用重新编译和链接其它库和代码即可
缺点:平台性不好
制作:先把子功能函数都编译成*.o,但是需要添加参数-fPIC,-f后面接选项,PIC(position independent code与链接地址独立的代码),然后使用gcc -shared链接选项生成动态库
我们还是以上面的为例子吧,看下面的操作libtest/
├── include
│ ├── a.h
│ ├── b.h
│ └── c.h
├── lib
│ └── libmy_lib.a//这个我们上面制造的静态库,暂时放着
└── src
├── a.c
├── b.c
├── c.c
└── main.c
[root@localhost lib]# pwd
/root/桌面/library/libtest/lib
//编译生成与链接地址无关的二进制文件
[root@localhost lib]# gcc -c -fPIC -I../include ../src/a.c -o a.o
[root@localhost lib]# gcc -c -fPIC -I../include ../src/b.c -o b.o
[root@localhost lib]# gcc -c -fPIC -I../include ../src/c.c -o c.o
[root@localhost lib]# ls
a.o b.o c.o libmy_lib.a
//编译生成
[root@localhost src]# gcc main.c -I../include -L../include -lmySlib -o main
[root@localhost src]# ls
a.c b.c c.c main main.c
[root@localhost src]# ./main
./main: error while loading shared libraries: libmySlib.so: cannot open shared object file: No such file or directory
[root@localhost src]# cd ../include/
[root@localhost include]# LD_LIBRARY_PATH=`pwd`
[root@localhost include]# ../src/main
hello world
[root@localhost include]#