深入理解C语言中的静态库与动态库 —— 原理与实践
引言
在 C 语言编程中,库是预编译的代码集合,用于实现特定功能,以供其他程序使用。库可以分为静态库和动态库两种主要类型。静态库在编译阶段被链接到目标程序中,而动态库则是在运行时被加载。本文旨在深入探讨这两种库的工作原理、优缺点以及它们在实际项目中的应用。

静态库与动态库的基础知识
1.1 静态库
定义及用途:
静态库是一组预先编译好的目标文件的集合,这些文件包含了一个或多个源文件的函数和数据。当一个程序被链接时,静态库中的代码会被复制到最终的可执行文件中,使得每个使用该库的程序都包含了库的副本。
创建和使用:
假设我们有一个简单的库libfoo.a,其中包含一个函数foo()。
// foo.c
void foo() {printf("Hello from foo!\n");
}
可以使用以下命令来创建静态库:
gcc -c foo.c # 编译源文件生成目标文件 foo.o
ar rcs libfoo.a foo.o # 将目标文件打包成静态库
ranlib libfoo.a # 更新静态库的索引信息
然后,在主程序中使用它:
#include <stdio.h>
#include "foo.h"int main() {foo();return 0;
}
链接静态库到主程序:
gcc main.c -L. -lfoo -o main
链接过程:
- 当链接器遇到
-lfoo选项时,它会在指定的目录(这里是.表示当前目录)查找名为libfoo.a的静态库。 - 链接器读取库中的符号表,寻找未定义的符号(如
foo)。 - 如果找到匹配的符号,则链接器将相应的代码和数据复制到最终的可执行文件中。
优势:
- 可移植性:静态库与平台无关,可以在任何支持相同编译器的平台上使用。
- 独立性:静态库被链接到可执行文件中,这意味着可执行文件不需要外部库就能运行。
- 部署简单:静态链接的程序只需要一个可执行文件即可运行,无需额外安装库。
劣势:
- 可执行文件体积大:每个使用静态库的程序都会包含一份完整的库代码,导致可执行文件体积增大。
- 内存占用多:如果多个程序同时运行并且都使用相同的静态库,那么这些程序在内存中会各自保留一份库代码的副本,导致内存使用量增加。
- 难以更新:一旦静态库被链接到程序中,就很难更新库中的代码而不重新编译和链接整个程序。
1.2 动态库
定义及用途:
动态库(或共享库)在程序运行时才被加载。它们通常存储在一个单独的位置,并且可以在多个程序之间共享。这有助于减少磁盘空间的占用和提高内存使用的效率。
创建和使用:
假设我们有一个简单的库libbar.so,其中包含一个函数bar()。
// bar.c
void bar() {printf("Hello from bar!\n");
}
可以使用以下命令来创建动态库:
gcc -shared -fPIC -o libbar.so bar.c
然后,在主程序中使用它:
#include <stdio.h>
#include <dlfcn.h>int main() {void (*bar)();void *handle = dlopen("./libbar.so", RTLD_LAZY);if (!handle) {fprintf(stderr, "%s\n", dlerror());return 1;}bar = (void (*)())dlsym(handle, "bar");if ((void *)bar == NULL) {fprintf(stderr, "%s\n", dlerror());dlclose(handle);return 1;}bar();dlclose(handle);return 0;
}
加载机制:
- 在程序启动时,动态链接器会根据配置的搜索路径(如
LD_LIBRARY_PATH环境变量)查找所需的动态库。 - 动态链接器将找到的动态库加载到内存中,并解析其中的符号引用。
- 符号解析完成后,程序可以调用动态库中的函数。
优势:
- 内存节约:动态库可以被多个程序共享,从而减少内存使用。
- 易于更新:动态库可以在不重新编译和链接程序的情况下更新。
- 可扩展性:动态库可以方便地添加新功能而不会影响已有的程序。
劣势:
- 加载时间长:由于动态库需要在运行时加载,可能会导致程序启动时间稍长。
- 部署复杂:需要确保动态库存在于系统的适当位置,并且所有依赖项都正确安装。
- 版本兼容性问题:不同版本的动态库可能导致程序行为变化。

