Linux笔记---动静态库(使用篇)

目录

1. 库的概念

2. 静态库(Static Libraries)

2.1 静态库的制作

2.2 静态库的使用

2.2.1 显式指定库文件及头文件路径

2.2.2 将库文件安装到系统目录

2.2.3 将头文件安装到系统目录

3. 动态库

3.1 动态库的制作

3.2 动态库的使用

3.2.1 显式指定库文件路径

2.2.2 将路径加载到环境变量中 

2.2.3 配置文件

4. 总结与补充


1. 库的概念

库(Library) 是一组预先编译好的代码(函数、类、数据等)的集合,可以被多个程序共享和重复使用。库的核心目的是代码复用,避免开发者重复编写相同的功能(如文件操作、数学计算等)。

本质上来说库是一种可执行代码的二进制形式,可以被操作系统载如内存执行。

按照代码复用的形式,库可以分为两种:

  • 静态库:.a [Linux].lib [Windows]
  • 动态库:.so [Linux]、.dll [Windows]

库是在链接这一步被使用的,实际上就是一堆 .o 文件的集合,我们可以特定的工具来将这些 .o 文件进行打包,进而形成库。 

为举例方便,这里给出我们自己实现的简单的C语言库---myc:

// mystdio.h#pragma once
#include <stdio.h>
#define MAX 1024
#define NONE_FLUSH (1<<0)
#define LINE_FLUSH (1<<1)
#define FULL_FLUSH (1<<2)typedef struct IO_FILE
{int fileno;int flag;char outbuffer[MAX];int bufferlen;int flush_method;
}MyFile;MyFile *MyFopen(const char *path, const char *mode);
void MyFclose(MyFile *);
int MyFwrite(MyFile *, void *str, int len);
void MyFFlush(MyFile *);// mystdio.c#include "mystdio.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>static MyFile *BuyFile(int fd, int flag)
{MyFile *f = (MyFile*)malloc(sizeof(MyFile));if(f == NULL) return NULL;f->bufferlen = 0;f->fileno = fd;f->flag = flag;f->flush_method = LINE_FLUSH;memset(f->outbuffer, 0, sizeof(f->outbuffer));return f;
}MyFile *MyFopen(const char *path, const char *mode)
{int fd = -1;int flag = 0;if(strcmp(mode, "w") == 0){flag = O_CREAT | O_WRONLY | O_TRUNC;fd = open(path, flag, 0666);}else if(strcmp(mode, "a") == 0){flag = O_CREAT | O_WRONLY | O_APPEND;fd = open(path, flag, 0666);}else if(strcmp(mode, "r") == 0){flag = O_RDWR;fd = open(path, flag);}else{//TODO}if(fd < 0) return NULL;return BuyFile(fd, flag);
}
void MyFclose(MyFile *file)
{if(file->fileno < 0) return;MyFFlush(file);close(file->fileno);free(file);
}
int MyFwrite(MyFile *file, void *str, int len)
{// 1. 拷贝memcpy(file->outbuffer+file->bufferlen, str, len);file->bufferlen += len;// 2. 尝试判断是否满足刷新条件!if((file->flush_method & LINE_FLUSH) && file->outbuffer[file->bufferlen-1] == '\n'){MyFFlush(file);}return 0;
}
void MyFFlush(MyFile *file)
{if(file->bufferlen <= 0) return;// 把数据从用户拷贝到内核文件缓冲区中int n = write(file->fileno, file->outbuffer, file->bufferlen);(void)n;fsync(file->fileno);file->bufferlen = 0;
}// mystring.h#pragma once
int my_strlen(const char *s);// mystring.c#include "mystring.h"int my_strlen(const char *s)
{const char *start = s;while(*s){s++;}return s - start;
}

接下来,我们会介绍如何将上述的原文件打包成动静态库并使用。 

2. 静态库(Static Libraries)

  • 文件扩展名:.a(Archive)

  • 特点:
    • 在编译时,库的代码会被直接复制到最终的可执行文件中。

    • 生成的可执行文件独立,不依赖运行时环境中的库文件。

    • 缺点:文件体积较大,且更新库时需要重新编译程序。

  • 创建工具:ar(归档工具)+ ranlib(生成索引)。

  • 使用场景:适合对程序独立性要求高的场景。

2.1 静态库的制作

静态库使用 ar 指令进行打包:

ar -rc lib[库名].a [目标文件s]

lib[库名].a 是静态库文件的命名规范,实际上的库名需要去掉lib前缀以及.a扩展名。

通常来说,只有库文件是不够的,还需要将库的头文件交给用户,所以我们可以使用如下的Makefile来将库及其头文件一起打包交给用户:

SRC=$(wildcard *.c)
OBJ=$(SRC:.c=.o)libmyc.a:$(OBJ)ar -rc $@ $^$(OBJ):$(SRC)gcc -c $^.PHONY:output
output:mkdir -p lib/includemkdir -p lib/mylibcp -f *.h lib/includecp -f *.a lib/mylibtar czf lib.tgz lib.PHONY:clean
clean:rm -rf *.o libmyc.a lib lib.tgz

2.2 静态库的使用

gcc/g++会默认链接c标准库,但是myc库是我们自己制作的第三方库,所以在编译时需要指定链接myc库。

假设用户已经接收到了我们的 lib.tgz 包,并且用户的代码(usercode.c)调用了我们库中的方法:

注:使用tar xzf lib.tgz进行解包得到lib目录。 

// usercode.c#include "mystdio.h"
#include "mystring.h"
#include <string.h>
#include <unistd.h>int main()
{MyFile *filep = MyFopen("./log.txt", "a");if(!filep){printf("fopen error!\n");return 1;}int cnt = 10;while(cnt--){char *msg = (char*)"hello myfile!!!";MyFwrite(filep, msg, strlen(msg));MyFFlush(filep);printf("buffer: %s\n", filep->outbuffer);sleep(1);}MyFclose(filep); // FILE *fpconst char *str = "hello bit!\n";printf("strlen: %d\n",my_strlen(str));return 0;
}
2.2.1 显式指定库文件及头文件路径

在编译时,需要指定头文件所在路径、要链接的库文件路径以及指定库文件:

gcc -o [可执行程序] [目标文件s] -I [头文件路径] -L [库路径] -l [库名]

2.2.2 将库文件安装到系统目录

我们知道,所谓安装,实际上就是把文件拷贝到指定的系统目录下。这样,在我们未显式指定库文件所在目录时,系统就能够在默认目录中找到。

当然,除了拷贝,建立链接也是可以的。 

  • /lib/usr/lib:系统级库
  • /usr/local/lib:用户安装的第三方库

我们将 libmyc.a 文件拷贝到三个库中的一个即可完成安装,此时不在需要指明库所在路径:

但是,不建议安装到系统级库,用户自己要安装的第三方库最好安装到 /usr/local/lib 中。 

2.2.3 将头文件安装到系统目录
  • /usr/include:系统级头文件
  • /usr/local/include:本地安装的第三方库头文件
  • /usr/include/<库名> 或 /usr/local/include/<库名>:特定软件的子目录

我们将自己的头文件拷贝到上述目录下即可完成安装,此时不再需要指明头文件所在路径:

3. 动态库

  • 文件扩展名:.so(Shared Object)

  • 特点:
    • 在程序运行时被动态加载到内存,多个程序可共享同一份库代码。

    • 可执行文件体积小,库更新时无需重新编译程序。

    • 缺点:依赖运行时环境中的库文件(若缺失会导致程序无法运行)。

  • 创建工具:gcc/g++ 的 -shared 选项。

  • 使用场景:大多数系统库(如 glibc)和通用功能库(如 OpenSSL)。

3.1 动态库的制作

// 编译目标文件时需要带上-fPIC选项,fPIC:产生位置无关码(position independent code) 
gcc/g++ -c -fPIC [原文件s]// 生成库文件时需要带上-shared选项,shared: 表示生成共享库格式 
gcc/g++ -o lib[库名].so [目标文件s] -shared

同样的,lib[库名].so 是命名规范,实际上的库名需要去掉lib前缀和 .so扩展名。

我们可以使用如下的Makefile来对库及其头文件进行打包:

SRC=$(wildcard *.c)
OBJ=$(SRC:.c=.o)libmyc.so:$(OBJ)gcc -shared -o $@ $^$(OBJ):$(SRC)gcc -fPIC -c $^.PHONY:output
output:mkdir -p lib/includemkdir -p lib/mylibcp -f *.h lib/includecp -f *.so lib/mylibtar czf lib.tgz lib.PHONY:clean
clean:rm -rf *.o libmyc.so lib lib.tgz

3.2 动态库的使用

我们以同样的代码作为示例,将库及头文件安装到系统目录的方式与静态库一样,这里就不再重复,但是对于显式给出库文件路径的方式,我们要多说两句。

3.2.1 显式指定库文件路径

假如我们未将库文件安装到系统目录当中,并显式指定某路径下的库文件:

我们会发现编译通过了,但是:

当我们运行生成的可执行程序时,会发现系统显式找不到对应的库。

这是因为,我们仅仅告诉了编译器:“这个库是存在的”,所以编译器完成了编译。

但是动态链接是在程序运行时才将库与可执行程序产生链接,负责链接的是系统,然而系统并不知道在哪里找到这个库。 

要让操作系统在运行我们的程序时找到对应的动态库,我们可以选择安装的形式(与静态库的安装完全一致),也可采取以下几点中提到的措施。

 注意:与静态链接不同,接下来的几点措施(包括安装),都不需要重新编译可执行文件。

2.2.2 将路径加载到环境变量中 
# LD_LIBRARY_PATH:临时指定额外的库搜索路径。
export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/path/to/libs

使用 ldd 命令可以查看可执行程序链接的库及其所在路径:

但是,这种方式只是临时的,重新登录或更新环境变量时就会失效。 

2.2.3 配置文件
  • /etc/ld.so.conf:系统级库路径配置文件
  • /etc/ld.so.conf.d:用户库路径配置文件目录

我们可以直接在/etc/ld.so.conf中加入我们库文件的路径,但是我们依然更建议在用户库路径配置文件目录中添加自己的配置文件:

这里sudo echo创建文件的方式居然不行,只能用编辑器创建了。

然后加载配置文件:

sudo ldconfig

 结果与2.2.2相同,这里就不展示了。

4. 总结与补充

  • gcc/g++编译命令补充:
    • [-I] :指定头文件所在目录。

    • [-L]:指定库文件所在路径。

    • [-l]:指定要链接的库。

    • [-shared]:生成动态库。

    • [-fPIC]:产生位置无关码。

    • [-static]:使用静态链接。

  • 静态库使用ar命令进行打包:
    ar -rc lib[库名].a [目标文件s]
  • 将静态库与用户目标文件一起编译即可生成可执行程序。
  •  动态库使用gcc/g++进行打包,且目标文件需要携带位置无关码:
    // 编译目标文件时需要带上-fPIC选项,fPIC:产生位置无关码(position independent code) 
    gcc/g++ -c -fPIC [原文件s]// 生成库文件时需要带上-shared选项,shared: 表示生成共享库格式 
    gcc/g++ -o lib[库名].so [目标文件s] -shared
  • 动态库在编译时需要让gcc/g++知道这个库是存在的(给出路径或安装到系统,并指定库名)。在运行时,系统需要能够找到这个库(需要安装到系统)。
  • 第三方库在编译时要指定链接这个库。
  • 在编译时,我们的系统当中可能既安装了某个库的动态版本,又安装了某个库的静态版本。此时,编译器默认能采用动态链接则采用动态链接。如果要使用静态链接则需要带上 -static 选项,一旦带上这个选项,就意味着动态链接被禁用,如果某个库只有动态链接的版本,则会发生链接失败。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CAS(Compare And Swap)

CAS核心原理 操作流程 CAS 包含三个参数&#xff1a;内存值&#xff08;V&#xff09;、预期值&#xff08;E&#xff09;和新值&#xff08;N&#xff09;。执行步骤如下&#xff1a; 比较&#xff1a;检查当前内存值 V 是否等于预期值 E。 交换&#xff1a;如果相等&#…

宝塔面板安装docker flarum失败,请先安装依赖应用: [‘mysql‘]:5/8

安装失败的解决方案 提示错误请先安装依赖应用: [mysql]:5/8 解决方案&#xff1a;不要使用最新的docker mysql&#xff0c;使用5.7.44版本docker mysql&#xff0c;等安装完毕再安装docker flarum就不会报错了。 如果安装完成你不知道默认的账号密码可以看这里 宝塔docker f…

c#的.Net Framework 的console 项目找不到System.Window.Forms 引用

首先确保是建立的.Net Framework 的console 项目,然后天健reference 应用找不到System.Windows.Forms 引用 打开对应的csproj 文件 在第一个PropertyGroup下添加 <UseWindowsForms>true</UseWindowsForms> 然后在第一个ItemGroup 下添加 <Reference Incl…

基于 mxgraph 实现流程图

mxgraph 可以实现复杂的流程图绘制。mxGraph里的Graph指的是图论(Graph Theory)里的图而不是柱状图、饼图和甘特图等图(chart)&#xff0c;因此想找这些图的读者可以结束阅读了。 作为图论的图&#xff0c;它包含点和边&#xff0c;如下图所示。 交通图 横道图 架构图 mxGrap…

21.Excel自动化:如何使用 xlwings 进行编程

一 将Excel用作数据查看器 使用 xlwings 中的 view 函数。 1.导包 import datetime as dt import xlwings as xw import pandas as pd import numpy as np 2.view 函数 创建一个基于伪随机数的DataFrame&#xff0c;它有足够多的行&#xff0c;使得只有首尾几行会被显示。 df …

STL之空间配置器

1. 什么是空间配置器 空间配置器&#xff0c;顾名思义就是为各个容器高效的管理空间(空间的申请与回收)的&#xff0c;在默默地工作。虽然在常规使用STL时&#xff0c;可能用不到它&#xff0c;但站在学习研究的角度&#xff0c;学习它的实现原理对我们有很大的帮助。 2. 为什…

Axure项目实战:智慧城市APP(三)教育查询(显示与隐藏交互)

亲爱的小伙伴&#xff0c;在您浏览之前&#xff0c;烦请关注一下&#xff0c;在此深表感谢&#xff01; 课程主题&#xff1a;教育查询 主要内容&#xff1a;教育公告信息&#xff0c;小升初、初升高、高考成绩查询&#xff1b;教育公告信息为传统的信息页面&#xff0c;小升…

最大字段和问题 C++(穷举、分治法、动态规划)

问题描述 给定由n个整数&#xff08;包含负整数&#xff09;组成的序列a1,a2,…,an&#xff0c;求该序列子段和的最大值。规定当所有整数均为负值时定义其最大子段和为0 穷举法 最简单的方法就是穷举法&#xff0c;用一个变量指示求和的开始位置&#xff0c;一个变量指示结束…

【数据转换】- Halcon<->Mat

背景介绍 最近在写C#联合Haclon调用C的.dll文件进行联合编程。大致需求就是C#设计界面&#xff0c;然后调用Haclon的图像处理库&#xff0c;C把目标检测的模型进行TensorRT部署生成动态链接库&#xff0c;之后界面操作加载模型、对图像进行检测等功能。 设计界面如下&#xf…

MFC中如何判断一个窗口当前状态是显示还是隐藏

文章目录 一、核心方法&#xff1a;使用 CWnd::IsWindowVisible函数原型示例代码 二、注意事项1. 父窗口的影响2. 窗口最小化/最大化状态3. 窗口尚未创建 三、扩展&#xff1a;通过窗口样式直接判断四、完整示例代码五、总结 在MFC中&#xff0c;判断窗口当前是显示还是隐藏状态…

Java 大视界 -- 基于 Java 的大数据分布式系统的监控与运维实践(155)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…

消息队列性能比拼: Kafka vs RabbitMQ

本内容是对知名性能评测博主 Anton Putra Kafka vs RabbitMQ Performance 内容的翻译与整理, 有适当删减, 相关数据和结论以原作结论为准。 简介 在本视频中&#xff0c;我们将首先比较 Apache Kafka 和传统的 RabbitMQ。然后&#xff0c;在第二轮测试中&#xff0c;会将 Kaf…

[ComfyUI] SDXL Prompt Styler 自定义节点的作用解析

1. SDXL Prompt Styler 的位置与基本功能 在 ComfyUI 的 “新建节点” → “实用工具” 下,可以找到 Style 节点(SDXL Prompt Styler)。该节点的主要作用是对输入的描述进行结构化处理,并在转换为 Stable Diffusion XL (SDXL) 提示词时,自动补充风格相关的内容,使提示词…

【JavaScript】金丹期功法

目录 数组声明数组数组的基本使用遍历数组案例&#xff1a;求数组中的最值数组操作查询数据修改数据新增数据案例&#xff1a;数组筛选删除数据 案例&#xff1a;渲染柱形图 数组 数组&#xff08;Array&#xff09;是一种可以按顺序保存数据的数据类型 场景&#xff1a;如果…

学习本地部署DeepSeek的过程(基于LM Studio)

除了使用Ollama部署DeepSeek&#xff0c;还可以使用LM Studio部署DeepSeek&#xff0c;后者是一款允许用户在本地计算机上运行大型语言模型&#xff08;LLMs&#xff09;的桌面应用程序&#xff0c;旨在简化本地模型的使用&#xff0c;无需云端连接或复杂配置即可体验 AI 功能。…

AOA与TOA混合定位,MATLAB例程,自适应基站数量,三维空间下的运动轨迹,滤波使用EKF

本代码实现了一个基于 到达角(AOA) 和 到达时间(TOA) 的混合定位算法,结合 扩展卡尔曼滤波(EKF) 对三维运动目标的轨迹进行滤波优化。代码通过模拟动态目标与基站网络,展示了从信号测量、定位解算到轨迹滤波的全流程,适用于城市峡谷、室内等复杂环境下的定位研究。 文…

C++:函数(通识版)

一、函数的基础 1.什么是函数&#xff1f;&#xff08;独立的功能单位&#xff09; 函数是C中封装代码逻辑的基本单元&#xff0c;用于执行特定任务。 作用&#xff1a;代码复用、模块化、提高可读性。 2、函数的基本结构 返回类型 函数名(参数列表) {// 函数体return 返回值…

STL之map和set

1. 关联式容器 vector、list、deque、 forward_list(C11)等&#xff0c;这些容器统称为序列式容器&#xff0c;因为其底层为线性序列的数据结构&#xff0c;里面存储的是元素本身。 关联式容器也是用来存储数据的&#xff0c;与序列式容器不同的是&#xff0c;其里面存储的是结…

虚拟机(一):Java 篇

虚拟机&#xff08;一&#xff09;&#xff1a;Java 篇 虚拟机&#xff08;二&#xff09;&#xff1a;Android 篇 架构 运行时数据区&#xff1a; 栈&#xff1a; 堆&#xff1a; 堆&#xff1a;通过new创建的对象都在堆中分配。OutOfMemoryError TLAB(Thread Local All…

无线安灯按钮盒汽车零部件工厂的故障告警与人员调度专家

在汽车零部件制造领域&#xff0c;生产线故障与物料短缺等问题往往引发连锁反应&#xff0c;导致停机损失与成本激增。传统人工巡检与纸质工单模式已难以满足高效生产需求&#xff0c;而无线安灯按钮盒的智能化应用&#xff0c;正成为破解这一难题的关键利器。 一、精准告警&am…