Linux用 gcc 生成 .a 静态库和 .so 动态库


前言

本文主要通过举例来说明在 Linux 中如何创建静态库和动态库,以及使用它们。

我们通常把一些公用函数制作成函数库,供其它程序使用。函数库分为静态库动态库两种。

静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库。

动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在。


一、以hello实例程序为例说明静态库和动态库

1.准备源程序

(1)在Ubuntu中新创建一个文件夹 test1
(2)编辑生成例子程序 hello.h、hello.c 和 main.c。

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;
}

2.将hello.c编译成hello.o文件

无论静态库,还是动态库,都是由.o 文件创建的。因此,我们必须将源程序 hello.c 通过 gcc 先编译成**.o** 文件。在系统提示符下键入以下命令得到 hello.o 文件。

gcc -c hello.c

运行 ls 命令看看是否生成了 hello.o 文件
在这里插入图片描述
在 ls 命令结果中,我们看到了 hello.o 文件,本步操作完成。
下面我们先来看看如何创建静态库,以及使用它。

3.由.o文件创建静态库

静态库文件名的命名规范是以 lib 为前缀,紧接着跟静态库名,扩展为 .a

例如:我们将创建的静态库名为 myhello ,则静态库文件名就是 libmyhello.a 。在创建和使用静态库时,需要注意到这点。创建静态库时使用 ar 命令。在系统提示符下键入以下命令将创建静态库文件 libmyhello.a

ar -crv libmyhello.a hello.o

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 中了。

删除静态库文件:

rm libmyhello.a
在这里插入图片描述

再次运行:

./hello
在这里插入图片描述

程序照常运行,静态库中的公用函数已经连接到目标文件中了。
我们继续看看如何在 Linux 中创建动态库。我们还是从.o 文件开始。

5.由.o 文件创建动态库文件

动态库文件名命名规范和静态库文件名命名规范类似,也是在动态库名增加前缀 lib,但其文件扩展名为 .so
例如:我们将创建的动态库名为 myhello,则动态库文件名就是 libmyh
ello.so。用 gcc 来创建动态库。
在系统提示符下键入以下命令得到动态库文件 libmyhello.so。

gcc -shared -fPIC -o libmyhello.so hello.o (-o 不可少)

照样使用ls命令查看动态库文件是否生成:
在这里插入图片描述

6.在程序中使用动态库

在程序中使用动态库和使用静态库完全一样,也是在使用到这些公用函数的源程序中包含这些公用函数的原型声明,然后在用 gcc 命令生成目标文件时指明动态库名进行编译。

我们先运行 gcc 命令生成目标文件,再运行它看看结果。
使用命令:

gcc -o hello main.c -L. -lmyhello

接下来./hello 会提示出错,原因是找不到动态库文件 libmyhello.so,因为虽然连接时用的是当前目录的动态库,但是运行时,程序是到/usr/lib 中找库文件的,若找到,则载入动态库,否则将提示类似错误而终止程序运行。因此将文件 libmyhello.so 复制到目录/usr/lib 中就 OK 了
在这里插入图片描述

使用下列命令将libmyhello.so移动到 /user/lib中

mv libmyhello.so /usr/lib
在这里插入图片描述
报错:提示没有权限。

开启管理员权限,输入命令:

sudo cp -i libmyhello.so /usr/lib

运行结果,此时成功执行:
在这里插入图片描述

7.静动态库同名时,gcc命令调用库文件情况

上步运行成功了。这也进一步说明了动态库在程序运行时是需要的。
我们回过头看看,发现使用静态库和使用动态库编译成目标程序使用的 gcc 命令完全一样,那当静态库和动态库同名时,gcc 命令会使用哪个库文件呢?抱着对问题必究到底的心情,来试试看。

先删除除.c 和.h 外的所有文件,恢复成我们刚刚编辑完举例程序状态。

