Linux基础IO----动态库与静态库

news2025/6/2 9:26:37

什么是库?

库是由一些.o文件打包在一起而形成的可执行程序的半成品。

如何理解这句话呢?

首先,一个程序在运行前需要进行预处理、编译、汇编、链接这几步。

预处理: 完成头文件展开、去注释、宏替换、条件编译等,最终形成xxx.i文件。
编译: 完成词法分析、语法分析、语义分析、符号汇总等,检查无误后将代码翻译成汇编指令,最终形成xxx.s文件。
汇编: 将汇编指令转换成二进制指令,最终形成xxx.o文件。
链接: 将生成的各个xxx.o文件进行链接,最终形成可执行程序。

在链接之前的这些.o文件就是上文提到的被打包进库里的文件。

那么为什么要对这些文件进行打包呢?

拿我们平时常用的库举例,我们写程序所用到的输入输出函数,比如prinf、scanf,这些函数都不是我们自己去实现的,为什么却能为我们所用呢?原因就是这些函数是在库里的程序实现的,不需要我们手动重新再去“造轮子”,拿来就直接能用,库里还有很多方便使用的函数,在程序里包含不同的库,能使用的库函数就越多越方便我们实现程序。所以库存在的意义就是帮我们把基础的常用的函数等打包到一起直接提供我们使用。所以我们又说库是可执行程序的“半成品”。

认识动静态库

编写一个简单的程序:

能够正常打印处hello lib是因为我们的库中已经有了printf函数的实现,在gcc生成可执行程序时,将C标准库链接到我们的程序里了。接下来我们通过ldd+文件名命令查看一个可执行程序链接了哪些库吧:

注意:ldd命令只能查看可执行程序依赖的库和动态库的依赖库。

 其中lib.c.so.6就是该可执行程序依赖的库文件,我们通过ls命令可以发现lib.c.so.6是一个软链接:

在Linux中,动态库的后缀是.so  静态库的后缀是.a

去掉前缀lib和后缀.so  .a和后面的版本号就是库的名字,在这里就是c

在默认情况下gcc 或者g++编译器都是默认动态连接库的,若想进行静态链接需要加上-static选项:

可以发现静态连接库的可执行程序的大小非常大,这是因为静态链接就是把静态库直接整个加入到你的可执行程序里的,所以这个程序执行就不需要依赖仍和库。

 

动静态库的特征

静态库的特征

静态库是程序在编译链接的时候把库的代码直接复制到可执行程序里,导致文件变大,并且程序在执行的时候不在依赖静态库。

优点:静态链接库的程序运行不再依赖静态库。

缺点:程序占用大量空间,如果有多个程序都是静态链接的同一个库就会有很多重复代码。

动态库的特征

动态库是在程序运行的时候才去链接相应代码的,多个程序共享库内的代码,所以动态库也叫共享库,能够大大节省内存。链接时将库映射到进程的进程地址空间的共享区中,当进程调用库内的函数时就可以直接去共享区内找。

优点:节省空间,内存中不会存在很多重复的代码。

缺点:程序必须以来动态库运行,如果动态库找不到了程序就运行不了。

静态库的打包和使用

为了更好的理解静态库,下面我们将自己写的方法打包进一个我们自己的静态库中。

add.h add.c:

#pragma once

extern int my_add(int x, int y);
#include "add.h"

int my_add(int x, int y)
{
	return x + y;
}

 

sub.h sub.c:

#pragma once

extern int my_sub(int x, int y);
#include "add.h"

int my_sub(int x, int y)
{
	return x - y;
}

打包: 

 第一步:让源文件生成.o文件

第二步:用ar命令将 .o文件打包

ar命令是gnu的归档工具,常用于将目标文件打包为静态库,下面我们使用ar命令的-r选项和-c选项进行打包。

  • -r(replace):若静态库文件当中的目标文件有更新,则用新的目标文件替换旧的目标文件。
  • -c(create):建立静态库文件。

此外,我们可以用ar命令的-t选项和-v选项查看静态库当中的文件。

  • -t:列出静态库中的文件。
  • -v(verbose):显示详细的信息。

 第三步:将头文件和生成的静态库组织起来

