C静态库的创建与使用--为什么要引入静态库?

这篇具有很好参考价值的文章主要介绍了C静态库的创建与使用--为什么要引入静态库?。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

C源程序需要经过预处理、编译、汇编几个阶段,得到各自源文件对应的可重定位目标文件,可重定位目标文件就是各个源文件的二进制机器代码,一般是.o格式。比如:util1.c、util2.c及main.c三个C源文件,经过预处理器、编译器、汇编器的处理,就可以得到各自的目标文件util1.o,util2.o以及main.o。可重定位目标文件中的地址是从0开始的,需要链接器将若干个可重定位目标文件通过符号解析重定位等工作,链接成为一个可执行的二进制目标文件。在Linux下,可以使用gcc -c 对源文件进行预处理、编译、汇编,得到目标文件:

C静态库的创建与使用--为什么要引入静态库?

 可以看到源文件util1.c及util2.c被编译成为了对应的目标文件util1.o及util2.o。在给定的例子中,util1.c和util2.c实际上分别定义了两个函数add和mult,返回两个整数的加法和乘法结果(这么做有点儿蠢,这里只是作为一个例子,讲清楚后面静态库的概念)。两个函数的定义如下:

//util1.c
int add(int a,int b)
{
    return a + b;
}

//util2.c
int mult(int a,int b)
{
    return a * b;
}

util.h中包含了对这两个函数的声明。  main.c使用其中的add函数:

#include <stdio.h>
#include "util.h"

int main()
{
    int a = 5;
    int b = 10;
    int c = add(a,b);
    printf("%d\n",c);
    return 0;
}

实际上,所有的编译系统都提供一种机制,将所有相关的目标模块(即目标文件)打包成为一个单独的文件,称为静态库。在Linux中,静态库以一种被称为存档(archive)的文件格式存放在磁盘中。存档文件由后缀.a标识,.a格式的存档文件是一组连接起来的可重定位目标文件的集合,有一个头部用来描述每个成员目标文件的大小和位置。C标准定义了许多静态库,如标准IO操作scanf,printf,字符串操作strcpy等,它们在libc.a库中;一些浮点数学函数如sin,cos等,它们在libm.a库中。

当然,静态库是目标文件的集合,我们也可以将自己定义的函数编译成目标代码,加入静态库中。为了为若干目标文件创建静态库,可以使用ar rcs:

C静态库的创建与使用--为什么要引入静态库?

 ar rcs 后面紧跟的libutil.a是创建的静态库的名字,通常以lib三个字母开头,后面的util可以自己指定,静态库以.a为后缀。util1.o 及 util2.o 是我们要加入静态库的两个目标文件。这样,就创建了一个静态库文件libutil.a。可以使用ar t来查看静态库文件中包含的目标文件:

C静态库的创建与使用--为什么要引入静态库?

接下来,我们在main函数中使用这个库。要在main中使用libutil.a库,需要链接通过编译main.c得到的目标文件main.o和libutil.a:

C静态库的创建与使用--为什么要引入静态库?

 可以看到,gcc将main.c对应的目标文件与库libutil.a链接起来,得到了可执行文件main。我们执行可执行文件main,得到期望的结果:

C静态库的创建与使用--为什么要引入静态库?

 注意,main函数中include了头文件util.h,在util.h中对libutil.a中的函数进行了声明。

那么,重点来了,为什么需要引入静态库这种东西呢?将C标准提供的所有库都放在一个可重定位目标模块中不行吗?

事实上是可以的,不过,这种设计有一个很大的缺点是系统中的每个可执行文件都要包含这个整个的大的目标模块的完全副本,这样做很浪费存储空间。比如,C标准的libc.a大约5MB,现在有一台机器装载了15个用到了C标准库的可执行文件,那么这15个可执行文件里每一个实际上都经过链接器的链接,嵌入了libc.a库中的5MB目标代码,而实际上它们可能用到5MB目标代码里的很小一部分(比如,某个目标文件可能只引用了标准库中的strcpy函数),这样,造成了严重的存储空间浪费。而静态库实际上提供了这样一种功能:相关的函数可以被编译为独立的目标模块,然后封装成一个单独的静态库文件,当链接器构造一个可执行文件时,它只“提取”静态库里被应用程序引用的目标模块(换句话说,对于程序中用不到的,链接器不会将它复制到可执行文件中去),比如例子中main.c只用到了add函数,链接器就只会将库libutil.a中的multi1模块复制到可执行文件,而不会复制multi2模块。

还有一种方法,就是把每个函数创建独立的可重定位目标文件。而这种方法对于应用程序员来说是极其不友好的,因为这种方法要求应用程序员显示地链接需要的目标模块到可执行文件中,这是一个容易出错且耗时的过程。

总结来说,静态库提供了将每一个目标模块独立地打包的功能,并且可以由链接器自动地提取被程序引用的目标模块,这减少了可执行文件在磁盘和内存中的大小,并且大大降低了程序员链接各个目标文件的压力。

 

注意:若目标代码引用了一个静态库,比如,假设libx.a引用了liby.a和libz.a中的模块,那么,我们必须在命令行中将libx.a放在liby.a和libz.a之前:

gcc main.c libx.a libz.a liby.a

这个要求是必须的。如果将libx.a 放在了它们任意一个之后,比如:

gcc main.c libz.a libx.a liby.a

