动静态库-Linux 学习

news2025/3/5 10:48:22

在软件开发中,程序库是一组预先编写好的程序代码,它们存储了常用的函数、变量和数据结构等。这些库可以帮助开发者节省大量的时间和精力,避免重复编写相同的代码。当我们在 Linux 系统中开发程序时,经常会用到两种类型的程序库:静态库和动态库。

  • 静态库 (.a):静态库是指在程序编译时,将库文件中的代码直接复制到可执行文件中。这意味着当程序运行时,库文件中的代码已经包含在可执行文件中,不需要在运行时进行加载。
  • 动态库 (.so):动态库是指在程序运行时才加载的库文件。程序本身不包含库文件的代码,而是通过链接到动态库来调用其中的函数和数据结构等。动态库通常以共享库的形式存在,多个程序可以共享一个动态库。

从库的制作者角度

静态库

我们可以自己尝试写一个简单的计算程序来实现简单的加减乘除功能。

我们可以先创建以下文件:

加法:Add.c , Add.h
减法:Sub.c , Sub.h
乘法:Mul.c , Mul.h
除法:Div.c , Divh

image.png

如果我们要上面的文件形成一个库,我们是不能在上面的这些文件中写main函数的,因为库中是没有main 函数的,我们也不能把main 函数打入库中。

// Add.h
#pragma once // 防止头文件被重复包含
#include <stdio.h>
int Add(int,int);
----------------------------
// Add.c
#include "Add.h"
int Add(int x, int y)
{
	return x + y;
}
// Sub.h
#pragma once
#include <stdio.h>
int Sub(int,int);
----------------------------
// Sub.c

#include "Sub.h"
int Sub(int x, int y)
{
	return x - y;
}

//Mul.h
#pragma once
#include <stdio.h>
int Mul(int,int);
----------------------------
// Mul.c

#include "Mul.h"
int Mul(int x, int y)
{
	return x * y;
}


//Div.h
#pragma once
#include <stdio.h>
int Div(int,int,int*);
------------------------------
// Div.c

#include "Div.h"
int Div(int x, int y, int *code)
{
	*code = 0;
	if (y == 0)
	{
	 *code = -1;
	 return -1;	
	}
	return x / y;
}


我们现在要将上面的一堆的头文件 和 源文件 形成静态库

为 我们的方法进行测试,我们可以建立一个叫TestMain.c 的文件。

// TestMain.c
#include "Add.h"
#include "Sub.h"
#include "Mul.h"
#include "Div.h"


 int x = 20;
 int y = 10;

 printf("%d + %d = %d\n",x, y, Add(x,y));
 printf("%d - %d = %d\n",x, y, Sub(x,y));
 printf("%d * %d = %d\n",x, y, Mul(x,y));
 

我们可以运行一下上面的测试程序TestMain.c,不过我们要先进行编译:

// Linux 系统 

gcc -o test Add.c Sub.c Mul.c Div.c TestMain.c

// 我们在编译的时候没有加上头文件,因为当前的头文件和源文件是在同一个路径下的,所以我们不用加上头文件。


上面我们是把所有的源文件都进行了编译的,其实如果我们要形成一个可执行程序,我们不太建议将所有的源文件都直接进行编译,因为我们一旦这样做,每一个源文件都要进行预处理,编译,汇编,链接。所以一般在面对这样的多文件项目时我们一般都建议把这样的源文件编译成.o 文件。编译成.o 文件之后,然后我们在把所有的.o 文件进行对应的链接 形成一个可执行。

.c–(预处理,编译,汇编)—> .o ----(链接)----> 可执行

// 介绍一下`-c` 的选项。
gcc -c test.c // 在 test.c形成可执行前(即:链接前) 终止,最终会生成一个`.o` 的文件,默认是同名的`test.o`

所以如果我们把上面文件中的TestMain.o 去掉,为什么要去掉呢?因为如果我们要做一个静态库,库中是不能有main 函数的,main 函数只能是用这个库的用户写的。这样一来就只剩下Add.0,Sub.o,Mul.o,Div.o 了,我们只需要对Add.0,Sub.o,Mul.o,Div.o 这些.o 文件进行打包,以后如果某个用户想要用到这些加减乘除的方法,只需要将自己写的main 函数编译成xxx.o 文件,然后再和打包好的文件中的.o 文件进行链接就好了。

生成静态库

那我们如何对我们的.o文件进行打包呢?

ar 命令
ar命令是一个把所有的.o 文件打包形成我们对应的库文件的过程。

 ar -rc libmymath.a add.o sub.o 