静态库与动态库的区别
- 存储方式:静态库中的代码在编译期间被直接嵌入到最终的可执行文件中;而动态库则作为独立的文件存在,由动态链接器在运行时加载。
- 链接时刻:静态库在编译阶段被链接,而动态库则在运行时被加载。
- 文件大小与加载速度:由于静态库中的代码被重复复制到每一个使用它的程序中,因此使用静态库的程序往往体积更大;动态库因为可以被多个程序共享,所以文件大小较小,但加载速度可能较慢。
- 内存占用与程序启动时间:使用动态库的程序在启动时需要额外的时间加载共享库,但在运行过程中,共享库仅被加载一次,节省了内存资源。
- 维护与更新:动态库更容易更新,因为它只需替换文件即可生效,而静态库一旦链接到可执行文件后就很难更新。
创建和使用示例
2.1 静态库示例
假设我们已经创建了libfoo.a,现在我们将展示如何将它链接到一个简单的程序中:
gcc main.c -L. -lfoo -o main
链接过程详解:
gcc调用链接器时,它会检查所有的.o文件和静态库。- 对于每一个未定义的符号(如
foo),链接器会在静态库中查找对应的定义。 - 当找到匹配的定义时,链接器会将相应的代码段和数据段复制到最终的可执行文件中。
示例输出:
$ ./main
Hello from foo!
2.2 动态库示例
假设我们已经创建了libbar.so,现在我们将展示如何在程序中使用它:
gcc main.c -ldl -o main
加载机制详解:
- 当程序启动时,动态链接器会尝试加载程序依赖的所有动态库。
- 动态链接器会解析动态库中的符号表,并将符号绑定到正确的内存位置。
- 程序可以通过调用
dlopen、dlsym和dlclose等函数来手动加载和卸载动态库。
示例输出:
$ ./main
Hello from bar!
性能考虑
内存使用效率:动态库可以被多个程序共享,因此减少了内存的使用。静态库中的代码在每个程序中都有一个副本,增加了内存消耗。
加载时间:静态库中的代码在编译时就被整合进程序中,因此不需要额外的加载时间。而动态库需要在程序启动时加载,这可能会稍微增加启动时间。
动态链接的优化:现代操作系统提供了延迟加载和按需加载的技术,可以显著减少动态库加载带来的开销。例如,在Linux上,动态链接器可以延迟加载那些在程序执行过程中并不立即需要的动态库。
实际项目中的选择
在实际项目中,选择使用静态库还是动态库取决于多个因素:
- 可维护性:动态库易于更新和维护,不需要重新编译和发布整个应用程序。
- 部署便利性:静态库简化了部署过程,因为所有依赖都已经被整合进程序本身。
- 跨平台支持:某些平台可能不支持动态库或者有不同的动态库格式,例如Windows下的DLL与Linux下的SO。
- 安全性:动态库可以利用ASLR等安全特性来提高程序的安全性。
示例场景:
- 游戏开发:在游戏开发中,为了获得最佳性能,通常会选择静态库来减少加载时间和内存使用。
- 企业级软件:对于需要频繁更新的企业级软件来说,使用动态库可以降低维护成本并提高更新效率。

