linux下动态与静态链接库的使用及区别

1.为什么要有链接库
我们平时在写C代码时一定会经常重复写某些很多程序中都经常用到的代码,比方说字符串的简单操作函数,或者链表等。
那么我们如何能避免老是重复的去写这些基础的代码呢?也许你会不假思索的想到把这些常用的函数装到一个头文件里,
调用的时候只要include该头文件不就可以么。的确这样做是可以达到目的,但是如果我们把许多种不同功能的操作代码
(可以理解为一个函数)都放在一个头文件下,数量少了还好,如果数量有几十甚至上百时,这么多功能不同的函数组合在
一个头文件里,我想也许这个头文件只会你一个人去看它,因为其他人根本不知道里面写的是啥。此时你又会想,将这
写函数按功能装在不同的头文件里不就解决了上述问题。问题到这足以证明你有一颗灵活的大脑,哈哈。但话说回来我
们一般都在头文件里只定义函数,函数的具体实现都会另起一个文件里去实现。

例如:
我们在hello.h中定义如下

#ifndef HELLO_H
#define HELLO_H
//此函数功能实现打印Hello xxx
void hello(char *s);
#endif

然后我们另起一个名为hello.c的文件用于实现hello函数

void hello(char *s)
{
printf(“Hello %s\n”,s);
}

之所以规范要把头文件定义的函数和实现分离,主要原因是供使用头文件的人阅读起头文件来简单且方便。因为可能使用
你头文件的人根本不关心你的某个函数内部是如何如何用了几千行代码实现的某个功能,它只关心某个函数是用来干什么
的如何来传参数使用就足够了,所以如果你把头文件中的函数和实现放在一起的话,如果函数实现就几行还可接受,如果
它的实现代码量特别大的话,显然会对于上述使用者看头文件带来一定的复杂度,而且这样写我想你的代码结构也不会很好。
到这里我想我已把为什么要用头文件以及为什么要把头文件里的函数和实现分文件写说清楚明了了。既然上述方法已实现
了我们刚开始讨论的遇到的问题,那么为什么还要谈动态或静态链接库呢?

对此我们在回到上述头文件与其函数分开实现在不同文件的问题,我们可以想想一下假如我们的一个主程序中用到了超过10个
自己实现的.h文件,那么对应就会有10个.c文件,此时你会如何编译??

如果你是新手,也许你会这样操作 gcc main.c 1.c 2.c 3.c … 10.c

很明显这样变一起来太繁琐了,或许你比较聪明使用makefile来管理你的编译,然后make一键编译,哦,乍一看好像解决了上述
编译的繁琐问题,但其实这还是治标不治本,你的makefile文件还得你自己来写把,那么我们在换个主程序,你的makefile是
不又得重新编译?好了此时我们的静态或动态链接库来帮助你解决麻烦,链接库也正是好多第三方库使用的方法,如我们在编
译线程相关的函数是得加-lpthread,使用libevent时后面加levent使用连接mysql时在后面加 -lmysql等。

2.链接库的功能
假如我们给上面的1.c-10.c先生成.o文件然后将其都放在名为libhello.a的链接文件中,那么我们编译的时候可以直接如下
gcc main.c -lhello

3.定义自己的链接库
1)静态链接库

在linux环境中, 使用ar命令创建静态库文件.如下是命令的选项:
d —–从指定的静态库文件中删除文件
m —–把文件移动到指定的静态库文件中
p —–把静态库文件中指定的文件输出到标准输出
q —–快速地把文件追加到静态库文件中
r —–把文件插入到静态库文件中
t —–显示静态库文件中文件的列表
x —–从静态库文件中提取文件
还有多个修饰符修改以上基本选项,详细请man ar 以下列出三个:
a —–把新的目标文件(*.o)添加到静态库文件中现有文件之后
b —–*****************************之前
v —–使用详细模式
ar的详细说明请读者自行 man ar就可获得

ar 命令的命令行格式如下:
ar [-]{dmpqrtx}[abcfilNoPsSuvV] [membername] [count] archive files…

参数archive定义库的名称, files是库文件中包含的目标文件的清单, 用空格分隔每个文件. ar与archive中间的为上述的命令选项

接下来我们就以下面的三个文件为例:
hello.h中的内容如下:

#ifndef HELLO_H
#define HELLO_H
//此函数功能实现打印Hello xxx
void hello(char *s);
#endif

hello.c中的内容如下:

void hello(char *s)
{
printf(“Hello %s\n”,s);
}

main.c中的内容如下:

#include “hello.c” //hello.c得在你当前目录下
int main(int argc,char **argv)
{
hello(“shreck”);
return 0;
}

接下来就开始生成我们自己的静态链接库
(1)将hello.c编译生成目标.o文件
gcc -c hello.c

此时我们ls,查看当前目录如下:
xj@ubuntu:~/ryw/test/hello$ ls
hello.c hello.h main.c