ar是gnu归档工具,选项:rc表示(replace and create),他的意思是:把所有的.o文件打包生成一个.a 文件,如果存在就替换(replace)如果不存在就创建(create)。

库的名字要以lib 开头,以.a 结尾,所以上面我们的库是mymath ,但是加上前缀和后缀之后就是:libmymath.a 了。

静态库的原理就是将库中的源代码直接翻译成.o 目标二进制文件,然后打包

动态库

一、什么是动态库?

动态库,也叫共享库(Shared Library),是一种在程序运行时才被加载到内存中的代码库。它的文件名通常以 libxxx.so 的形式命名(xxx 是库的名字,so 表示 shared object)。
与动态库相对的是静态库(Static Library),静态库在程序编译时会被直接嵌入到可执行文件中,文件名通常以 libxxx.a 的形式命名。

二、生成动态库

1. 编译选项

生成动态库需要使用 gcc 编译器,并指定以下选项:

  • -fPIC:生成位置无关代码(Position Independent Code, PIC)。这种代码可以在内存中的任何位置运行,是动态库的必要条件。
  • -shared:表示生成共享库格式。
2. 示例代码

假设我们有以下文件:

  • add.c:实现加法函数。
  • sub.c:实现减法函数。

文件内容如下:

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

int add(int a, int b) {
    return a + b;
}
// sub.c
#include <stdio.h>

int sub(int a, int b) {
    return a - b;
}
3. 生成动态库的步骤
# 编译源文件为位置无关代码
gcc -fPIC -c add.c sub.c

# 生成动态库
gcc -shared -o libmymath.so add.o sub.o

执行完上述命令后,会生成一个名为libmymath.so 的动态库文件。

从库的使用者角度

静态库的使用

上面我们自己写的库libmymath.a 是第三方库,gcc 默认不认识 。所以我们在使用gcc D的时候还要加上一个-l 选项来链接到我们的库,还记得上面我们所说的吗,我们的库是以lib为前缀的,以.a 为后缀的,但是在用-l 链接的时候,我们要去掉前缀和后缀,直接用mymath 像这样:-lmymath ,我们还要用-L 来制定库的路径,我们这样来表示-L. ,用. 来表示当前路径,如果你的静态库在当前路径下的话,所以连在一起就是:

[root@localhost linux]# gcc TestMain.c -L. -lmymath
  • TestMain.c 是用户写的程序。
  • -L 指定库路径
  • -l 指定库名
  • -I : 指定头文件的路径

如果我们要把自己写的库交给其他用户使用的话,那我们就要把.h.a 交给他,

gcc 默认是动态链接的,

动态库的使用

使用动态库

1. 编译选项

在使用动态库时,需要指定以下选项:

  • -L:指定动态库所在的路径(如果动态库不在系统默认路径中)。
  • -l:指定要链接的动态库的名字(去掉 lib 前缀和 .so 后缀)。
2. 示例代码

假设我们有一个主程序 main.c,它调用了动态库中的函数:

// main.c
#include <stdio.h>

int add(int a, int b);
int sub(int a, int b);

int main() 
{
    printf("add(10, 20) = %d\n", add(10, 20));
    printf("sub(100, 20) = %d\n", sub(100, 20));
    return 0;
}
3. 编译主程序
# 编译主程序
gcc main.c -o main -L. -lmymath
  • -L. 表示动态库在当前目录下。
  • -lmymath 表示链接 libmymath.so 动态库。

运行动态库

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

系统默认的动态库路径通常是 /usr/lib/usr/local/lib或是:/lib64 。可以将动态库文件拷贝到这些路径下:

# 拷贝动态库到 /usr/local/lib
cp libmymath.so /usr/local/lib/
方法二:通过使用软连接 查找动态库
ln -s /*动态库的路径*/ libmymath.so

/*动态库的路径*/ : 这里填动态库的路径

libmymath.so : 这个是软连接的名字。

这样,我们通过在当前目录下建立软连接的方式找到我们对应的库。
gcc 可以在当前目录下找到动态库。所以我们可以在当前目录下建立对应库的软连接,这样来让 gcc 找到,我们也可以在系统的指定目录下建立对应的软连接。

方法三:设置 LD_LIBRARY_PATH 环境变量(LD 的意思就是 load -加载,所以这句话就是 : 加载库路径环境变量)

如果不想将动态库文件拷贝到系统路径,可以通过设置 LD_LIBRARY_PATH 环境变量来指定动态库的路径:

# 设置 LD_LIBRARY_PATH
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/*这里填你写的动态库的路径*/

但是这里导入的环境变量是内存级的,所以你重启电脑之后就没了。

方法四:直接更改系统配置文件:使用 ldconfig 配置动态库路径

