文章目录
本文主要通过举例来说明在Linux中如何创建静态库和动态库,以及使用它们。
一、静态库和动态库的定义
1、什么是静态库
2、什么是动态库
3、二者的区别
二、用 gcc 生成 .a 静态库和 .so 动态库
1、编辑生成例子程序 hello.h、hello.c 和 main.c
2、将 hello.c 编译成.o 文件
3、由.o文件创建静态库
4、在程序中使用静态库
5、由.o文件创建动态库文件
6、在程序中使用动态库
7、思考:当静态库和动态库同名时,gcc 命令会使用哪个库文件呢?
三、实践操作
1、作业要求
2、实操过程
2.1、编辑主程序文件和子程序文件
2.2、将.c文件编译成.o文件
2.3、将目标文件用 ar工具生成 .a 静态库文件, 再用 gcc进行链接,生成可执行程序
2.4、将目标文件用 ar工具生成 .so 动态库文件, 再用 gcc进行链接,生成可执行程序
2.5、静态库与动态库文件大小比较
总结
一、静态库和动态库的定义
1、什么是静态库
指在我们的应用中,有一些公共代码是需要反复使用,就把这些代码编译为“库”文件;在链接步骤中, 连接器 将从库文件取得所需的 代码 ,复制到生成的 可执行文件 中的这种库。
2、什么是动态库
就是存放在系统中的某个特定位置,提供了一些大部分程序都会使用到的功能集合。这样主程序在编译的过程当中就不需要把这部分功能编译到自己的程序中。只需要到系统中特定的位置直接调用动态函数库的功能就行了
3、二者的区别
库是共享程序代码的方式,一般分为静态库和动态库。. 静态库:链接时完整地拷贝至可执行文件中,被多次使用就有多份冗余拷贝。. 动态库:链接时不复制,程序运行时由系统动态加载到内存,供程序调用,系统只加载一次,多个程序共用,节省内存。
二、用 gcc 生成 .a 静态库和 .so 动态库
#mkdir test1 #cd test1
- 用 vim、nano 或 gedit 等文本编辑器编辑生成所需要的 3 个文件:
(这里以vim编辑器为例,分别编译 hello.h、 hello.c、main.c)
具体代码如下:
/*程序 1: hello.h*/
#ifndef HELLO_H
#define HELLO_H
void hello(const char *name);
#endif //HELLO_H
/*程序2:hello.c*/
#include <stdio.h>
void hello(const char *name)
{
printf("Hello %s!\n", name);
}
/*程序3:main.c*/
#include "hello.h"
int main()
{
hello("everyone");
return 0;
}
进入文件后,nano文件名即可进入编辑模式;代码写进之后,“:ctrl+x
”,再回车即可保存并退出文件,同时,可以运用“ls
”命令查看文件是否创建成功。
实操效果截图:
2、将 hello.c 编译成.o 文件
- 无论静态库,还是动态库,都是由
.o 文件
创建的。因此,必须将源程序 hello.c 通过 gcc 先编译成.o 文件
在系统提示符下键入下列命令得到 hello.o 文件:
gcc -c hello.c
由上图结果可知,在 ls 命令结果中,生成了 hello.o 文件
3、由.o文件创建静态库
- 静态库文件名的命名规范是以
lib
为前缀,紧接着跟静态库名,扩展名为.a
。例如:我们将创建的静态库名为 myhello,则静态库文件名就是 libmyhello.a - 创建静态库用
ar 命令
由上图结果可知,在 ls 命令结果中,生成了 libmyhello.a文件
4、在程序中使用静态库
如何使用静态库内部的函数呢?
只需要在使用到这些公用函数的源程序中包含这些公用函数的原型声明,然后用gcc 命令生成目标文件时指明静态库名,gcc 将会从静态库中将公用函数连接到目标文件中。注意,gcc 会在静态库名前加上前缀 lib,然后追加扩展名.a 得到的静态库文件名来查找静态库文件。
在程序 3:main.c 中,我们包含了静态库的头文件 hello.h,然后在主程序 main 中直接调用公用函数 hello。下面先生成目标程序 hello,然后运行 hello 程序看看结果
方法一:
gcc -o hello main.c -L. –lmyhello
(注:自定义的库时,main.c 还可放在-L.和 –lmyhello 之间,但是不能放在它俩之后,否则会提示 myhello 没定义但是是系统的库时,如g++ -o main(-L/usr/lib) -lpthread main.cpp就不出错。)
方法二:
gcc main.c libmyhello.a -o hello
方法三:
先生成 main.o:
gcc -c main.c
再生成可执行文件:
gcc -o hello main.o libmyhello.a
例如用方法二,其实例图如下
-
输入命令运行后,键入
./hello
,返回Hello everyone!
-
删除静态库文件,确定公用函数hello是否真的连接到目标文件hello中:
由上图可以看出,命令运行之后,键入“ls”命令,发现静态库文件libmyhello.a已经被删除,但键入“./hello”后,仍会输出“Hello everyone!”;这说明:程序照常运行,静态库中的公用函数已经连接到目标文件中了
5、由.o文件创建动态库文件
动态库文件名命名规范和静态库文件名命名规范类似,也是在动态库名增加前缀 lib,但其文件扩展名为.so。例如:我们将创建的动态库名为 myhello,则动态库文件名就是 libmyhello.so。
用 gcc 来创建动态库
输入如下命令得到动态库文件libmyhello.so:
gcc -shared -fPIC -o libmyhello.so hello.o (-o 不可少)
6、在程序中使用动态库
- 在程序中使用动态库和使用静态库完全一样,也是在使用到这些公用函数的源程序中包含这些公用函数的原型声明,然后在用 gcc 命令生成目标文件时指明动态库名进行编译。
- 先运行 gcc 命令生成目标文件,再运行它看看结果:
通过上述结果可以发现,第一行命令没有问题,但是当我们键入“./hello”时,却出现了错误提示,由错误提示可知,虽然连接时用的是当前目录的动态库,但程序在运行时,会在/usr/lib 和/lib 等目录中查找需要的动态库文件。若找到,则载入动态库,否则将提示类似上述错误而终止程序运行
mv libmyhello.so /usr/lib
./hello
有图可以看到,当输入第一行命令时,出现报错,无法进行文件的复制,提示为“Permission denied”,即权限不够
- 解决办法:
输入“sudo su”,进入root身份,再键入“su root”,最后输入相关命令:
sudo -s
mv libmyhello.so /usr/lib
7、思考:当静态库和动态库同名时,gcc 命令会使用哪个库文件呢?
- 先删除除.c 和.h 外的所有文件,恢复成刚刚编辑完举例程序状态:
- 并运用“ls”命令进行查看:
rm -f hello hello.o /usr/lib/libmyhello.so
ls
执行命令后得到
- 再来创建静态库文件 libmyhello.a 和动态库文件 libmyhello.so:
gcc -c hello.c
ar -cr libmyhello.a hello.o
gcc -shared -fPIC -o libmyhello.so hello.o
通过上述最后一条 ls 命令,可以发现静态库文件 libmyhello.a 和动态库文件 libmyhello.so 都已经生成,并都在当前目录中
- 运行 gcc 命令来使用函数库 myhello 生成目标文件 hello,并运行程序 hello:
gcc -o hello main.c -L. -lmyhello
./hello
由图可知,结果出现了报错。从该运行的结果中很容易知道,当静态库和动态库同名时,gcc 命令将优先使用动态库,默认去连/usr/lib 和/lib 等目录中的动态库,将文件 libmyhello.so 复制到目录/usr/lib中即可。
三、实例演训
1、作业要求
在第一次作业的程序代码基础进行改编,除了x2x函数之外,再扩展写一个x2y函数(功能自定),main函数代码将调用x2x和x2y ;将这3个函数分别写成单独的3个 .c文件,并用gcc分别编译为3个.o 目标文件
将x2x、x2y目标文件用 ar工具生成1个 .a 静态库文件, 然后用 gcc将 main函数的目标文件与此静态库文件进行链接,生成最终的可执行程序,记录文件的大小
将x2x、x2y目标文件用 ar工具生成1个 .so 动态库文件, 然后用 gcc将 main函数的目标文件与此动态库文件进行链接,生成最终的可执行程序,记录文件的大小,并与之前做对比
2、实操过程
2.1、编辑主程序文件和子程序文件
- 创建一个目录,用来保存此次练习的文件:
mkdir test2
cd test2
//sub1.c
#include<stdio>
float x2x(int a,int b)
{
float x;
x=a+b;
return x;
}
//sub2.c
#include<stdio.h>
float x2y(int h, int k)
{
float m;
m=h-k;
return m;
}
//sub.h
#ifndef SUB_H
#define SUB_H
float x2x(int a,int b);
float x2y(int h,int k);
#endif
//main2.c
#include <stdio.h>
#include"sub.h"
int main()
{
float z,q;
int a=1,b=2,h=5,k=3;
z = x2x(a, b);
q = x2y(h, k);
printf("%f", z);
printf("%f", q);
return 0;
}
2.2、将.c文件编译成.o文件
键入如下命令将三个.c文件编译成.o文件:
gcc -c sub1.c
gcc -c sub2.c
gcc -c main2.c
ls
2.3、将目标文件用 ar工具生成 .a 静态库文件, 再用 gcc进行链接,生成可执行程序
- 将两个目标文件用ar工具生成.a静态库:
ar -crv libsabc.a sub1.o sub2.o
- 用 gcc进行链接,生成可执行程序,并运行之:
gcc -o main2 main2.c libabc.a
./main2
2.4、将目标文件用 ar工具生成 .so 动态库文件, 再用 gcc进行链接,生成可执行程序
- 将两个目标文件用ar工具生成.so动态库,用 gcc进行链接:
gcc -shared -fpic -o libscba.so sub1.c sub2.c
gcc -o sub main2 main2.c libcba.so
./main2
发现出错
这是由于linux自身系统设定的相应的设置的原因,即其只在/lib and /usr/lib下搜索对应的.so文件,故需将对应so文件拷贝到对应路径。
sudo cp libcba.so /usr/lib
2.5、静态库与动态库文件大小比较
用入用来查看静态库与动态库文件大小
ls -lh
- 由图可知,静态库生成文件比动态库要小得多
总结
本文简单介绍了在Ubuntu18.04系统下,gcc生成静态库.a、动态库.so的详细过程与使用方法,同时又通过一个具体的作业实例进行演训,又进一步加深对这部分知识的理解与掌握。模仿做题的过程比较简单,但是需要耐心和细心,出现错误,找到办法,多尝试尝试一下网上其他的一些文献资料推荐的解决办法,对你进一步掌握要点是有帮助的。