动静态库的制作
文章目录:
- 什么是程序库?
- 动态链接和静态链接
- 动静态库的认识
- 静态库的创建与使用
- 创建
- 使用
- 动态库的创建与使用
- 创建
- 使用
什么是程序库?
程序库:一般是软件作者为了发布方便、替换方便或二次开发目的,而发布的一组可以单独与应用程序进行 compile time 或 runtime 链接的二进制可重定位目标码文件。通俗来讲,一个库就是一个文件,这个文件可在编译时由编译器直接链接到可执行程序中,也可以在运行时由操作系统的 runtime environment 根据需要动态加载到内存中。
实际上,每个程序都需要依赖很多基础的底层库,因此库的存在是非常中重要的。库有两种:
- 静态库(.a、.lib):程序在编译链接的时候把库的代码链接到可执行程序中。程序运行时将不再需要静态库。
- 动态库(.so、.dll):程序在执行的时候才去链接动态库的代码,多个程序共享动态库的代码。

库命名约定: 库通常以前缀 “lib” 命名。对于所有的C标准库都是如此。在连接时,对库的命令引用将不包含库的前缀或后缀。
动态链接和静态链接
- 动态链接:在可执文件开始运行之前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程就称为动态链接(dynamic linking)。

动态库存储在磁盘中,它可以由很多个进程共享,所以动态链接使得可执行文件更小。若某个程序需要使用某个动态库时,只需要在该进程的进程地址空间的共享区开辟空间,然后通过该进程的页表将物理内存中的动态库映射到该进行的虚拟内存中。
- 静态链接:可执行程序编译链接时,将程序中使用到的静态库的代码拷贝到该可执行程序中。
动静态库的认识
如下所示,在 Linux 下编写一个简单的程序,接下来我们将用该程序来认识一下动静态库。

在该程序中我们通过调用 printf 来输出目标字符串,而 printf 是库函数。因此,在使用 gcc 编译此程序时,将C标准库也链接进来了。
在 Linux 下,可以通过指令 ldd 可执行程序名 来查看一个可执行程序所依赖的库文件:

从上图可看出,libc-2.17.so 实际上就是一个动态库。在 Linux 下,.so 为后缀的是动态库,.a 为后缀的是静态库。
gcc/g++ 编译器默认生成的二进制程序都是动态链接的,如果想要实现静态链接,可以在使用 gcc/g++ 编译文件时加上 -static 选项。

采用静态链接生成的可执行程序不依赖其它库文件,使用指令 ldd 可执行程序名 可以查看该可执行程序所依赖的库文件,如下所示 :

静态库的特点:
- 静态库对库函数的链接是放在编译时期完成的。
- 程序在运行时将与函数库无任何联系,便于移植。
- 静态链接生成的可执行程序非常大,浪费空间和资源,因为所有相关的目标文件与牵涉到的函数库将被链接形成一个可执行文件。
动态库的特点:
- 动态库将一些库函数的链接载入推迟到程序运行的时期。
- 可以实现进程之间的资源共享。(因此动态库也称为共享库)
- 将一些程序升级变得简单。
- 可以真正做到链接载入完全由程序员在程序代码中控制(显示调用)。
静态库的创建与使用
创建
静态链接库其实就相当于压缩包,其内部可以包含多个源文件。需要注意的是,并不是任意一个源文件都可以被加工成静态链接库,其至少需要满足以下两个条件:
- 源文件中只提供可重复使用的代码,如:函数、设计好的类等,其中不能包含 main 函数;
- 源文件在实现具备模块功能的同时,还需要提供访问它的接口,也就是各个功能模块声明部分的头文件;
接下来,我们将演示如何创建一个静态库,以下面四个文件为例,其中两个源文件 calc.c 和 Print.c ,两个头文件 calc.h 和 Pint.h :


1、接下来先编译所有的源文件生成对应的目标文件:

