静态链接库与动态链接库
静态链接库与动态链接库
- 一、从源程序到可执行文件
- 二、编译、链接和装入
- 三、静态链接库与动态链接库
- 四、静态链接库与动态链接库的制作与使用
- 1.静态库的制作及使用
- 2.动态库的制作及使用
一、从源程序到可执行文件
由于计算机无法直接理解和执行高级语言(C、C++、Java)程序,需要将高级语言程序转换为机器语言程序(机器语言是用二进制代码表示的、计算机唯一可以直接识别和执行的一种机器指令的集合),通常把这种转换过程叫做翻译。
在C/C++中,整个翻译过程可以分为四步:
- 预处理阶段:预处理器对源程序进行
#include
头文件展开、#define
宏替换、#ifdef
条件编译以及行号和文件标识的添加等预处理操作,输出结果是一个以.i为扩展名的源文件。通过g++ -E main.cpp > main.i
命令可以生成预处理信息并将预处理信息重定向到指定文件中。 - 编译阶段:编译器对预处理后的源程序进行词法分析、句法分析等编译操作,生成一个以.s为扩展名汇编语言源程序。汇编语言源程序中的每条语句都以一种文本格式描述了一条低级机器语言指令。通过
g++ -S main.cpp -o main.s
命令即可可以编译生成汇编文件。 - 汇编阶段:汇编器将汇编语言源程序翻译成机器语言指令,并把这些指令打包成一个以.o为扩展名的可重定位目标文件,它是一种二进制文件,因此用文本编辑器打开会显示乱码。通过
g++ -c main.cpp -o main.o
命令即可汇编生成可重定位目标文件。 - 链接阶段:链接器将多个可重定位目标文件和链接库合并为一个可执行目标文件,或简称可执行文件,最终生成的可执行文件被保存在磁盘上。通过
g++ main.cpp -o main
命令即可实现从源程序到可执行文件的翻译过程。
二、编译、链接和装入
从操作系统程序执行的角度来看,一个程序想要放在内存中执行,除了上面的翻译过程(预处理、编译、汇编、链接),还需要对程序进行装入操作。
具体来说,编译是由编译程序对用户源程序进行编译,形成若干目标模块的过程,这些目标模块中的地址均为伪逻辑地址,即 0-10
、0-10
、0-10
。链接则是由链接程序将一组目标模块及所需库函数进行链接,形成一个完整的装入模块的过程,这个装入模块中的地址为逻辑地址,即 0 - 32
。而装入则是由装入程序将装入模块装入内存,装入后形成的是物理地址,即 1000-1032
。
链接方式主要分为以下三种:
- 静态链接:装入前链接成一个完整的装入模块,以后不再拆开。
- 装入时动态链接:运行前装入内存时边装入边链接,便于对目标模块的修改、更新与共享。
- 运行时动态链接:运行时需要目标模块才装入并连接,加快了程序装入,节省了内存空间。
装入方式也有三种:
- 绝对装入:在单道程序环境下,编译时直接生成物理地址目标代码,直接按此地址装入。仅适用于单道程序,且要求程序员对内存极为熟悉,但实际上编程一般使用符号地址。
- 可重定位装入:通过静态重定位在装入时将地址变换一次性完成,一次性装入。装入时必须分配固定大小全部空间,内存不足无法装入,不支持程序浮动。
- 动态运行时装入:装入时不进行地址变换,直到运行时再借助重定位寄存器进行动态重定位。装入部分代码即可运行,可以分散存储,支持程序浮动,便于共享。
三、静态链接库与动态链接库
装入时动态链接还是运行时动态链接都属于动态链接,都是将部分链接操作推迟到程序执行时才进行,而动态链接一般需要使用动态链接库。而静态链接则是在生成可执行文件之前完成所有链接操作,使用的库文件被称作静态链接库。
静态函数库一般命名为 libxxx.a
或 xxx.lib
。静态函数库在编译的时候会直接整合到目标程序中,所以利用静态函数库编译成的文件会比较大,这类函数库最大的优点就是编译成功的可执行文件可以独立运行,而不再需要向外部要求读取函数库的内容,但升级时如果函数库更新则需要重新编译。
动态函数库一般命名为 libxxx.so
或 xxx.dll
。与静态函数库被整个编译到程序中不同,动态函数库只有当可执行文件需要使用到函数库的机制时,程序才会去读取函数库来使用,也就是说可执行文件无法单独运行,这样方便升级,只要替换对应动态库即可,不必重新编译整个可执行文件。
静态库和动态库最本质的区别就是加载的时机,静态库在编译阶段就会加载到可执行文件中,而动态库会在执行阶段加载到可执行文件中。
四、静态链接库与动态链接库的制作与使用
1.静态库的制作及使用
静态库的制作命令为:
g++ -c xxx.cpp -o xxx.o # 将目标源文件xxx.c编译成目标文件xxx.o
ar -rcs libxxx.a xxx.o # 使用ar工具制作静态库libxxx.a
lib_fun.h:
void func(int &num);
lib_fun.cpp:
#include "lib_fun.h"void func(int &num) {num++;
}
main.cpp:
#include <iostream>
#include "lib_fun.h"using namespace std;int main() {int x = 0;func(x);cout << x << endl;return 0;
}
首先我们使用 tree
命令查看一下当前目录结构,其中user目录模拟用户,lib目录模拟库。
atreus@iZwz9fsfltolu74amg1v0rZ:~/Code/lib_demo$ tree
.
├── lib
│ ├── lib_fun.cpp
│ └── lib_fun.h
└── user├── lib_fun.h└── main.cpp2 directories, 4 files
atreus@iZwz9fsfltolu74amg1v0rZ:~/Code/lib_demo$
我们在user目录下先编译一下main.cpp看一下效果,可以看到 func(int&)
声明了但没有定义,因为如果没有找到声明会直接报 was not declared in this scope
。
atreus@iZwz9fsfltolu74amg1v0rZ:~/Code/lib_demo$ cd user/
atreus@iZwz9fsfltolu74amg1v0rZ:~/Code/lib_demo/user$ g++ main.cpp -o main
/usr/bin/ld: /tmp/ccnh1mTY.o: in function `main':
main.cpp:(.text+0x2a): undefined reference to `func(int&)'
collect2: error: ld returned 1 exit status
atreus@iZwz9fsfltolu74amg1v0rZ:~/Code/lib_demo/user$
然后我们回到lib目录制作静态库,静态库的命名格式为 libxxx.a
,并将制作好的静态库拷贝到user目录下cd。
atreus@iZwz9fsfltolu74amg1v0rZ:~/Code/lib_demo/user$ cd ../lib/
atreus@iZwz9fsfltolu74amg1v0rZ:~/Code/lib_demo/lib$ g++ -c lib_fun.cpp -o lib_fun.o
atreus@iZwz9fsfltolu74amg1v0rZ:~/Code/lib_demo/lib$ ar -rcs libfun.a lib_fun.o
atreus@iZwz9fsfltolu74amg1v0rZ:~/Code/lib_demo/lib$ cp libfun.a ../user/
atreus@iZwz9fsfltolu74amg1v0rZ:~/Code/lib_demo/lib$ tree ../
../
├── lib
│ ├── libfun.a
│ ├── lib_fun.cpp
│ ├── lib_fun.h
│ └── lib_fun.o
└── user├── libfun.a├── lib_fun.h└── main.cpp2 directories, 7 files
atreus@iZwz9fsfltolu74amg1v0rZ:~/Code/lib_demo/lib$
我们转到user目录下,重新进行编译,编译命令中的 -l
后面跟的是动态库的名字,即 libxxx.a
中的 xxx
, -L
后面跟的是静态链接库 libxxx.a
的存放位置,这里就在当前文件夹。可以看到,在链接了静态库之后就可以正常执行了。
atreus@iZwz9fsfltolu74amg1v0rZ:~/Code/lib_demo/user$ g++ main.cpp -l fun -L ./ -o main
atreus@iZwz9fsfltolu74amg1v0rZ:~/Code/lib_demo/user$ ./main
1
atreus@iZwz9fsfltolu74amg1v0rZ:~/Code/lib_demo/user$
2.动态库的制作及使用
动态库的制作命令为:
g++ -shared -fPIC -o libxxx.so xxx.cpp # 制作动态库libxxx.so
首先我们依然是使用 tree
命令查看一下当前目录结构。
atreus@iZwz9fsfltolu74amg1v0rZ:~/Code/lib_demo$ tree
.
├── lib
│ ├── lib_fun.cpp
│ └── lib_fun.h
└── user├── lib_fun.h└── main.cpp2 directories, 4 files
atreus@iZwz9fsfltolu74amg1v0rZ:~/Code/lib_demo$
同样的,在lib目录下制作动态库,并将其拷贝到user目录下。
atreus@iZwz9fsfltolu74amg1v0rZ:~/Code/lib_demo/lib$ g++ -shared -fPIC -o libfun.so lib_fun.cpp
atreus@iZwz9fsfltolu74amg1v0rZ:~/Code/lib_demo/lib$ cp libfun.so ../user/
atreus@iZwz9fsfltolu74amg1v0rZ:~/Code/lib_demo/lib$
我们转到user目录下,重新进行编译,编译命令中的 ./libfun.so
即为动态库的存放路径。可以看到,在链接了动态库之后就可以正常执行了。
atreus@iZwz9fsfltolu74amg1v0rZ:~/Code/lib_demo/lib$ cd ../user/
atreus@iZwz9fsfltolu74amg1v0rZ:~/Code/lib_demo/user$ g++ main.cpp ./libfun.so -o main
atreus@iZwz9fsfltolu74amg1v0rZ:~/Code/lib_demo/user$ ./main
1
atreus@iZwz9fsfltolu74amg1v0rZ:~/Code/lib_demo/user$
相关文章:

静态链接库与动态链接库
静态链接库与动态链接库 一、从源程序到可执行文件二、编译、链接和装入三、静态链接库与动态链接库四、静态链接库与动态链接库的制作与使用1.静态库的制作及使用2.动态库的制作及使用 一、从源程序到可执行文件 由于计算机无法直接理解和执行高级语言(C、C、Java…...
ffmpeg 抓取一帧数据
FFmpeg功能比较强大,这里记录一条从摄像机抓拍的一条命令: ffmpeg.exe -i rtsp://admin:hisense2021192.168.1.64:554/live0.264 -r 1 -ss 00:00:00 -t 00:00:01 -f image2 image.jpg ; ---执行成功。 这是一条网络摄像机的抓图命令,其实就…...

学好数据结构的秘诀
学好数据结构的秘诀 作为计算机专业的一名“老兵”,笔者从事数据结构和算法的研究已经近20余年了,在学习的过程中,也会遇到一些问题,但在解决问题时,积累了一些经验,为了让读者在学习数据结构的过程中少走…...

IT知识百科:什么是下一代防火墙和IPS?
引言 随着网络攻击的日益增多,防火墙和入侵防御系统(Intrusion Prevention System, IPS)已成为企业网络安全的必备设备。然而,传统的防火墙和IPS已经无法满足复杂多变的网络安全威胁,因此,下一代防火墙和I…...
常量指针和指针常量, top-level const和low-level const
区分常量指针和指针常量,并且认识什么是top-level const和low-level const。 1.判别: 拿到一个指针(例如const int* a),就从左往右读,只看const和*。const读作常量,*读作指针,int类型这些不用管。 2.指针常量 int a…...

【iOS】-- GET和POST(NSURLSession)
文章目录 NSURLSessionGET和POST区别 GET方法GET请求步骤 POSTPOST请求步骤 NSURLSessionDataDelegate代理方法AFNetWorking添加头文件GETPOST第一种第二种 NSURLSession 使用NSURLSession,一般有两步操作:通过NSURLSession的实例创建task;执…...

@RequestBody,@RequestParam,@RequestPart应用场景和区别
ReqeustBody 使用此注解接收参数时,适用于请求体格式为 application/json,只能用对象接收 RequestParam 支持application/json,也同样支持multipart/form-data请求 RequestPart RequestPart这个注解用在multipart/form-data表单提交请求的方法…...

libevent高并发网络编程 - 02_libevent缓冲IO之bufferevent
文章目录 1. 为什么需要缓冲区?2. 水位3. bufferevent常用API3.1 evconnlistener_new_bind()3.2 evconnlistener_free()3.3 bufferevent_socket_new()3.4 bufferevent_enable()3.5 bufferevent_set_timeouts()3.6 bufferevent_setcb()3.7 bufferevent_setwatermark(…...

院内导航移动导诊服务体系,院内导航怎么实现?
院内导航怎么实现?经过多年发展,医院规模愈加庞大,尤其是综合性医院,院区面积较大,门诊、医技、住院等大楼及楼区内部设计复杂,科室、诊室数量众多,对于新患者犹如进入了迷宫,客观环…...
MCTP协议和NCSI
MCTP(Management Component Transport Protocol)是一种管理组件传输协议,用于在计算机系统中管理各种组件,例如固件、BIOS、操作系统等。MCTP 协议定义了一种传输格式,以便在各种总线上进行通信,例如 PCIe、…...

Jmeter接口测试流程详解
1、jmeter简介 Jmeter是由Apache公司开发的java开源项目,所以想要使用它必须基于java环境才可以; Jmeter采用多线程,允许通过多个线程并发取样或通过独立的线程对不同的功能同时取样。 2、jmeter安装 首先需要安装jdk(最好是最…...

怎样使用Web自动化测试减少手动劳动?以百度网站为例
从入门到精通!企业级接口自动化测试实战,详细教学!(自学必备视频) 目录 摘要 步骤1:安装和配置Selenium 步骤2:启动浏览器并访问百度网站 步骤3:关闭浏览器 总结 摘要 本指南将…...

union和位域的混合使用
1、union(共用体) 1.1、概述 C 语言中,union是一种数据类型,对比于结构体,结构体中的每个成员都占用独立的内存空间,而联合中所有的成员都共享同一个内存空间。 也就是说,union中的不同成员要…...

PMP 高项 07-项目质量管理
项目质量管理 概念 质量的基本概念 克劳斯比:符合要求 戴明:低成本条件下可预测的一致性和可靠度,适应市场需要 朱兰:适用性,满足客户需要 国际标准化组织:质量是反映实体(产品、过程或活动等…...

鸿蒙Hi3861学习十一-Huawei LiteOS-M(内存池)
一、简介 LiteOS将内核与内存管理分开实现,操作系统内核仅规定了必要的内存管理函数原型,而不关心这些内存管理函数是如何实现的。 LiteOS内存管理模块管理系统的内存资源,包括:初始化、分配、释放。 不采用C标准库中的内存管理函…...

MySQL原理(七):内存管理和磁盘管理
前言 上一篇介绍了 MySQL 的日志,这一篇将介绍内存管理和磁盘管理相关的内容。 内存管理 MySQL 的数据都是存在磁盘中的,我们要更新一条记录的时候,得先要从磁盘读取该记录,然后在内存中修改这条记录。修改完这条记录后会缓存起…...

【Shell脚本】Linux安装Nginx以及开机自启
目录 一、Linux安装Nginx脚本1、把编写好的安装Nginx脚本放置到nginx.sh文件中2、在检查网络的时候,这里的IP地址,填写的需要安装Nginx服务器的IP地址3、这里的端口号可按照自己的需要进行修改4、安装Nginx脚本 二、Nginx开机自启 一、Linux安装Nginx脚本…...

solidworks三维建模竞赛练习题
solidworks三维建模竞赛练习题:3D01‐ 01 solidworks三维建模竞赛练习题:3D01‐ 02 solidworks三维建模竞赛练习题:3D01‐ 03 solidworks三维建模竞赛练习题:3D01‐ 04 solidworks三维建模竞赛练习题:3D01‐ 05 solidw…...

Redis---订阅和发布
目录 消息系统命令 消息系统 发布/订阅,即 pub/sub,是一种消息通信模式:发布者也称为消息生产者,生产和发送消息到存储系统;订阅者也称为消息消费者,从存储系统接收和消费消息。这个存储系统可以是文件系…...

使用Statsmodel进行假设检验和线性回归
如果你使用 Python 处理数据,你可能听说过 statsmodel 库。Statsmodels 是一个 Python 模块,它提供各种统计模型和函数来探索、分析和可视化数据。该库广泛用于学术研究、金融和数据科学。在本文中,我们将介绍 statsmodel 库的基础知识、如何…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面
代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http…...

【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...

jdbc查询mysql数据库时,出现id顺序错误的情况
我在repository中的查询语句如下所示,即传入一个List<intager>的数据,返回这些id的问题列表。但是由于数据库查询时ID列表的顺序与预期不一致,会导致返回的id是从小到大排列的,但我不希望这样。 Query("SELECT NEW com…...
【把数组变成一棵树】有序数组秒变平衡BST,原来可以这么优雅!
【把数组变成一棵树】有序数组秒变平衡BST,原来可以这么优雅! 🌱 前言:一棵树的浪漫,从数组开始说起 程序员的世界里,数组是最常见的基本结构之一,几乎每种语言、每种算法都少不了它。可你有没有想过,一组看似“线性排列”的有序数组,竟然可以**“长”成一棵平衡的二…...
使用python进行图像处理—图像滤波(5)
图像滤波是图像处理中最基本和最重要的操作之一。它的目的是在空间域上修改图像的像素值,以达到平滑(去噪)、锐化、边缘检测等效果。滤波通常通过卷积操作实现。 5.1卷积(Convolution)原理 卷积是滤波的核心。它是一种数学运算,…...

生产管理系统开发:专业软件开发公司的实践与思考
生产管理系统开发的关键点 在当前制造业智能化升级的转型背景下,生产管理系统开发正逐步成为企业优化生产流程的重要技术手段。不同行业、不同规模的企业在推进生产管理数字化转型过程中,面临的挑战存在显著差异。本文结合具体实践案例,分析…...