如果动态库文件存放在非默认路径下,可以通过修改 /etc/ld.so.conf 文件或在 /etc/ld.so.conf.d/ 下添加配置文件来指定动态库路径。

例如:

# 创建配置文件
echo "/path/to/your/library" > /etc/ld.so.conf.d/mylib.conf

# 更新动态库缓存
ldconfig

同一组库,提供动静两种库,gcc默认使用动态库。

五、使用外部库

Linux 系统中有很多外部库,例如:

  • ncurses:用于处理屏幕显示和用户输入的库。
  • glib:用于通用工具函数的库。
  • openssl:用于加密和安全通信的库。

使用外部库的步骤与使用自己编写的动态库类似,需要:

  1. 安装外部库(通常通过包管理工具,如 aptyum)。
    比如安装ncurses
    sudo yum install -y ncurses-devel
  2. 在编译时指定库的路径和名称。

例如,使用 ncurses 库:

# 编译
gcc main.c -o main -lncurses

# 运行
./main

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

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

相关文章

DeepSeek 系列模型:论文精读《A Survey of DeepSeek Models》

引言&#xff1a;一篇快速了解 DeepSeek 系列的论文。我在翻译时加入了一些可以提高 “可读性” 的连词 ✅ NLP 研 2 选手的学习笔记 笔者简介&#xff1a;Wang Linyong&#xff0c;NPU&#xff0c;2023级&#xff0c;计算机技术 研究方向&#xff1a;文本生成、大语言模型 论文…

机器人学习模拟框架 robosuite (3) 机器人控制代码示例

Robosuite框架是一个用于机器人模拟和控制的强大工具&#xff0c;支持多种类型的机器人。 官方文档&#xff1a;Overview — robosuite 1.5 documentation 开源地址&#xff1a;https://github.com/ARISE-Initiative/robosuite 目录 1、通过键盘或SpaceMouse远程控制机器人…

kakfa-3:ISR机制、HWLEO、生产者、消费者、核心参数负载均衡

1. kafka内核原理 1.1 ISR机制 光是依靠多副本机制能保证Kafka的高可用性&#xff0c;但是能保证数据不丢失吗&#xff1f;不行&#xff0c;因为如果leader宕机&#xff0c;但是leader的数据还没同步到follower上去&#xff0c;此时即使选举了follower作为新的leader&#xff…

【微知】如何查看Mellanox网卡上的光模块的信息?(ethtool -m enp1s0f0 看型号、厂商、生产日期等)

背景 服务器上插入的光模块经常被忽略&#xff0c;往往这里是定位问题最根本的地方。如何通过命令查看&#xff1f; 命令 ethtool提供了-m参数&#xff0c;m是module-info的意思&#xff0c;他是从光模块的eeprom中读取数据。&#xff08;应该是用i2c协议读取的&#xff09;…

yum源选要配置华为云的源,阿里云用不了的情况

curl -O /etc/yum.repos.d/CentOS-Base.repo https://repo.huaweicloud.com/repository/conf/CentOS-7-reg.repo

好数——前缀和思想(题目分享)

今天我的舍友去参加“传智杯”广东省的省赛&#xff0c;跟我说了这样一道题&#xff0c;他说他想不出来怎么去优化代码&#xff0c;怎么做都是套用两层for循环超时&#xff0c;下面我就根据题意&#xff0c;使用前缀和的算法去优化一下思路&#xff0c;题目本身是不难的&#x…

MWC 2025 | 移远通信大模型解决方案加速落地,引领服务机器人创新变革

随着人工智能、大模型等技术的蓬勃发展&#xff0c;生成式AI应用全面爆发。在此背景下&#xff0c;服务机器人作为大模型技术在端侧落地的关键场景&#xff0c;迎来了前所未有的发展机遇。 作为与用户直接交互的智能设备&#xff0c;服务机器人需要应对复杂场景下的感知、决策和…

springboot425-基于SpringBoot的BUG管理系统(源码+数据库+纯前后端分离+部署讲解等)

&#x1f495;&#x1f495;作者&#xff1a; 爱笑学姐 &#x1f495;&#x1f495;个人简介&#xff1a;十年Java&#xff0c;Python美女程序员一枚&#xff0c;精通计算机专业前后端各类框架。 &#x1f495;&#x1f495;各类成品Java毕设 。javaweb&#xff0c;ssm&#xf…

FineReport 操作注意

1.父单元格重复的时候&#xff0c;如何取消合并 效果如下&#xff1a; 只需要在单元格中&#xff0c;将数据设置为【列表】即可。 2.待定

