静态库:程序在编译链接的时候把库的代码链接到可执行文件中,程序运行的时候将不再需要静态库(静态库是拷贝自己需要的部分)。
动态库:程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。一个动态链接的可执行文件仅仅包含它用到的函数的入口地址的一个表,而不是外部函数所在目标文件的整个机器码。在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接。动态库可以在多个进程间共享,所以动态链接使得可执行文件更小,操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程公用,节省了内存和磁盘空间。(动态库是将完整的库加载到内存)
比较:静态库是牺牲了空间效率换取了时间效率,动态库是牺牲了时间效率换取了空间效率,没有好与坏的区别,看具体需要。
在windows中:动态库(.dll),静态库(.lib)
在linux中:动态库(.so),静态库(.a)
1、生成静态库(linux centos7)
main.c
#include <stdio.h>
#include "myAdd.h"
#include "mySub.h"
int main()
{
printf("add:%d\n", my_add(10, 20));
printf("sub:%d\n", my_sub(10, 20));
return 0;
}
myAdd.h
#pragma once
int my_add(int x, int y);
myAdd.c
#include "myAdd.h"
int my_add(int x, int y)
{
return x + y;
}
mySub.h
#pragma once
int my_sub(int x, int y);
mySub.c
#include "mySub.h"
int my_sub(int x, int y)
{
return x - y;
}
生成.o文件:
gcc -c *.c
有时候.o文件特别多,为了不丢失,将所有.o文件打一个包,生成一个库。
ar -rc libMyMath.a mySub.o myAdd.o
打包的时候不能把main.o也打包了,不然别人用库的时候会提示main函数重复定义。
看一下库的构成:
ar -tv libMyMath.a
需要注意:
1、打包静态库需要所有的源文件的.o
2、交付给本人使用的时候,一定是两个东西:头文件+.a(静态库)
头文件在预处理时使用,告诉编译器我有这个方法,静态库在链接时被使用。
我们用一个Makefile文件来把这两个东西打包:
#定义一个变量,类似于C语言的宏定义
lib=libMyMath.a
#将.o文件打包成静态库
$(lib):myAdd.o mySub.o
ar -rc $@ $^
myAdd.o:myAdd.c
gcc -c myAdd.c
mySub.o:mySub.c
gcc -c mySub.c
#打包成要给本人用的库(发布)
.PHONY:output
output:
#-p是指产生一个多级目录
mkdir -p lib/include
mkdir -p lib/lib
cp *.a lib/lib
cp *.h lib/include
#清理工程(清理)
.PHONY:clean
clean:
rm -rf *.o *.a lib
然后执行make命令和make output命令,用tree命令查看结果:
在没有把头文件和库文件拷贝到默认路径的情况下,需要按如下命令执行:
gcc -o main main.c -I./lib/include -lMyMath -L./lib/lib
./main //执行生成的可执行文件
-I头文件的路径(不指名则从默认路径找)
-l要连接的库的库名(去掉lib和.a)
-L要连接的库的路径(不指名则从默认路径找)
为什么我们使用我们的库这样麻烦,C库使用起来很简单,一个选项都没有?
原因是:C库的头文件和库文件是被安装到系统默认路径的。
把自己的库安装到系统中就相当于把头文件拷贝到默认路径当中,把库文件拷贝到默认路径当中。然后当程序用的时候就不用任何选项了,直接包含头文件就可以了。
2、生成动态库(linux centos7)
我们用一个Makefile文件来打包生成动态库:
#定义一个变量,类似于C语言的宏定义
lib=libMyMath.so
#将.o文件打包成静态库
$(lib):myAdd.o mySub.o
gcc -shared -o $@ $^
#在生成动态库时,生成.o文件需要加-fPIC选项(产生位置无关码)
myAdd.o:myAdd.c
gcc -fPIC -c myAdd.c
mySub.o:mySub.c
gcc -fPIC -c mySub.c
#打包成要给本人用的库(发布)
.PHONY:output
output:
#-p是指产生一个多级目录
mkdir -p lib/include
mkdir -p lib/lib
cp *.so lib/lib
cp *.h lib/include
#清理工程(清理)
.PHONY:clean
clean:
rm -rf *.o *.so lib
执行如下命令,用tree命令查看结果:
make
make output
执行如下命令生成可执行文件:
gcc -o main main.c -I./lib/include -lMyMath -L./lib/lib
但是此时直接执行./main会报错:
用ldd
命令查看发现找不到我们的库:
原因:编译器和加载器是两个东西,我们提供的路径是给编译器看的,所以编译可以生成可执行文件,但是运行时加载器在默认路径下找不到我们的库。
方法1:将.so拷贝到系统共享路径下一般指/usr/lib
方法2:更改LD_LIBRARY_PATH
export LD_LIBRARY_PATH=/home/test/bit81/lesson_5/lib/lib
如上图所示,此时已经可以找到我们的库文件。
程序执行成功!
当然我们可以将动态库和静态库一起打包到/lib/lib
目录下给用户,这样用户默认就是动态链接,加-static
参数就是静态链接:
gcc -o main main.c -I./lib/include -lMyMath -L./lib/lib 静态链接
gcc -o main main.c -I./lib/include -lMyMath -L./lib/lib -static 动态链接
库的安装:将头文件放到系统的头文件路径当中,将库文件安装到系统的库文件路径当中。
库的卸载:把头文件删掉,把库文件删掉。
注意:在使用C和C++内置的库和系统调用接口,编译的时候不用指出库的名字。但是对于第三方库,比如说phtread库,编译的时候一定要加上-l库名
,因为编译器虽然能找到路径,但是不知道要链接哪个库,我们用-l选项来告诉编译器需要链接哪个库。例如:gcc -o test test.c -lpthread
。