当我们把自己的库给别人用的时候,实际上需要给别人两个文件夹,一个文件夹下面放的是一堆头文件的集合,另一个文件夹下面放的是所有的库文件。

因此,在这里我们可以将add.h和sub.h这两个头文件放到一个名为include的目录下,将生成的静态库文件libcal.a放到一个名为lib的目录下,然后将这两个目录都放到mathlib下,此时就可以将mathlib给别人使用了。

 

使用:

创建下面程序,包含头文件add.h:

#include <stdio.h>
#include <add.h>

int main()
{
    int a = 10, b = 20;
    int sum = my_add(a, b);
    printf("%d + %d = %d\n",a,b,sum);
    return 0;
}

 方法一:使用gcc编译main.c生成可执行程序时需要携带三个选项:

  • -I:指定头文件搜索路径。
  • -L:指定库文件搜索路径。
  • -l:指明需要链接库文件路径下的哪一个库。

gcc main.c -I./mathlib/include -L./mathlib/lib -lcal

根据命名规则我们的库名字就是cal,所以 最后一个选项是cal

 说明:

  • 编译器不知道你自己写的头文件add.h的位置,所以编译的时候需要你指出来,同时还需要库文件的路径来定位库。
  • 实际中,在库文件的lib目录下可能会有大量的库文件,因此我们需要指明需要链接库文件路径下的哪一个库。库文件名去掉前缀lib,再去掉后缀.so或者.a及其后面的版本号,剩下的就是这个库的名字。
  • -I-L-l这三个选项后面可以加空格,也可以不加空格。

方法二:把头文件和库文件拷贝到系统路径下

为什么用C语言自带的库不是需要这样大费周章的写路径,其实是因为编译器找库和头文件首先是去系统路径下找的,所以不需要直接指出,同时gcc是用来编译C程序的所以默认找的是c库所以也不用指明库的名字

可以尝试:

sudo cp mathlib/lib/libcal.a /lib64/