xj@ubuntu:~/ryw/test/hello$ gcc -c hello.c

xj@ubuntu:~/ryw/test/hello$ ls
hello.c hello.h hello.o main.c

(2)创建名字为libhello.a的静态链接库
ar cqs libmyhello.a hello.o //将.o文件放入名为libmyhello.a的链接库中

xj@ubuntu:~/ryw/test/hello$ ar cqs libmyhello.a hello.o

xj@ubuntu:~/ryw/test/hello$ ls
hello.c hello.h hello.o libmyhello.a main.c

(3)在主程序中使用静态库
如上我们的libhello.a的静态库文件已经生成,那么我们如何使用到它的内部函数呢?

a.首先我们要在使用这些公共函数的源程序中包含这些公用函数的原型声明(头文件里,所以只需包含头文件即可)
b.接着在用gcc命令编译时指明静态库名,gcc就会从静态库中将公用函数连接到目标文件中,需要注意的是gcc会自动在静态库名前加一个前缀lib和扩展名.从而得到扩展名为.a的静态库文件名来查找静态库文件

实例如下
gcc main.c -L. -lmyhello

xj@ubuntu:~/ryw/test/hello$ gcc main.c -L. -lmyhello

xj@ubuntu:~/ryw/test/hello$ ls
a.out hello.c hello.h hello.o libmyhello.a main.c

编译完成后我们的当前目录下运行./a.out结果如下
xj@ubuntu:~/ryw/test/hello$ ./a.out
Hello shreck

其中-L.是用来显示指出动态链接库在当前目录下,用户程序的链接库一般存在/user/lib中,
所以如果我们不想每次编译时都加-L.参数的话就可以把生成的动态链接库放在/user/lib下,这样我们编译时只需这样

gcc main.c -lmyhello

(4)删除静态库文件看程序还能否正确运行
我们将当前目录下刚生成的libhello.a文件删除(注意/user/lib下也不能有该文件别忘了),然后运行程序,结果如下

xj@ubuntu:~/ryw/test/hello$ ls
a.out hello.c hello.h hello.o libmyhello.a main.c

xj@ubuntu:~/ryw/test/hello$ rm -rf libmyhello.a
xj@ubuntu:~/ryw/test/hello$ ./a.out
Hello shreck

xj@ubuntu:~/ryw/test/hello$ ls
a.out hello.c hello.h hello.o main.c

程序正常运行,由此我们可以得出静态链接文件是在编译时就连接进来了,这一点与动态链接明显不同

2)动态链接库
还是用上面的hello.h,hello.c ,main.c三个文件

(1)将hello.c编译生成hello.o文件
gcc -c -fPIC hello.c (网站上使用的:由于我的是amd64位机所以得加-fPIC参数,运行结果如下)

xj@ubuntu:~/ryw/test/hello$ gcc -c -fPIC hello.c
xj@ubuntu:~/ryw/test/hello$ ls
hello.c hello.h hello.o main.c

(2)由.o文件创建动态库文件
命令如下
gcc -fPIC -shared -o libmyhello.so hello.o

需要注意的是用-shared参数来生成动态链接库,加-fPCI是因为我的是64位机,
libmyhello.so为动态库的名字(注意扩展名为.so),hello.o为往该库加的文件,
可以是多个文件,命令执行后会生成libmyhello.so文件,ls当前目录查看结果为

xj@ubuntu:~/ryw/test/hello$ ls
hello.c hello.h hello.o main.c
xj@ubuntu:~/ryw/test/hello$ gcc -fPIC -shared -o libmyhello.so hello.o
xj@ubuntu:~/ryw/test/hello$ ls
hello.c hello.h hello.o libmyhello.so main.c

(3)在程序中使用动态库
我们可以向静态库一样编译我们的main.c具体如下
gcc main.c -L. -lmyhello

xj@ubuntu:~/ryw/test/hello$ gcc main.c -L. -lmyhello
xj@ubuntu:~/ryw/test/hello$ ls
a.out hello.c hello.h hello.o libmyhello.so main.c

运行./a.out结果如下
xj@ubuntu:~/ryw/test/hello$ ./a.out
Hello shreck

(4)删除动态链接库
由于动态链接库是在程序运行时才加载的(静态库是在编译时就加载)所以一旦删除动态库,
那么程序在运行时将会报错说找不到某动态库,大家不妨试试,这里我就不在赘余了。

4.常见的链接库

平时我们遇到的链接库其实也挺多的,例如我们在编译带有线程库的函数时得在后面加-lpthread,
编译使用了libevent库的文件时,得在后面加上-levent,还有编译json或异步I/O等时都得在编译时都得加上特有的链接库。

需要声明的一点是,为了在讲解链接库时能够让大家理解起来更容易些,所以我一直都只说其针对c语言以及c的函数什么的,
其实动态库被几乎大部分语言所使用,每个语言几乎都有自己的第三方库,所以对应的也基本都会用到链接库。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值