(1)输入命令行sudo rm -f hello hello.o /usr/lib/libmyhello.so删除.c和.h外的所有文件,恢复成刚刚编辑完举例程序状态
(2)输入命令ls查看是否有hellohello.o文档)
(3)再来创建静态库文件 libmyhello.a 和动态库文件 libmyhello.so
在这里插入图片描述

通过最后一条 ls 命令,可以发现静态库文件 libmyhello.a 和动态库文件 libmyhello.so 都已经生成,并都在当前目录中。然后,我们运行 gcc 命令(gcc -o hello main.c -L. –lmyhello)来使用函数库 myhello 生成目标文件 hello,并运行程序 hello。
在这里插入图片描述

从程序 hello 运行的结果中很容易知道,当静态库和动态库同名时,gcc 命令将优先使用动态库,默认去连/usr/lib 和/lib 等目录中的动态库,将文件 libmyhello.so 复制到目录/usr/lib中即可。

二、Linux下静态库和动态库的生成和使用

一共有三个.c文件和两个头文件.h,第一个子程序sub1.c包含一个算数运算函数float x2x(int a,int b),该函数功能是对两个输入整型参数做减法运算,将结果作为浮点数返回;第二个子程序add1.c包含一个算数运算函数 float x2y(int a,int b),该函数功能是对两个输入整型参数做加法运算,将结果浮点数返回;主程序main.c将调用x2x和x2y

要求:将这3个函数分别写成单独的3个 .c文件,并用gcc分别编译为3个.o 目标文件;将x2x、x2y目标文件用 ar工具生成1个 .a 静态库文件, 然后用 gcc将 main函数的目标文件与此静态库文件进行链接,生成最终的可执行程序,记录文件的大小。

1.编写代码

创建所有文件:
在这里插入图片描述

sub1.c和sub1.h文件代码:


add1.c和add1.h文件代码:


main.c文件代码:

2.静态库.a文件的生成和使用

(1)将sub1.c和add1.c文件编译成.o文件

gcc -c sub1.c add1.c

在这里插入图片描述

(2)生成静态库.a文件

ar crv libfile.a sub1.o add1.o

在这里插入图片描述

(3)使用.a库文件创建可执行程序并运行

gcc -o main main.c libfile.a

在这里插入图片描述

3.动态库.so文件的生成与使用

(1) 生成共享库.so文件

gcc -shared -fPIC -o libfile.so sub1.o add1.o

在这里插入图片描述

(2)使用共享库文件创建可执行程序
同样再运行./main是需要先将动态库文件移到/usr/lib目录下,参考前文讲解即可
在这里插入图片描述

4.静态库与动态库的生成文件的大小比较

(1)ll命令讲解
在控制台输入命令ll,会显示如下内容:
在这里插入图片描述

ll命令是Linux系统中常用的一个命令,它用于显示当前目录下的文件和文件夹的详细信息。ll命令实际上是ls—I命令的别名,它会以列表的形式显示文件的权限、所有者、所属组、大小、创建时间等信息。使用Il命令可以方便地查看文件的属性和状态,对于管理文件和文件夹非常有用。

1.文件类型
在这里插入图片描述
表示该文件的类型:

“-”表示普通文件;
“d”表示目录;
“l”表示链接文件;
“p”表示管理文件;
“b”表示块设备文件;
“c”表示字符设备文件;
“s”表示套接字文件;

2.文件属性
在这里插入图片描述

第一段表示文件创建者/所有者对该文件所具有的权限,第二段表示创建者/所有者所在的组的其他用户所具有的权限,第三段表示其他组的其他用户所具有的权限。

r(Read,读取权限):对文件而言,具有读取文件内容的权限;对目录来说,具有浏览目录的权限。
w(Write,写入权限):对文件而言,具有新增、修改文件内容的权限;对目录来说,具有删除、移动目录内文件的权限。
x(eXecute,执行权限):对文件而言,具有执行文件的权限;对目录来说,该用户具有进入目录的权限。
另外,这里还有2个很特殊的属性,平时不怎么常见,这里也顺带解释一下:

s或S(SUID,Set UID):可执行的文件搭配这个权限,便能得到特权,任意存取该文件的所有者能使用的全部系统资源。请注意具备SUID权限的文件,黑客经常利用这种权限,以SUID配上root帐号拥有者,无声无息地在系统中开扇后门,供日后进出使用。
t或T(Sticky):/tmp和 /var/tmp目录供所有用户暂时存取文件,亦即每位用户皆拥有完整的权限进入该目录,去浏览、删除和移动文件。
综合起来可得,对于back_init文件,其创建者/所有者具有可读可写可执行的权限,其创建者/所有者所在的组的其他用户具有可读可写可执行的权限,其他组的其他用户则具有可读可执行但不可写的权限。

3.目录/链接个数
在这里插入图片描述
对于目录文件,表示它的第一级子目录的个数。注意此处看到的值要减2才等于该目录下的子目录的实际个数。

比如这里的include目录下,其实是没有子目录的,所以应该是0,但是它这里却显示2,这是因为要加上.目录和…目录。在linux下,.目录表示当前目录,…目录表示上一级目录。

这也可以解释上图中第一行的.目录下的2和第二行…目录下的4。因为当前目录下没有子目录,所以加上.目录和…目录这2个目录就等于2,所以第一行会显示2。而上一级目录共有2个子目录,加上上一级目录的.目录和…目录这2个目录,所以这里的第二行显示的是4。

对于其他文件,表示指向它的链接文件的个数。

例如:
当前./目录,无子目录
在这里插入图片描述
当前目录的上一级…/目录,有两个子目录

在这里插入图片描述

4.所有者及组
在这里插入图片描述
表示该文件的所有者/创建者(owner)及其所在的组(group)。

5.文件大小
在这里插入图片描述
如果是文件,则表示该文件的大小,单位为字节。
如果是目录,则表示该目录符所占的大小,并不表示该目录下所有文件的大小。

6.修改日期
在这里插入图片描述
该文件最后修改的日期时间。

7.文件名称
在这里插入图片描述
8.字体颜色

在大多数的linux shell窗口中,还能用颜色来区分不同文件的属性:

灰白色表示普通文件;
亮绿色表示可执行文件;
亮红色表示压缩文件;
灰蓝色表示目录;
亮蓝色表示链接文件;
亮黄色表示设备文件;
当然,这里需要使用系统缺省的配色方案。如果你自定义了shell的配色方案,则有可能与上面的定义不一致。

最后还要说明一点的是,可以看到上述的图片中,main文件和libtest.sow文件的后面还带了一个星号(),这也是linux系统下用于标记可执行文件的另外一种方式。也就是说,凡是文件名后面带了一个星号()的,都是在说明这是一个可执行文件。

(2)比较文件
在这里插入图片描述
可见 libtest.a文件比libfile.a文件大的多,即动态库要比静态库大。

原因:
(1)静态库和动态库都是对目标文件的处理,也可以说库文件已经是机器码文件
(2)静态库的代码在编译过程中已经被载入可执行程序,因此体积较大。
共享库的代码是在可执行程序运行时才载入内存的,在编译过程中仅简单的引用,因此代码体积较小。
(3)静态库是牺牲了空间效率,换取了时间效率,共享库是牺牲了时间效率换取了空间效率,因此各有优缺点。


总结

通过用gcc生成静态库和动态库的练习过程,基本上能够熟练的生成静态库和动态库。在两种库的比较中,能够明显看出两者的差别。虽然,过程中,遇到一些小问题,但是很快就解决了。只要慢慢多练几遍,便很快能够掌握。可执行文件是通过编译链接获取得到的,利用工具将源码编译得到.o文件,接下来就是将.o文件链接得到可执行文件。

参考博客

gcc生成静态库.a和动态库.so

linux命令 ll信息详解

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

菲菲QAQ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值