sudo cp mathlib/include/*

将你的库加入系统路径,需要注意的是就算把文件拷贝到系统路径下,使用gcc时仍需指定库名。

扩展:
实际上我们拷贝头文件和库文件到系统路径下的过程,就是安装库的过程。但并不推荐将自己写的头文件和库文件拷贝到系统路径下,这样做会对系统文件造成污染。

动态库的打包和使用

打包:

还是以刚才的四个文件举例:

第一步:让源文件生成对应的.o文件

此时用源文件生成目标文件时需要携带-fPIC选项:

  • -fPIC(position independent code):产生位置无关码。

fPIC的作用:

  • 实现内存地址无关性
    • 使用-fPIC编译的代码,不依赖与在内存中的具体位置,可在不同内存地址上正确运行。动态链接库被不同程序加载时,可能加载到不同内存地址,位置无关代码可以适应这种情况。
  • 支持动态库共享
    • 允许多个进程共享动态库的单个副本 。若不使用-fPIC,加载动态库代码段时,因代码含绝对地址,需重定位,这会使每个使用该库的进程在内核里生成各自的代码段副本,造成内存浪费
  • 提高程序灵活性与可移植性
    • 使动态链接库能在不同环境、不同内存布局下加载和使用,无需因内存地址差异重新编译 

第二步:使用-shared选项将所有目标文件打包为动态库

gcc -shared -o libcal.so add.o sub.o

第三步:将头文件和生成的动态库组织起来 

和静态库一样,将头文件和库文件整合到一起一个放include一个放lib目录

 

使用:

还是用main.c来演示:

#include <stdio.h>
#include <add.h>

int main()
{
	int x = 20;
	int y = 10;
	int z = my_add(x, y);
	printf("%d + %d = %d\n", x, y, z);
	return 0;
}

说明一下,使用该动态库的方法与刚才我们使用静态库的方法一样,我们既可以使用 -I-L-l这三个选项来生成可执行程序,也可以先将头文件和库文件拷贝到系统目录下,然后仅使用-l选项指明需要链接的库名字来生成可执行程序,下面我们仅以第一种方法为例进行演示。

gcc main.c -I./mlib/include -L./mlib/lib -lcal

编译成功但是执行失败,这是为什么呢? 

在编译时通过 -L./mlib/lib 指定了动态库的搜索路径,但这只对编译链接阶段有效,而运行阶段的动态库搜索需要单独配置。

方法一:拷贝.so文件到系统共享库路径下

我们直接将库文件拷贝到系统共享的库路径下,这样一来系统就能够找到对应的库文件了。

 sudo cp mlib/lib/libcal.so /lib64

方法二:更改LD_LIBRARY_PATH

LD_LIBRARY_PATH是程序运行动态查找库时所要搜索的路径,我们只需将动态库所在的目录路径添加到LD_LIBRARY_PATH环境变量当中即可。

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/YOURPATH/libtest/mlib/lib

 但是这个环境变量的修改时暂时的,下次重启就失效了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2386644.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LeetCode百题刷004(哈希表优化两数和问题)

遇到的问题都有解决的方案&#xff0c;希望我的博客可以为你提供一些帮助 一、哈希策略优化两数和问题 题目地址&#xff1a;1. 两数之和 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/two-sum/description/ 思路分析&#xff1a; 题目要求在一个整型…

FPGA通信之VGA与HDMI

文章目录 VGA基本概念&#xff1a;水平扫描&#xff1a;垂直扫描&#xff1a; 时序如下&#xff1a;端口设计疑问为什么需要输出那么多端口不输出时钟怎么保证电子枪移动速度符合时序VGA转HDMI 仿真电路图代码总结&#xff1a;VGA看野火电子教程 HDMITMDS传输原理为什么使用TMD…

Leetcode百题斩-二叉树

二叉树作为经典面试系列&#xff0c;那么当然要来看看。总计14道题&#xff0c;包含大量的简单题&#xff0c;说明这确实是个比较基础的专题。快速过快速过。 先构造一个二叉树数据结构。 public class TreeNode {int val;TreeNode left;TreeNode right;TreeNode() {}TreeNode…

ACM Latex模板:合并添加作者和单位

目录&#xff1a; 1.ACM会议论文Latex模板&#xff0c;逐个添加作者和单位&#xff1a; 1&#xff09;Latex&#xff1a; 2&#xff09;效果&#xff1a; 2. ACM会议论文Latex模板&#xff0c;合并添加作者和单位&#xff1a; 1&#xff09;Latex&#xff1a; 2&#x…

将MCP(ModelContextProtocol)与Semantic Kernel集成(调用github)

文章目录 将MCP&#xff08;ModelContextProtocol&#xff09;与Semantic Kernel集成&#xff08;调用github&#xff09;一、模型上下文协议&#xff08;MCP&#xff09;简介1.1 简介1.2 示例 二、集成步骤2.1 安装环境依赖2.2 构建语义内核&#xff08;Kernel&#xff09;2.3…

游戏引擎学习第311天:支持手动排序

仓库: https://gitee.com/mrxiao_com/2d_game_7(已满) 新仓库: https://gitee.com/mrxiao_com/2d_game_8 回顾并为今天的内容定下基调 我们接下来要继续完成之前开始的工作&#xff0c;上周五开始的部分内容&#xff0c;虽然当时对最终效果还不太确定&#xff0c;但现在主要任…

LambdaQueryWrapper、MybatisPlus提供的基本接口方法、增删改查常用的接口方法、自定义 SQL

DAY26.2 Java核心基础 MybatisPlus提供的基本接口方法 分页查询 导入依赖springboot整合Mybatis-plus <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.3</version&g…

深度学习---可视化

模型可视化 深度学习模型可视化是理解、调试和优化模型的关键技术&#xff0c;涉及模型结构、参数、层输出、数据流动、训练过程等多维度分析。 一、可视化的核心作用 模型理解 解析复杂模型的网络架构&#xff08;如CNN的层级连接、Transformer的注意力机制&#xff09;。揭…

Windows系统如何查看ssh公钥

很多人只是一味的为拿到ssh公钥而努力&#xff0c;往往却会忽略了ssh公钥与私钥背后的作用。 咱们在这里会花两分钟。 一分钟速通概念&#xff0c;一分钟教会你如何获取。 一分钟速通概念&#xff1a; 如何生成&#xff1a; SHH 公钥 与 私钥 是基于非对称加密算法&#xff…

【2025】嵌入式软考中级部分试题

大题: 大模型 神经网络 机器学习 深度学习的包含关系 不一定对 订阅-发布者模型 发布/订阅模式特点: ①解耦:发布者和订阅者之间没有直接联系,它们通过中间的消息代理(如消息队列或事件总线)进行通信。这种解耦使得系统更加灵活,可以独立地添加或移除发布者和订阅者…

Antd中Upload组件封装及使用:

1.Upload上传组件功能: 文件校验 : 文件格式校验/文件大小校验/上传文件总个数校验 相关功能 : 拖拽功能/上传到远程(七牛)/文件删除及下载 2.组件效果展示: 3.疑难点及解决方案: Promise.all多文件并行上传到远程(七牛云): (1)在beforeUpload钩子函数中获取token (2)循环fi…

Linux环境基础开发工具->vim

引入&#xff1a;vim是什么&#xff1f; vs叫作继承开发环境&#xff0c;我们可以在里面编辑代码&#xff0c;调式代码&#xff0c;运行代码....这种叫集成开发环境&#xff1b;而vim只用来编辑代码&#xff0c;也就是类似于在windows上打开一个记事本来写代码的操作 集成开发…

跳板问题(贪心算法+细节思考)

首先直接看题&#xff1a; 这题直接贪心其实问题不大&#xff1a; 下面先展示我的一个错误代码&#xff1a; # include<iostream> # include<vector> # include<algorithm>using namespace std;int main() {int N,M;cin>>N>>M;vector<vecto…

RuoYi前后端分离框架集成UEditorPlus富文本编辑器

一、背景 采用若依框架搭建了一个小型的电子书项目,项目前端、后端、移动端就一人,电子书的章节内容是以富文本内容进行呈现的,产品设计人员直接给了一个第三方收费的富文本编辑器截图放到开发文档中,提了一沓需求点,概况下来就是要做成下图中的样子。作为一个后端开发人…

IPD流程落地:项目任务书Charter开发

目录 简介 第一个方面&#xff0c;回答的是Why的问题。 第二点&#xff0c;要回答做什么的问题&#xff0c;也就是产品定义What的问题。 第三点就是要回答执行策略与计划的问题&#xff0c;也就是How、When、Who的问题。 第四点是对上述这些分析的总结分析&#xff0c;要为…

Vue 2 混入 (Mixins) 的详细使用指南

1.基本概念 混入 (Mixins) 是 Vue 2 中用于组件代码复用的重要特性&#xff0c;它允许你将可复用的功能分发到多个组件中。 混入是一种灵活的代码复用方式&#xff0c;可以包含任意组件选项&#xff08;data、methods、生命周期钩子等&#xff09;。当组件使用混入时&#xff…

FreeRTOS 在物联网传感器节点的应用:低功耗实时数据采集与传输方案

FreeRTOS 在物联网传感器节点的应用&#xff1a;低功耗实时数据采集与传输方案 二、FreeRTOS 任务划分与优先级设计 任务名称优先级执行周期功能描述Sensor_Collect3100ms多传感器数据采集与预处理Data_Process2事件驱动数据滤波/压缩/异常检测LoRa_Transmit41s低功耗长距离数…

Linux环境下基于Docker安装 PostgreSQL数据库并配置 pgvector

文章目录 1 docker-compose 安装 PostgreSQL 容器内安装 pgvector1.1 基于 docker-compose 安装 PostgreSQL 数据库1.2 容器内配置 pgvector 2. docker-compose Dockerfile 形式直接配置PostgreSQL数据库及 pgvector参考资料 PostgreSQL是一种功能强大的开源关系数据库管理系…

(9)-java+ selenium->元素定位之By name

1.简介 上一篇已经介绍了通过id来定位元素,继续介绍其他剩下的七种定位方法中的通过name来定位元素。本文来介绍Webdriver中元素定位方法之By name,顾名思义,就是我们想要定位的目标元素节点上,有一个name ="value"的属性,这样我们就可以通过name的value直接去…

深浅拷贝?

一、定义&#xff1a; 浅拷贝&#xff1a;只复制对象的第一层属性&#xff0c;若第一层属性是引用类型&#xff08;如对象、数组&#xff09;&#xff0c;则复制其内存地址&#xff0c;修改拷贝后的嵌套对象会影响原对象。 深拷贝&#xff1a;递归复制对象的所有层级&#xf…