2、一旦我们有了一个目标文件(或多个文件),就使用 GNU ar 命令来将所有的目标文件创建成最终的库:
ar 命令是 GNU 的归档工具,常用于将目标文件打包为静态库,下面我们将使用 ar -rc 命令来对目标文件进行打包。
-rreplace :在库中插入模块(替换)。当插入的模块名已经在库中存在,则替换同名的模块。如果若干模块中有一个模块在库中不存在,ar 显示一个错误信息,并不替换其它同名模块。默认情况下,新的成员增加在库的结尾处,可以使其它任选项来改变增加的位置。-ccreate : 创建一个库。不管库是否存在,都将创建。
ar -rc libcalc.a calc.o Print.o

我们可以使用 ar -tv 来查看静态库中包含的文件:
ar -tv libcalc.a

3、将头文件和生成的静态库组织起来:
当我们将自己的静态库给别人使用时,实际上需要给出两个文件夹,一个文件夹下面存储静态库中的所有头文件,另一个文件夹下存储所有的库文件。
创建一个目录 mathlib ,在该目录下创建 include 和 lib 目录,将 calc.h 和 Print.h 这两个头文件放到 include 目录下,将生成的静态库文件 libcalc.a 放到 lib 目录下,然后就可以将 mathlib 给别人用了。

使用 makefile 将以上步骤组织起来,形成 makefile 文件。
libmath.a:calc.o Print.oar -rc libmath.a calc.o Print.ocalc.o:calc.cgcc -c calc.c -o calc.o -std=c99
Print.o:Print.cgcc -c Print.c -o Print.o -std=c99 .PHONY:output
output:mkdir -p lib-static/lib mkdir -p lib-static/includecp *.a lib-static/lib cp *.h lib-static/include.PHONY:clean
clean:rm -rf *.o *.a lib-static
写好 makefile 以后,我们就可以将静态库进行一键发布。首先使用 make 生成所有目标文件对应的源文件,然后 make output 将这些目标文件与静态库文件组织起来:

使用
Linux 下使用静态库,只需要在编译的时候,指定静态库的搜索路径(-L选项)、指定静态库名(不需要 lib 前缀和 .a 后缀,-l选项)、指定头文件的搜索路径(-I选项)。
- -
L:表明静态库的搜索路径 - -
l:指定链接时需要的库,编译器查找库时由隐含的命令规则,即在给出的i那个字前面加上 lib,后面加上 .a 或 .so 来确定库的名称。 - -
I:指定头文件的搜索路径
gcc test.c -I./lib-static/include -L./lib-static/lib -lmath

