一、静态库和动态库
1.库的含义
- 库就是已经写好的,成熟的,可以直接使用的代码,通常情况下,将已经写好的一些公用函数制作成函数库,提供给使用者和其他程序反复使用。
- 从本质上来说,库是一种可执行代码的二进制形式,可被操作系统载入内存执行。
- 库分为静态库和动态库两种。
2.静态库(.a)
- 静态库:在链接步骤中,连接器将从库文件取得所需的代码,复制到生成的可执行文件中的这种库。
- 静态库的特点是可执行文件中包含了库代码的一份完整拷贝, 使得程序在运行时与函数库再无联系,移植方便。
- 静态库的缺点是被多次使用就会有多份冗余拷贝,使得空间和资源被浪费。并且,即使仅当静态库的某一小部分获得了更新,那么所有的程序都会被重新下载。
3.动态库(.so)
- 动态库:在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入。
- 动态库的优点是解决了静态库对空间和资源造成的浪费,也使得当库更新时,用户仅需更新改动的部分。
4.静态库和动态库的区别
- 静态库在程序的链接阶段被复制到了程序中,和程序运行的时候没有关系。
- 动态库在链接阶段没有被复制到程序中,而是程序在运行时由系统动态加载到内存中供程序调用。使用动态库的优点是系统只需载入一次动态库,不同的程序可以得到内存中相同的动态库的副本,因此节省了很多内存。
二、用.gcc生成静态库和动态库
1.准备工作
(1)创建目录
- 打开
Document
下的terminal(终端)
- 在
terminal
中输入以下指令,创建一个名为test1
的目录并将此目录切换为目标目录
mkdir test1 /*创建名为test1的目录*/
cd test1 /*切换工作目录为test1*/
- 如图,在
Document
下出现了名为test1
的新目录
(2)创建文件
- 创建文件,以用于代码的存放,创建文件的详细操作请参考博客
- 通过终端指令创建好三个用于存放代码的文件
(3)编写代码
- 在hello.h中输入如下代码
#ifndef HELLO_H
#define HELLO_H
void hello(const char *name);
#endif //HELLO_H
- 在hello.c中输入如下代码
#include <stdio.h>
void hello(const char *name)
{
printf("Hello %s!\n", name);
}
- 在main.c中输入如下代码
#include "hello.h"
int main()
{
hello("everyone");
return 0;
}
输入代码保存完毕后,文件如图
(4)gcc编译得到.o文件
- 在终端输入指令
gcc 文件名 -o 文件名
或者gcc -c 文件名
,可得到.o文件,详细的操作请参考博客
- 如图,成功编译得到.o文件
2.静态库(.a)的生成和使用
(1)由.o文件创建静态库文件
静态库文件名的命名规范是以
lib
为前缀,紧接着跟静态库名,扩展名为.a
- 在终端输入指令
ar -crv lib静态库名.a 文件名.o
,可由.o文件创建一个静态库文件,前缀是lib - 如图,输入指令
ar -crv libmyhello.a hello.o
,可得到libmyhello.a
静态库文件
(2)在程序中使用静态库
只需在使用到这些公用函数的源程序中包含这些公用函数的原型声明,然后在用 gcc 命令生成目标文件时指明静态库名,gcc 将会从静态库中将公用函数连接到目标文件中。
- 有三种方法可以生成目标文件,使得静态库可以在程序中使用
gcc -o hello main.c -L. –lmyhello
gcc main.c libmyhello.a -o hello
gcc -c main.c
gcc -o hello main.o libmyhello.a
-
如图,采用第一种方法生成目标文件hello
-
在终端中输入指令
./hello
,执行hello文件,验证其是否可以正常运行
显示Hello everyone!
,程序可以正常运行 -
在终端中输入指令
rm libmyhello.a
,移除静态库后,再次运行hello,验证静态库中的公用函数是否已经全部连接到目标文件中。
移除静态库后,目标文件仍然能够正常运行,表明静态库中的公用函数已经连接到目标文件中。
3.动态库(.so)的生成和使用
(1)动态库的生成
动态库文件名命名规范和静态库文件名命名规范类似,只是文件拓展名改为
.so
- 在终端中输入指令
gcc -shared -fPIC -o libmyhello.so hello.o
,即可生成动态库文件libmyhello.so
(2)在程序中使用动态库
- 通过gcc指令生成目标文件,在终端输入指令
gcc main.c libmyhello.so -o hello
,生成连接了动态库的名为hello
的目标文件
- 通过在终端输入指令
./hello
来运行hello文件,验证连接是否成功
- 如上图,出现了
error(错误)
,其原因是No such file or directory
,即没有找到动态库。 - 具体解决方法是先将文件
libmyhello.so
复制到目录/usr/lib
中,即在终端中输入指令mv libmyhello.so /usr/lib
后,输入./hello
执行hello文件
- 如上图,出现error(错误):Permission denied(权限谨慎使用)
- 解决办法:使用root权限,详细参考博客:Linux下出现permission denied的解决办法
首先输入指令su root
,然后输入密码,若发现首部变为root
,尾部的$ 变为了#,则说明已经成功
- 现在再将
libmyhello.so
移动到/usr/lib/
目录中,并执行hello文件,在终端中输入指令mv libmyhello.so /usr/lib
,然后输入指令./hello
,执行hello文件
- 如上图,此时文件夹中已经没有
libmyhello.so
文件,表明该文件已经转移成功,执行hello文件后,显示Hello everyone!
表明函数连接成功
4.分析静态库和动态库
思考:当静态库和动态库同名时,gcc 命令会使用哪个库文件呢?
-
首先先将除了.h和.c以外的文件删除,只保留.h和.c文件,如图所示
-
创建静态库文件
libhello.a
和动态库文件libhello.so
-
生成目标文件hello
-
现在运行hello目标文件,验证其优先使用哪个库
-
发现出现找不到库的错误,这表明在静态库和动态库同名同时存在的情况下,会优先使用动态库
三、实例
1.实例1
具体的过程参考上述hello的建立过程
(1)代码部分
A1.c
#include<stdio.h>
void print1(int arg)
{
printf("A1 print arg:%d\n",arg);
}
A2.c
#include<stdio.h>
void print2(char *arg)
{
printf("A2 printf arg:%s\n",arg);
}
A.h
#ifndef A_H
#define A_H
void print1(int);
void print2(char *);
#endif
test.c
#include<stdio.h>
#include"A.h"
int main()
{
print1(1);
print2("test");
exit(0);
}
- 文件创建如图
(2)生成.o文件
指令:gcc -c A1.c A2.c
(3)静态库的构建和使用
指令ar -crv libfile.a A1.o A2.o
、gcc -o test test.c libfile.a
(4)动态库的构建和使用
指令:gcc -shared -fPIC -o libfile.so A1.o A2.o
、gcc -o test test.c libfile.so
2.实例2
具体的过程参考上述hello的建立过程
(1)代码部分
sub1.c
float x2x(int a,int b)
{
float c=0;
c=a+b;
return c;
}
sub2.c
float x2y(int a,int b)
{
float c=0;
c=a/b;
return c;
}
sub.h
#ifndef SUB_H
#define SUB_H
float x2x(int a,int b);
float x2y(int a,int b);
#endif
main.c
#include<stdio.h>
#include"sub.h"
void main()
{
int a,b;
printf("Please input the value of a:");
scanf("%d",&a);
printf("Please input the value of b:");
scanf("%d",&b);
printf("a+b=%.2f\n",x2x(a,b));
printf("a/b=%.2f\n",x2y(a,b));
}
- 文件创建如图
(2)生成.o文件
指令:gcc -c sub1.c sub2.c
(3)静态库的构建和使用
指令ar -crv libsub.a sub1.o sub2.o
、gcc -o main main.c libsub.a
(4)动态库的构建和使用
指令:gcc -shared -fPIC -o libsub.so sub1.o sub2.o
、gcc -o main main.c libsub.so
四、总结
总的来说,静态库和动态库的操作还是比较简单的,不过还是需要大家亲自操作,将理论在实践中验证。本篇博客仅供大家参考,若博客中存在纰漏,希望大家斧正。