3D手眼标定转换详细实施步骤及原理概述

3D手眼标定转换详细实施步骤及原理概述 一、手眼标定的核心目标二、3D手眼标定的原理概述一、基本概念与坐标系定义**二、数学建模与方程推导****1. 坐标变换的齐次矩阵表示****2. 手眼标定方程推导** **三、方程求解方法****1. 分离旋转与平移****2. 旋转矩阵求解****3. 平移向…

Verilog:SCCB控制器

目录 一、SCCB协议 &#xff08;1&#xff09;SCCB时序 &#xff08;2&#xff09;与I2C的区别 二、Verilog 实现 &#xff08;1&#xff09;设计要求 &#xff08;2&#xff09;设计要点 &#xff08;3&#xff09;模块完整代码 三、功能验证 &#xff08;1&#xff09;写…

与中国联通技术共建:通过obdiag分析OceanBase DDL中的报错场景

中国联通软件研究院&#xff08;简称联通软研院&#xff09;在全面评估与广泛调研后&#xff0c;在 2021年底决定采用OceanBase 作为基础&#xff0c;自研分布式数据库产品CUDB&#xff08;即China Unicom Database&#xff0c;中国联通数据库&#xff09;。目前&#xff0c;该…

大数据与网络安全讲座

&#x1f345; 点击文末小卡片 &#xff0c;免费获取网络安全全套资料&#xff0c;资料在手&#xff0c;涨薪更快 大数据的价值为大家公认。业界通常以4个“V”来概括大数据的基本特征——Volume(数据体量巨大)、Variety(数据类型繁多)、Value(价值密度低)、Velocity(处理速度快…

张驰咨询:用六西格玛重构动力电池行业的BOM成本逻辑

在动力电池行业&#xff0c;BOM&#xff08;物料清单&#xff09;成本每降低1%&#xff0c;都可能改写企业的利润曲线。某头部企业的三元锂电池BOM成本曾较行业标杆高出11%&#xff0c;单电芯利润率被压缩至3%的生死线。然而&#xff0c;通过张驰咨询的六西格玛方法论&#xff…

pyside6学习专栏(九):在PySide6中使用PySide6.QtCharts绘制6种不同的图表的示例代码

PySide6的QtCharts类支持绘制各种型状的图表&#xff0c;如面积区域图、饼状图、折线图、直方图、线条曲线图、离散点图等&#xff0c;下面的代码是采用示例数据绘制这6种图表的示例代码,并可实现动画显示效果&#xff0c;实际使用时参照代码中示例数据的格式将实际数据替换即可…

《深度学习实战》第10集:联邦学习与隐私保护

第10集&#xff1a;联邦学习与隐私保护 2025年3月4日更新了代码&#xff0c;补充了实例程序运行截图 和 如何提高模型准确率的方法 系统梳理 集集精彩 代码验证 保证实战 随着数据隐私问题日益受到关注&#xff0c;联邦学习&#xff08;Federated Learning&#xff09; 作为一…

【数据结构】二叉树总结篇

遍历 递归 递归三部曲&#xff1a; 1.参数和返回值 2.终止条件 3.单层逻辑&#xff08;遍历顺序&#xff09; var preorderTraversal function(root) { // 第一种let res[];const dfsfunction(root){if(rootnull)return ;//先序遍历所以从父节点开始res.push(root.val);//递归…

软考-数据库开发工程师-3.1-数据结构-线性结构

第3章内容比较多&#xff0c;内容考试分数占比较大&#xff0c;6分左右 线性表 1、线性表的定义 一个线性表是n个元素的有限序列(n≥0)&#xff0c;通常表示为(a1&#xff0c;a2, a3,…an). 2、线性表的顺序存储(顺序表) 是指用一组地址连续的存储单元依次存储线性表中的数据元…

【五.LangChain技术与应用】【2.LangChain虚拟环境搭建(下):环境优化与调试】

一、Docker化部署:别让你的环境成为薛定谔的猫 经历过"在我机器上能跑"惨案的老铁都懂,传统虚拟环境就像个黑盒子。去年我帮客户部署LangChain应用,因为glibc版本差了0.1,整个服务直接崩成烟花。从那天起,我所有项目都强制上Docker! Dockerfile生存指南: #…

deepseek+mermaid【自动生成流程图】

成果&#xff1a; 第一步打开deepseek官网(或百度版&#xff08;更快一点&#xff09;)&#xff1a; 百度AI搜索 - 办公学习一站解决 第二步&#xff0c;生成对应的Mermaid流程图&#xff1a; 丢给deepseek代码&#xff0c;或题目要求 生成mermaid代码 第三步将代码复制到me…