将导致链接时错误:链接器要求如果目标代码(可重定位目标模块或静态库目标模块)引用了静态库(不包括可重定位目标模块)中的模块,那么在命令行中必须把引用者放在前面,被引用者放在后面。详细原因与链接器的工作过程有关,这里不做赘述。

 

 博主会持续更新精心原创文章,在逻辑严瑾的同时,力求最通俗易懂地把知识写得清楚,写得明白。觉得博主写得还可以的小伙伴儿点点关注,点点赞啦~

 有不足的地方,也请各路大神批评指正,感谢大家的支持~文章来源地址https://www.toymoban.com/news/detail-710581.html

到了这里,关于C静态库的创建与使用--为什么要引入静态库?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 为什么HTTP/3要引入UDP?(快速了解QUIC)

    笔者前段时间参加银行技术面时被问到了这个问题,特来整理资料以供记录分享 HTTP/3是HTTP协议的最新版本,它的诞生是为了解决HTTP/1和HTTP/2在性能和效率上的问题。在HTTP/3之前,HTTP协议使用的是TCP作为传输层协议。然而,随着互联网的发展,TCP的性能瓶颈逐渐显现出来。为

    2024年01月17日
    浏览(40)
  • 分布式 - 谈谈你对分布式的理解,为什么引入分布式?

    不啰嗦,我们直接开始! 真正了解分布式系统的概念,日后工作中具有分布式系统设计思想。 能否在设计中对系统稳定性方面考虑周全。 能构建高 QPS 健壮的系统架构。 问题分析: 各种分布式框架层出不穷,Spring Cloud,阿里的 Dubbo,无论使用哪一个,原理都相同,考察下基

    2024年02月15日
    浏览(34)
  • facebook多账号运营为什么要用静态住宅ip代理?

    在进行Facebook群控时,ip地址的管理是非常重要的,因为Facebook通常会检测ip地址的使用情况,如果发现有异常的使用行为,比如从同一个ip地址频繁进行登录、发布内容或者在短时间内进行大量的活动等等,就会视为垃圾邮件或者恶意行为,导致账户被禁用或者限制。 因此,

    2024年02月21日
    浏览(37)
  • web中为什么要引入service层以及前端控制器DispatchServlet的作用以及原理剖析

    review: 最初的做法是: 一个请求对应一个Servlet,这样存在的问题是servlet太多了 把一些列的请求都对应一个Servlet, IndexServlet/AddServlet/EditServlet/DelServlet/UpdateServlet - 合并成FruitServlet 通过一个operate的值来决定调用FruitServlet中的哪一个方法 使用的是switch-case 在上一个版本中,Ser

    2024年02月04日
    浏览(29)
  • MySQL为什么选择B+树创建索引

    将磁盘中存储的所有数据记录依次加载,与给定条件对比,直到找到目标记录; 类比数组结构的线性查找,效率较低; 结合数组和链表结构(或者树结构)存储数据; 通过哈希函数(散列函数)计算哈希地址,相同输入在固定函数下输出保持不变; 哈希结构会发生哈希冲突

    2024年02月13日
    浏览(36)
  • 小米随身wifi为什么创建不了?小米wifi创建失败解决方法

    小米随身wifi为什么创建不了?现在很多朋友在使用小米随身wifi的过程中遇到了创建失败的情况,那么怎么办呢?下面小编就为大家介绍小米wifi创建失败的解决方法,一起来看看吧! 解决小米wifi创建失败方法步骤: 第一步、鼠标移至“我的电脑”右键打开“管理”。 第二步

    2024年02月07日
    浏览(37)
  • 为什么创建 Redis 集群时会自动错开主从节点?

    哈喽大家好,我是咸鱼 在《一台服务器上部署 Redis 伪集群》这篇文章中,咸鱼在创建 Redis 集群时并没有明确指定哪个 Redis 实例将担任 master,哪个将担任 slave 然而 Redis 却自动完成了主从节点的分配工作 如果大家在多台服务器部署过 Redis 集群的话,比如说在三台机器上部署

    2024年02月10日
    浏览(24)
  • pycharm创建的虚拟环境为什么用conda env list命令查询不到?

    问题描述:pycharm创建的虚拟环境为什么用conda env list命令查询不到。 pycharm开发环境可以创建虚拟环境,目的是为隔绝其他环境种库带来的版本干扰,但是发现一个问题,无论是在windows终端、anaconda终端、Pycharm开发环境中的终端使用conda env list命令都查不到venv环境。

    2024年02月10日
    浏览(39)
  • C静态库的创建与使用详解

    C源程序需要经过预处理、编译、汇编几个阶段,得到各自源文件对应的 可重定位目标文件 ,可重定位目标文件就是各个源文件的二进制机器代码,一般是.o格式。比如:util1.c、util2.c及main.c三个C源文件,经过预处理器、编译器、汇编器的处理,就可以得到各自的目标文件u

    2024年02月08日
    浏览(25)
  • tensorflow-gpu安装100%成功(tensorflow-gpu版和tensorflow-cpu版的区别、为什么要创建虚拟环境、如何同时使用两个gpu库、tensorflow-gpu版安装)

    1.tensorflow-gpu版和tensorflow-cpu版的区别 tensorflow-gpu版需要同时配置安装CUDA、cuDNN,而tensorflow-cpu版不需要配置,直接 pip/conda install tensorflow 即可安装tensorflow-cpu版本 2.为什么要创建虚拟环境 在安装gpu版本的库时通常会创建单独的虚拟环境,例如安装tensorflow-gpu,则需要利用 cond

    2024年02月08日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包