注意:-I,-L,-l 这三个选项后面可加空格,也可以将空格省略掉。
另一种方法:将头文件和库文件拷贝到存储系统头文件和系统库文件的路径下。
sudo cp lib-static/include/* /usr/include/
sudo cp lib-static/lib/* /lib64/
这个就不演示了。该方法采用的是直接将我们写好的库的头文件和库文件直接拷贝到系统路径下,虽然该方法比较简单,但是不推荐使用此方法,因为这样会对系统文件造成污染。
动态库的创建与使用
创建
共享库或动态链接库(dll)具有在多个程序之间共享一个库副本的巨大优势,因此称为共享库,将它们与多个程序链接的过程称为动态链接。接下来将演示如果在 Linux 上创建和使用共享库。
1、让所有源文件生成对应的目标文件:
在这里用源文件生成对应的目标文件时需要携带选项 fPIC,-fPIC 作用于编译阶段,告诉编译器产生与位置无关代码(Position-Independent Code),而产生的代码中,没有绝对地址,全部使用相对地址,因此代码可以被加载器加载到内存的任意位置,都可以正确的执行。则共享库被加载时,在内存的位置不是固定的。
gcc -fPIC -c Print.c -o Print_so.c -std=c99
gcc -fPIC -c Print.c

2、使用 -shared 选项将所有目标文件打包为动态库
- -shared :生成一个共享对象,该对象可以与其它对象链接以形成可执行文件
gcc -shared -o libcalc.so Print_so.o calc_so.o

3、将头文件和生成的动态库组织起来:
创建一个目录 lib_dyl ,在该目录下创建 include 和 lib 目录,将 calc.h 和 Print.h 这两个头文件放到 include 目录下,将生成的动态库文件 libcalc.so 放到 lib 目录下,然后就可以将 lib_dyl 给别人用了。

使用 makefile 将以上步骤组织起来,形成 makefile 文件。
libmath.so:calc_so.o Print_so.ogcc -shared -o libcalc.so calc_so.o Print_so.o
calc_so.o:calc.cgcc -fPIC -c calc.c -o calc_so.o -std=c99
Print_so.o:Print.cgcc -fPIC -c Print.c -o Print_so.o -std=c99 .PHONY:output
output:mkdir -p lib_dyl/lib mkdir -p lib_dyl/includecp *.so lib_dyl/lib cp *.h lib_dyl/include.PHONY:clean
clean:rm -rf *.o *.so lib_dyl
写好 makefile 以后,我们就可以将动态库进行一键发布。首先使用 make 生成所有目标文件对应的源文件,然后 make output 将这些目标文件与动态库文件组织起来:

使用
我们将动态库和测试代码拷贝到一个新的目录,接下来进行测试:

引用动态库编译成可执行文件(与静态库的方式一样),在使用动态库时也需要加路径,也需要使用 -l、-I、-L 这三个选项来生成可执行文件。

接下来运行:./test ,发现竟然报错了!!!生成的可执行程序不能正常运行。

那么猜测可能的原因,是因为动态库与测试程序不在同一个目录下,接下来进行验证:

经过测试后发现,动态库可以正常链接,可执行程序执行成功!但是,在实际中,动态库一般不会和我们自己的可执行在同一路径下。因此,该方法不实用。
这里需要注意一下,使用 -I、-l、-L 选项是让编译器能够找到我们使用的头文件和库文件所在位置,但是使用 gcc/g++ 生成可执行程序后,生成的可执行程序就和编译器没有关系了。当可执行程序运行时依旧找不到该可执行程序所依赖的库。
下面,将介绍四种方法来解决此问题。
1️⃣ :拷贝 .so 文件到系统共享库路径下,一般指 /usr/bin(不推荐此做法,拷贝库文件到系统库路径下会污染库)
sudo cp lib_dyl/include/* /usr/include/
sudo cp lib_dyl/lib/* /lib64/

上面只是一个演示,若不想将自己的库文件留着系统的库文件中,可以将它进行删除。

2️⃣:更改 LD_LIBRARY_PATH(配置完成之后,退出之后再次查看,)
LD_LIBRARY_PATH 是 Linux 系统下的环境变量名,类似于 PATH。它用于指定查找共享库(动态链接库)时除了默认路径(./lib 和 ./usr/lib)之外的其它路径。
使用场景:移植程序时经常需要使用一些特定的动态库,而这些编译好的动态库放在自己建立的目录中,这时可以将这些目录设置到 LD_LIBRARY_PATH 中。
在 Linux 下可以使用 export 命令来设置这个值:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/hyr/linux_code/linux17/uselib_dyl/lib_dyl/lib

export 导入变量后在重启时会失效。可以在 ~/.bashrc 或者 ~/.bash_profile 中添加 export 语句,前者在每次登录和每次打开 shell 都会读取一次,后者只在登陆时读取一次。
例如:在 ~/.bashrc 文件末尾添加我们动态库所在的路径,如下所示:

在添加保存之后,需要关闭当前的终端并重新打开一个新的终端,从而使上面的配置失效。
3️⃣ :配置 /etc/ld.so.conf.d/,配置完成之后使用 ldconfig 更新,使配置生效
可以通过配置 /etc/ld.so.conf.d/ 来解决此问题,该路径下存储的全是以 .conf 为后缀的文件。这些文件中存储的都是一些路径,系统会自动在该路径下查找所有配置文件里面的路径,然后在每一个路径下查找是否有你需要的库,若查找到了,则你的程序可以正确的链接到动态库了。

以下演示一下操作:

4️⃣:使用软链接将路径指向库
我们使用自己创建的动态链接与系统库建立软链接,这样就可以使可执行程序正确链接了:

可以使用 unlink 来取消软链接关系,如下所示:

相关文章:
动静态库的制作
文章目录:什么是程序库?动态链接和静态链接动静态库的认识静态库的创建与使用创建使用动态库的创建与使用创建使用什么是程序库? 程序库:一般是软件作者为了发布方便、替换方便或二次开发目的,而发布的一组可以单独与应…...
QMS-云质-质量软件-客诉,为什么应该用两段式来处理
-云质QMS原创文章,转载请注明来源- 客户满意度是决定企业是否能够基业长青的关键因素之一。 如果客诉处理的不好,会极大影响客户的满意程度。 通常处理客诉分为两个阶段。 第一个阶段是快反遏制,想方设法快速答复和解决客户提出的问题&…...
JS:关于邮箱的正则表达式及规则
常用正则表达式—邮箱(Email) 要验证一个字符串是否为邮箱的话,首先要了解邮箱账号的格式。我尝试过在网上找出一个标准的格式,但是很遗憾我没有找到。我也尝试使用RFC标准来判断邮箱的格式,但是也没有结果。网上些博…...
两句话,ChatGPT帮我写一个打飞机的游戏
大家好,我是全村的希望今天的主题是让 chatGPT 来帮我们写一个打飞机的游戏记得我刚学 Python 的时候,看的那本很经典的入门书《Python 编程:从入门到实践》,里面就有小项目就是教你编写一个打飞机的游戏我那时候是对着书一个一个…...
计算机图形学14:三维图形的投影变换
作者:非妃是公主 专栏:《计算机图形学》 博客地址:https://blog.csdn.net/myf_666 个性签:顺境不惰,逆境不馁,以心制境,万事可成。——曾国藩 文章目录专栏推荐专栏系列文章序一、三维图形的投…...
【ChatGPT4】王老师零基础《NLP》(自然语言处理)第二课
我的已经在起、点开了书《王老师带我成为救世主》,那个更新及时 (1)---------------------------------------------------------------------------------------- 我: 1我/喜欢/吃/苹果,因为/它/们/很/好吃。 2 Th…...
设计模式之中介者模式在前端的应用
文章目录中介者模式在前端的应用场景1.实现组件之间的松耦合2. 实现异步请求的协同3. 实现事件驱动的编程模型4. 实现复杂交互的协调总结中介者模式在前端的应用场景 中介者模式是一种常见的设计模式,它可以将对象之间的通信集中处理,从而提高系统的可维…...
2023年还能入行程序员吗?工作3年以上的黑马老学员怎么说?
很多人觉得,毕业3年,不过是毕业第1年的重复,键盘Ctrl、C和V键磨损更严重了。妥妥属于光涨年龄,不涨经验;只涨体重,不涨工资…… 他们不理解,为什么同样的起跑线,有人发展神速&#…...
接收机的噪声来源与噪声分析
噪声分类 射频接收机中的噪声主要可以分为两类:内部噪声和外部噪声。 内部噪声 内部噪声主要来自于接收机内部的放大器、混频器、本振等元件所产生的噪声。根据不同的产生机制,内部噪声可以分为以下几类: a. 电感噪声:由于电感…...
Android FrameWork——SystemServer
Android系统在启动的时候有两个非常重要的进程,一个是Zygote,另一个就是system_server。SystemServer是系统用来启动service的入口,比如我们常用的AMS,WMS,PMS等等都是由它创建的。 system_server进程的启动 system_…...
婴儿推车ASTMF883测试
1.cpc认证是总称,cpc认证下边有很多的标准,常见的有ASTM F963(铅含量)、CPSIA(邻苯8P)、ASTM F833(婴儿车)等; 2.婴儿车ASTM认证是什么 2019年8月2日,美国消…...
射频接收机概述
接收机架构 射频接收机架构是指电子设备中用于接收无线电信号的部分。它通常由前置放大器、中频放大器、混频器、局部振荡器和带通滤波器等组成。以下是一个基本的射频接收机架构: 前置放大器:前置放大器的作用是放大接收天线接收到的微弱无线电信号&am…...
实验三Numpy知识点总结
熟悉和使用NumPy模块 import numpy as np一、完成下列数组操作与运算。 (1)创建2行4列的数组arr_a,数组中的元素为0至7,要求用arange()函数创建。 arr_anp.arange(8) arr_a.resize(2,4) print(arr_a)[[0 1 2 3][4 5 6 7]]&…...
Code Review时学到的技巧之isAssignableFrom
🍊 Java学习:Java从入门到精通总结 🍊 深入浅出RocketMQ设计思想:深入浅出RocketMQ设计思想 🍊 绝对不一样的职场干货:大厂最佳实践经验指南 📆 最近更新:2023年4月1日 …...
IP协议以及相关技术
这里写目录标题前言正文IP基本认识IP的作用IP和MAC的关系IP地址的基础知识IP地址定义IP地址分类(IPv4)无分类IP地址CIDR子网掩码IPv6基础知识相关技术DNS域名解析ARPDHCPNATICMPIGMP总结参考连接前言 大家好,我是练习两年半的Java练习生,今天我们来讲一…...
SpringBoot 项目使用 Sa-Token 完成登录认证
一、设计思路 对于一些登录之后才能访问的接口(例如:查询我的账号资料),我们通常的做法是增加一层接口校验: 如果校验通过,则:正常返回数据。如果校验未通过,则:抛出异…...
javaScript 蓝桥杯----梅楼封的一天
目录一、介绍二、目标1.函数入参要求:2.出参要求:三、输出示例1.示例一2.示例二3.示例三4.示例四5.示例五四、待完善代码五、知识点1.正则表达式2.split方法3.test方法4.match方法5.matchAll方法6.slice方法7.replace() 方法8.repeat方法六、答案一、介绍…...
谷粒商城笔记+踩坑(18)——购物车
目录 一、环境搭建 1.1、购物车模块初始化 1.2、动静资源处理 1.3、页面跳转配置 二、数据模型分析 2.1、购物车需求 2.1.1、离线购物车和在线购物车需求、数据库选择redis 2.1.2、购物车数据结构 2.2、模型类抽取,Cart和CartItem 2.3、Redis依赖和配置、…...
进阶C语言:指针笔试题
在学习完进阶C指针之后,可以来做一些笔试题来进行提升、巩固,小编在这里给大家分享几道比较有意思的笔试题 目录 一、笔试题1: 二、笔试题2 三、笔试题3: 四、笔试题4: 五、笔试题5: 编辑 六、笔试题…...
基于SSM(jsp)的宿舍管理系统
带项目源码和数据库文件 MySQL 导入即可用 可作为毕设参考 框架:SSM 1.1功能需求 本系统的设计目的是为了让老师可以随时随地的关注学生的在校情情况,方便老师对学生在校情况的添加、修改、删除和查询,实时关注学生的安全问题。该系统能…...
未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...
应用升级/灾备测试时使用guarantee 闪回点迅速回退
1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间, 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点,不需要开启数据库闪回。…...
使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...
渲染学进阶内容——模型
最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...
【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)
骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术,它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton):由层级结构的骨头组成,类似于人体骨骼蒙皮 (Mesh Skinning):将模型网格顶点绑定到骨骼上,使骨骼移动…...
优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...
代码随想录刷题day30
1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...
C++:多态机制详解
目录 一. 多态的概念 1.静态多态(编译时多态) 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1).协变 2).析构函数的重写 5.override 和 final关键字 1&#…...