深度解析
3.1 编译与链接过程
编译阶段:
- 源代码被编译器转换为目标代码,这个过程会生成
.o文件,这些文件包含了编译后的指令和符号表。
链接阶段:
- 链接器负责将多个
.o文件和库文件合并成一个可执行文件。 - 链接器解决不同模块之间的地址偏移问题,确保所有符号正确引用。
重定位:
- 链接器在生成可执行文件时需要进行重定位,以确保所有符号的地址正确无误。
- 重定位信息存储在
.o文件和库文件中,链接器根据这些信息调整符号地址。
3.2 动态链接器的工作原理
动态库搜索路径:
- 系统会根据
/etc/ld.so.conf文件和LD_LIBRARY_PATH环境变量来确定动态库的搜索路径。 - 动态链接器会扫描这些路径,寻找必要的共享库。
延迟加载:
- 现代动态链接器支持延迟加载技术,即只有当程序试图访问某个动态库中的符号时,才会加载相应的动态库。
- 这种技术可以显著减少程序启动时间。
符号解析:
- 动态链接器负责解析程序依赖的符号引用,并将它们绑定到正确的内存位置。
- 解析过程包括全局和局部符号的处理。
案例研究:
- 动态库更新:假设一个程序使用了旧版本的动态库,而新的版本修复了一些重要的安全漏洞。只需将新版本的动态库放置在正确的位置,程序就可以自动使用新版本的库,而无需重新编译或重新链接程序。
3.3 性能分析工具
工具推荐:
- gprof:用于收集程序执行时的性能统计数据。
- valgrind:可以检测内存泄漏和错误使用。
- perf:用于系统级别的性能监控。
性能测试:
- 可以通过编写测试脚本来比较使用静态库和动态库的程序在内存使用和加载时间方面的表现。
- 使用上述工具来分析程序的性能,并记录结果。
示例实验设计:
-
实验步骤:
- 构建使用静态库的程序。
- 构建使用相同功能的动态库版本的程序。
- 使用
gprof或perf对两个版本的程序进行基准测试。 - 分析结果,对比内存使用和加载时间。
-
预期结果:动态库版本的程序可能会显示出较低的内存使用率和较长的加载时间,而静态库版本的程序可能会有较高的内存使用率和较短的加载时间。
结论
静态库和动态库各有优势和劣势。静态库适用于需要严格控制资源使用的情况,而动态库更适合于需要频繁更新的应用。开发者应该根据项目的具体需求来做出选择。
相关文章:
深入理解C语言中的静态库与动态库 —— 原理与实践
引言 在 C 语言编程中,库是预编译的代码集合,用于实现特定功能,以供其他程序使用。库可以分为静态库和动态库两种主要类型。静态库在编译阶段被链接到目标程序中,而动态库则是在运行时被加载。本文旨在深入探讨这两种库的工作原理…...
本地缓存库分析(一):golang-lru
文章目录 本地缓存概览golang-lru标准lrulru的操作PutGet 2q:冷热分离lruPutGet expirable_lru:支持过期时间的lruPutGet过期 总结 本地缓存概览 在业务中,一般会将极高频访问的数据缓存到本地。以减少网络IO的开销,下游服务的压…...
qt配置https请求
qt应用版本 windows 32位 先说下心理路程,你能遇到的我都遇到了,你能想到的我都想到了,怎么解决看这一篇就够了,从上午12点到晚上12点几乎没离开电脑(除了吃饭),对于openssl这种用的时候无感&am…...
C语言进阶——文件操作
一、文件的基本知识 1.1什么是文件 在程序设计中,一般谈的文件有两种:程序文件、数据文件。 程序文件:包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执…...
MYSQL-查看用户权限语法(二十一)
13.7.5.21 SHOW GRANTS 语句 SHOW GRANTS [FOR user]此语句以GRANT语句的形式显示分配给MySQL用户帐户的权限,必须执行GRANT语句才能复制权限分配。 注意 要显示MySQL帐户的非特权信息,请使用SHOW CREATE USER语句。 参见第 13.7.5.12 节“ SHOW CREA…...
在MySQL中存储IP地址的最佳实践
文章目录 一、IP地址的格式二、存储IP地址的数据类型选择1. VARCHAR优点缺点 2. INT 或 BIGINT优点缺点示例 3. VARBINARY优点缺点示例 三、最佳实践建议1. 选择合适的数据类型2. 索引优化3. 数据验证4. 安全性考虑 四、Java支持五、结论 在现代网络应用中,IP地址是…...
Vite打包配置
Vite打包配置 1.项目启动自动打开网页 {"scripts": {"dev": "vite --open"} }2.base配置打包公共路径 配置base选项的作用主要是指定项目在开发或生产环境中的公共基础路径。这个配置项对于确保资源能够正确加载尤为关键,尤其是在…...
node集成redis (教学)
文章目录 前言一、安装redis二、可视化界面测试连接1.vscode安装插件 三、node代码编写1.先安装两个库(redis和ioredis)2.测试连接 (前提是你的redis服务器要启动起来) 总结 前言 在Node.js中集成ioredis是一个常见的做法&#x…...
江协科技STM32学习- P22 实验-ADC单通道/ADC多通道
🚀write in front🚀 🔎大家好,我是黄桃罐头,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流 🎁欢迎各位→点赞👍 收藏⭐️ 留言📝…...
RL学习笔记-马尔可夫过程
参考资料:蘑菇书、周博磊老师课程 在强化学习中,智能体与环境交互是通过马尔可夫决策过程来表示的,因此马尔可夫决策过程是强化学习的基本框架。 马尔可夫性质 指一个随机过程在给定现在状态及所有过去状态情况下,其未来状态的条件…...
LeetCode Hot 100:动态规划
LeetCode Hot 100:动态规划 70. 爬楼梯 class Solution { public:int climbStairs(int n) {if (n 0)return 0;vector<int> dp(n 1);// 初始化dp[0] 1;// 状态转移for (int i 1; i < n; i) {dp[i] dp[i - 1];if (i > 2)dp[i] dp[i - 2];}return …...
使用Python制作雪景图片教程
如果你想用Python写一个程序来输出有关“深夜雪”的诗意文本或描述,可以通过简单的字符串输出来实现。以下是一个示例代码,展示如何用Python来描绘深夜雪的场景。 # 定义深夜雪的描述 description """ 夜幕降临,天空洒下银色…...
S-Function
目录 S-Function介绍 生成S-Function的三种常用手段 使用手写S-函数合并定制代码 使用S-Function Builder块合并定制代码 使用代码继承工具合并定制代码 S-Function介绍 我们可以使用S-Function扩展Simulink对仿真和代码生成的支持。例如,可以使用它们…...
如何具备阅读JAVA JDK虚拟机源码能力
源码位置https://github.com/openjdk/jdk 核心实现源码[部分截图] /* * Copyright (c) 1995, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistr…...
Python | Leetcode Python题解之第514题自由之路
题目: 题解: Test "godding" target "d"i 0left i lc 0 right i rc 0while Test[left] ! target:left - 1lc 1if left -1:left len(Test) - 1while Test[right] ! target:right 1rc 1if right len(Test):right 0prin…...
Docker 镜像下载问题及解决办法
Docker 镜像下载问题及解决办法 我在杂乱的、破旧的村庄寂寞地走过漫长的雨季,将我年少的眼光从晦暗的日子里打捞出来的是一棵棵开花的树,它们以一串串卓然不俗的花擦明了我的眼睛,也洗净了我的灵魂。 引言 在使用 Docker 时,用户…...
2分钟搞定 HarmonyOs Next创建模拟器
官方文档参考链接: 创建模拟器-管理模拟器-使用模拟器运行应用/服务-应用/服务运行-DevEco Studio - 华为HarmonyOS开发者https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/ide-emulator-create-V5 1. 首先打开Device Manager 2. 进入这个界面后…...
方形件排样优化与订单组批问题探析
方形件排样优化与订单组批问题是计算复杂度很高的组合优化问题,在工业工程中有很广泛的应用背景。为实现个性化定制生产模式,企业会选择订单组批的方式,继而通过排样优化实现批量切割,加工完成后再按照不同客户需求进行分拣&#…...
vue3组件通信--自定义事件
自定义事件是典型的子传父的方法。 为什么叫自定义事件呢?是因为我们用sendToy"getToy"这种格式写,很显然,在DOM中,没有叫sendToy的事件。 父组件FatherComponent.vue: <script setup> import ChildComponent fr…...
ubuntu 安装k3s
配置hostname的方法为 hostnamectl set-hostname k3sserver hostnamectlsudo apt-get update && sudo apt-get upgrade -y sudo apt-get install -y curl#手动下载v1.31.1k3s1 https://github.com/k3s-io/k3s/releases/tag/v1.31.1%2Bk3s1 #将k3s-airgap-images-amd64…...
OpenLayers 可视化之热力图
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 热力图(Heatmap)又叫热点图,是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...
C++.OpenGL (14/64)多光源(Multiple Lights)
多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...
【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论
路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中(图1): mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...
Razor编程中@Html的方法使用大全
文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...
【Linux】Linux安装并配置RabbitMQ
目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的,需要先安…...
深入浅出WebGL:在浏览器中解锁3D世界的魔法钥匙
WebGL:在浏览器中解锁3D世界的魔法钥匙 引言:网页的边界正在消失 在数字化浪潮的推动下,网页早已不再是静态信息的展示窗口。如今,我们可以在浏览器中体验逼真的3D游戏、交互式数据可视化、虚拟实验室,甚至沉浸式的V…...
拟合问题处理
在机器学习中,核心任务通常围绕模型训练和性能提升展开,但你提到的 “优化训练数据解决过拟合” 和 “提升泛化性能解决欠拟合” 需要结合更准确的概念进行梳理。以下是对机器学习核心任务的系统复习和修正: 一、机器学习的核心任务框架 机…...
【工具教程】多个条形码识别用条码内容对图片重命名,批量PDF条形码识别后用条码内容批量改名,使用教程及注意事项
一、条形码识别改名使用教程 打开软件并选择处理模式:打开软件后,根据要处理的文件类型,选择 “图片识别模式” 或 “PDF 识别模式”。如果是处理包含条形码的 PDF 文件,就选择 “PDF 识别模式”;若是处理图片文件&…...
npm install 相关命令
npm install 相关命令 基本安装命令 # 安装 package.json 中列出的所有依赖 npm install npm i # 简写形式# 安装特定包 npm install <package-name># 安装特定版本 npm install <package-name><version>依赖类型选项 # 安装为生产依赖(默认&…...
开源项目实战学习之YOLO11:12.6 ultralytics-models-tiny_encoder.py
👉 欢迎关注,了解更多精彩内容 👉 欢迎关注,了解更多精彩内容 👉 欢迎关注,了解更多精彩内容 ultralytics-models-sam 1.sam-modules-tiny_encoder.py2.数据处理流程3.代码架构图(类层次与依赖)blocks.py: 定义模型中的各种模块结构 ,如卷积块、残差块等基础构建…...
