动态库和静态库
前言
在linux系统下开发应用的时候我们常会用到一些已有的接口,这些接口一般是以库的形式提供给我们使用的,常见的形式有两种,一种以.a为后缀的静态库;一种是以.so为后缀的动态库。
动态库和静态库的创建和使用
1.静态库
假设有一个打印“hello world!”的功能的库,我们要在main函数中调用这个库中的打印接口。下面我们以静态库的形式来实现。
1.1.创建静态库
首先,我们有一个打印“hello world!”的源文件
ydq@docsis4 gcc $ cat hello.c
#include "hello.h"
#include <stdio.h>
void hello(void)
{
printf("hello world!\n");
}
接着我们用这个源文件来生成一个静态库,首先我们必须把源文件hello.c编译为目标文件hello.o。
ydq@docsis4 gcc $ gcc -c hello.c
ydq@docsis4 gcc $ ls
hello.c hello.h hello.o main.c
然后我们把目标文件归档
ydq@docsis4 gcc $ ar -r libhello.a hello.o
ar: creating libhello.a
ydq@docsis4 gcc $ ls
hello.c hello.h hello.o libhello.a*
1.2.链接静态库
我们用gcc编译时链接静态库,编译完成后,执行以下生成的可执行文件
ydq@docsis4 gcc $ gcc main.c libhello.a -o main.static
ydq@docsis4 gcc $ ./main.static
hello world
这样,我们就实现了用静态库的方式为我们的程序main.c提供一个打印hello world语言的函数接口了。
ps: 程序 ar 配合参数 -r 创建一个新库 libhello.a 并将命令行中列出的对象文件插入。采用这种方法,如果库不存在的话,参数 -r 将创建一个新的库,而如果库存在的话,将用新的模块替换原来的模块。
2.动态库
在这里,我们用动态库的形式实现打印"hello world!"的函数接口。
2.1.创建动态库
我们照常用hello.c来编译生成动态库,执行以下命令就可以生成一个动态库。
ydq@docsis4 gcc $ gcc -fPIC -shared hello.c -o libhello.so
我们查看一下当前目录,发现多了一个libhello.so文件
ydq@docsis4 gcc $ ls
hello.c hello.h libhello.so* main.c
2.2 动态库的使用
首先,我们编写一个main.c源文件。
ydq@docsis4 gcc $ cat main.c
#include "hello.h"
#include <stdio.h>
int main(void)
{
hello();
}
ydq@docsis4 gcc $
好了,我们已经有了调用hello()接口的main函数了,这时候可能部分同学就有疑问了,哎我们是怎么知道这个hello()函数是怎么使用的呢?嗯,这个是好问题。一般情况下我们可以通过对应的头文件来查看到这个函数的原型,有了原型,我们就知道咋传参使用啦。下面就是对应的头文件。
ydq@docsis4 gcc $ cat hello.h
#ifndef __HELLO_H_
#define __HELLO_H_
extern void hello(void);
#endif
好的,现在我们已经知道怎么使用hello()函数和编写了一个调用hello函数的main.c源文件,接下来我们就来编译可执行文件啦,编译很简单,我们只需要在gcc编译时加上-lhello选项就可以链接上动态库了,好让我们来尝试一下。
ydq@docsis4 gcc $ gcc main.c -o main -lhello
/usr/bin/ld: cannot find -lhello
collect2: error: ld returned 1 exit status
我去,咋回事?怎么提示说不能够找到hello这个库?莫非命令敲错了?其实没有指定动态库的路径拉,一般情况下系统只认/usr/lib这个目录是动态库的目录,所以编译器在链接的时候会默认去/usr/lib目录上找,在/usr/lib目录里当然没有你的libhello.so库啦。听到我这样说,肯定有很多小机灵鬼知道咋解决了,我直接把libhello.so给拷贝到/usr/lib目录下不就行了吗?嗯,这个确实可以解决问题,不过,我们还可以加-L./来指定链接的时候在当前工作目录下寻找libhello.so。好,我们再试一下。
ydq@docsis4 gcc $ gcc main.c -o main -lhello -L./
编译成功了,这时候我们在当前目录下有了main这个可执行文件,好家伙,让我们执行试试。
ydq@docsis4 gcc $ ./main
./main: error while loading shared libraries: libhello.so: cannot open shared object file: No such file or directory
哦,这个是咋回事?执行的时候又找不到这个动态库了?怎么解决呢?莫非也可以把libhello.so拷贝到/usr/lib上去就可以解决了?答案是不行的拉,除非是在链接前就把libhello.so放到/usr/lib目录中,这样在main执行的时候,才不会提示说找不到该库。那有没有解决方法呢?肯定啦,不然还怎么玩。
这时候,我们需要利用一个叫环境变量的东西(在这里就不讲了,自行google或百度啦),我们可以通过export,把当前目录添加到环境变量中去,查找动态库的环境变量名是LD_LIBRARY_PATH。接下来我们执行看看
ydq@docsis4 gcc $ export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
执行完毕后,我们运行main试试。
ydq@docsis4 gcc $ ./main
hello world
oh,喜大普奔,我们终于可以执行main可执行文件,也终于可以调用hello打印函数打印出hello world!了。
总结
到了这里,我们已经讲明白了动态库和静态库的创建和使用过程,下一章节我们会讲动态库和静态库的区别,以及选择使用静态库还是使用动态库的理由。