当前位置: 首页 > news >正文

【Linux系统编程】第三十一弹---深入理解静态库:从零开始制作与高效使用的完全指南

个人主页: 熬夜学编程的小林

💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】【Linux系统编程】

目录

1、静态库

1.1、怎么做静态库

1.2、怎么使用静态库


1、静态库

1.1、怎么做静态库

在Linux环境下,通常使用GCC(GNU Compiler Collection)编译器来编译源代码,并使用ar(archiver)工具来创建静态库。

  1. 编写源代码:首先,你需要有一些源代码文件,比如 x.c ,y.c ,z.c

  2. 编译源代码为对象文件:使用GCC编译器将源代码编译为目标文件(.o文件)

  3. 创建静态库:使用 ar工具将对象文件打包成静态库

头文件是一个手册提供函数的声明,告诉用户怎么用;.o提供实现,我们只需要补上一个main函数,调用头文件提供的方法,然后和.o进行链接,就能形成可执行

mymath.h

#pragma once // 防止头文件重复包含#include <stdio.h>int Add(int x,int y);

mymath.c

#include "mymath.h"int Add(int x,int y)
{return x + y;
}

mystdio.h

#pragma once #include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>#define LINE_SIZE 1024
#define FLUSH_NOW  1
#define FLUSH_LINE 2
#define FLUSH_FULL 4typedef struct _myFILE  
{unsigned int flags;int fileno;// 缓冲区char cache[LINE_SIZE];int cap;// 容量int pos;// 下次写入的位置
}myFILE;myFILE* my_fopen(const char* path,const char* flag);
void my_fflush(myFILE* fp);
ssize_t my_fwrite(myFILE* fp,const char* data,int len);
void my_fclose(myFILE* fp);

mystdio.c

#include "mystdio.h"myFILE* my_fopen(const char* path,const char* flag)
{int flag1 = 0;int iscreate = 0;mode_t mode = 0666;if(strcmp(flag,"r") == 0){flag1 = O_RDONLY;}else if(strcmp(flag,"w") == 0){flag1 = (O_WRONLY | O_CREAT | O_TRUNC);iscreate = 1;}else if(strcmp(flag,"a") == 0){flag1 = (O_WRONLY | O_CREAT | O_APPEND);iscreate = 1;}else {}int fd = 0;if(iscreate)fd = open(path,flag1,mode);else fd = open(path,flag1);if(fd < 0) return NULL;myFILE* fp = (myFILE*)malloc(sizeof(myFILE));if(fp == NULL) return NULL;fp->fileno = fd;fp->flags = FLUSH_LINE;fp->cap = LINE_SIZE;fp->pos = 0;return fp;
}
void my_fflush(myFILE* fp)
{write(fp->fileno,fp->cache,fp->pos);fp->pos = 0;
}
ssize_t my_fwrite(myFILE* fp,const char* data,int len)
{// 写入的本质是拷贝,条件允许就刷新memcpy(fp->cache + fp->pos ,data,len);// 考虑扩容与越界问题fp->pos += len;if((fp->flags&FLUSH_LINE) && fp->cache[fp->pos-1] == '\n'){my_fflush(fp);}return len; 
}
void my_fclose(myFILE* fp)
{my_fflush(fp);close(fp->fileno);free(fp);
}

main.c

#include "mymath.h"
#include "mystdio.h"
#include <string.h>
#include <stdio.h>int main()
{int a = 10;int b = 20;printf("%d + %d  = %d\n",a,b,myAdd(a,b));myFILE* fp = my_fopen("log.txt","w");if(fp == NULL) return 1;const char* message = "这是我写的...\n";my_fwrite(fp,message,strlen(message));my_fclose(fp);return 0;
}

编译并执行程序

[jkl@host lib]$ gcc main.c mymath.c mystdio.c
[jkl@host lib]$ ls
a.out  log.txt  main.c  mymath.c  mymath.h  mystdio.c  mystdio.h  
[jkl@host lib]$ ./a.out
10 + 20  = 30
[jkl@host lib]$ cat log.txt
这是我写的...

将.c文件(源文件)编译成.o文件(目标文件) [ -c选项告诉GCC只编译和汇编,但不链接]

gcc -c mymath.c  # 将mymath.c文件编译成.o文件,默认编译成mymath.o
gcc -c mystdio.c # 将mystdio.c文件编译成.o文件,默认编译成mystdio.o

 ​​​​​​

使用.h 文件和.o 文件编译main.c程序

[jkl@host roommate]$ ls
main.c  mymath.h  mymath.o  mystdio.h  mystdio.o
[jkl@host roommate]$ gcc main.c
/tmp/ccFk2rMI.o: In function `main':
main.c:(.text+0x21): undefined reference to `myAdd'
main.c:(.text+0x49): undefined reference to `my_fopen'
main.c:(.text+0x84): undefined reference to `my_fwrite'
main.c:(.text+0x90): undefined reference to `my_fclose'
collect2: error: ld returned 1 exit status

 gcc main.c 只编译了main.c文件,并没有包含对mymath.o 和 mystdio.h 的链接操作,因为main.c 依赖于mymath.h 和 mystdio.h 中声明的函数,因此仅编译main.c是不够的。

解决办法一:

gcc main.c mymath.o mystdio.o -o myexe

将.o文件 和.c文件一起编译链接。 

解决办法二:

将main.o也编译成.o文件

[jkl@host roommate]$ gcc -c main.c
[jkl@host roommate]$ ls
main.c  main.o  mymath.h  mymath.o  mystdio.h  mystdio.o
[jkl@host roommate]$ gcc mymath.o mystdio.o main.o -o myexe
[jkl@host roommate]$ ls
main.c  main.o  myexe  mymath.h  mymath.o  mystdio.h  mystdio.o
[jkl@host roommate]$ ./myexe
10 + 20  = 30
[jkl@host roommate]$ ls
log.txt  main.c  main.o  myexe  mymath.h  mymath.o  mystdio.h  mystdio.o
[jkl@host roommate]$ cat log.txt
这是我写的...

通过ar指令将所有.o文件打包: 

ar -rc libmyc.a *.o # 将所有.o文件打包成libmyc.a文件

r(replace) 选项表示替换库中已存在的文件

c(create) 选项表示创建一个新的库 。

1.2、怎么使用静态库

  • 方式一:直接使用打包的文件

为了更好的使用静态库,我们把前面打包的文件拷贝到另外的目录进行操作。

cp libmyc.a roommate/ # 将打包的文件拷贝到下级目录下
[jkl@host roommate]$ cp ../mymath.h . # 将.h文件拷贝到下级目录
[jkl@host roommate]$ cp ../mystdio.h .
[jkl@host roommate]$ ls
main.c  myexe  mymath.h  mymath.o  mystdio.h  mystdio.o

 直接使用gcc 编译

[jkl@host roommate]$ gcc main.c
/tmp/ccuZcLr1.o: In function `main':
main.c:(.text+0x21): undefined reference to `myAdd'
main.c:(.text+0x49): undefined reference to `my_fopen'
main.c:(.text+0x84): undefined reference to `my_fwrite'
main.c:(.text+0x90): undefined reference to `my_fclose'
collect2: error: ld returned 1 exit status

使用gcc 编译main.c 和 libmyc.a 

[jkl@host roommate]$ gcc main.c libmyc.a
[jkl@host roommate]$ ls
a.out  libmyc.a  main.c  mymath.h  mystdio.h
[jkl@host roommate]$ ./a.out
10 + 20  = 30
[jkl@host roommate]$ ls
a.out  libmyc.a  log.txt  main.c  mymath.h  mystdio.h
[jkl@host roommate]$ cat log.txt
这是我写的...
  •  方式二:将打包的文件拷贝到系统库中(严重不推荐)

我们可以将自己写的.h头文件写到/usr/bin/目录下。

我们可以将自己打包的方法实现文件写到/usr/bin54/目录下。 

 

查看拷贝的文件 

[jkl@host roommate]$ ls /usr/include/mymath.h
/usr/include/mymath.h
[jkl@host roommate]$ ls /usr/include/mystdio.h
/usr/include/mystdio.h
[jkl@host roommate]$ ls /usr/lib64/libmyc.a
/usr/lib64/libmyc.a

把上面的两步操作做完之后,我们可以直接编译main函数,头文件可以使用<>

main.c

#include <mymath.h>
#include <mystdio.h>
#include <stdio.h>
#include <string.h>int main()
{int a = 10;int b = 20;printf("%d + %d  = %d\n",a,b,myAdd(a,b));myFILE* fp = my_fopen("log.txt","w");if(fp == NULL) return 1;const char* message = "这是我写的...\n";my_fwrite(fp,message,strlen(message));my_fclose(fp);return 0;
}

直接使用gcc编译还是会报错,因为该方法的实现是我们自己写的,gcc/g++不认识,所以直接编译会报错。 

[jkl@host roommate]$ gcc main.c
/tmp/ccZqyRSO.o: In function `main':
main.c:(.text+0x21): undefined reference to `myAdd'
main.c:(.text+0x49): undefined reference to `my_fopen'
main.c:(.text+0x84): undefined reference to `my_fwrite'
main.c:(.text+0x90): undefined reference to `my_fclose'
collect2: error: ld returned 1 exit status

在gcc编译.c文件之后需要加参数,-l libmyc.a,且需要去掉lib和.a,因此正确的命令是gcc main.c -lmyc (-l后面可以加空格也可以不加空格) 

[jkl@host roommate]$ gcc main.c -lmyc
[jkl@host roommate]$ ls
a.out  main.c  mylib
[jkl@host roommate]$ ./a.out
10 + 20  = 30
[jkl@host roommate]$ ls
a.out  log.txt  main.c  mylib
[jkl@host roommate]$ cat log.txt
这是我写的...

 第二种方式不推荐,因此演示完之后最好将拷贝的文件给删除掉。

[jkl@host roommate]$ sudo rm /usr/include/mymath.h
[jkl@host roommate]$ sudo rm /usr/include/mystdio.h
[jkl@host roommate]$ sudo rm /usr/lib64/libmyc.a
[jkl@host roommate]$ ls /usr/include/mymath.h
ls: cannot access /usr/include/mymath.h: No such file or directory
[jkl@host roommate]$ ls /usr/include/mystdio.h
ls: cannot access /usr/include/mystdio.h: No such file or directory
[jkl@host roommate]$ ls /usr/lib64/libmyc.a
ls: cannot access /usr/lib64/libmyc.a: No such file or directory

 方式三:通过命令链接静态库

[jkl@host roommate]$ tree .
.
|-- main.c
`-- mylib|-- include|   |-- mymath.h|   `-- mystdio.h`-- lib`-- libmyc.a3 directories, 4 files

为什么不能直接使用 gcc main.c myc.a?

 因为告诉了gcc/g++编译器,但是没有告诉操作系统!!!

使用静态库:在编译其他程序时,可以通过-I(指定用户自定义头文件搜索路径)  -L(指定用户自定义库文件搜索路径)和 -l(执行确定的第三方库名称,去掉前缀lib和后缀.a)选项来链接静态库

[jkl@host roommate]$ gcc main.c -I ./mylib/include/ -L ./mylib/lib -lmyc
[jkl@host roommate]$ ls
a.out  main.c  mylib
[jkl@host roommate]$ ./a.out
10 + 20  = 30
[jkl@host roommate]$ ls
a.out  log.txt  main.c  mylib
[jkl@host roommate]$ cat log.txt
这是我写的...

上面是动态链接的

[jkl@host roommate]$ ldd a.outlinux-vdso.so.1 =>  (0x00007ffef6bf9000)libc.so.6 => /lib64/libc.so.6 (0x00007f0448055000)/lib64/ld-linux-x86-64.so.2 (0x00007f0448423000)

 gcc在不使用static选项的时候,并且只提供.a,只能静态链接当前的.a库,其他库正常动态链接,因此ldd能够查看动态库。

想要静态链接得加 -static

[jkl@host roommate]$ gcc main.c -I ./mylib/include/ -L ./mylib/lib -lmyc -static
[jkl@host roommate]$ ls
a.out  log.txt  main.c  mylib
[jkl@host roommate]$ ldd a.outnot a dynamic executable
[jkl@host roommate]$ file a.out
a.out: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=b10e09a9d03b05ebc14934c15a9d8b7071c94c29, not stripped

-static的意义是什么?

必须强制添加,因为将我们的程序进行静态链接,这要求我们链接的任何库都必须提供对应的静态库版本。

相关文章:

【Linux系统编程】第三十一弹---深入理解静态库:从零开始制作与高效使用的完全指南

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】 目录 1、静态库 1.1、怎么做静态库 1.2、怎么使用静态库 1、静态库 1.1、怎么做静态库 在Linux环境下&#xff0c;通常使用GCC&am…...

FFmpeg 简介及其下载安装步骤

目录 一、FFmpeg 简介 二、FFmpeg 安装步骤 2.1 打开官网 2.2 选择FFmpeg系统版本 2.3 下载FFmpeg压缩包 2.4 将下载好的压缩包进行解压 2.5 设置环境变量 2.5.1 在搜索栏中搜索【环境变量】&#xff0c;然后单击将其打开 2.5.2 找到系统变量中的【Path】&#xff0c;点…...

使用CSS+SVG实现加载动画

使用CSSSVG实现加载动画 效果展示 CSS知识点 SVG元素使用SVG相关CSS属性运用 整体页面布局 <section><div class"box"><div class"loader"><svg><circle cx"40" cy"40" r"40"></circl…...

物联网(IoT)的未来发展:智能互联时代的到来

物联网&#xff08;IoT&#xff09;的未来发展&#xff1a;智能互联时代的到来 物联网&#xff08;IoT&#xff09;正在迅速改变我们与世界互动的方式。无论是智能家居、智慧城市&#xff0c;还是工业自动化&#xff0c;物联网技术通过设备互联、数据采集和智能控制&#xff0…...

斯坦福 CS229 I 机器学习 I 构建大型语言模型 (LLMs)

1. Pretraining -> GPT3 1.1. Task & loss 1.1.1. 训练 LLMs 时的关键点 对于 LLMs 的训练来说&#xff0c;Architecture&#xff08;架构&#xff09;、Training algorithm/loss&#xff08;训练算法/损失函数&#xff09;、Data&#xff08;数据&#xff09;、Evalu…...

Java->排序

目录 一、排序 1.概念 2.常见的排序算法 二、常见排序算法的实现 1.插入排序 1.1直接插入排序 1.2希尔排序(缩小增量法) 1.3直接插入排序和希尔排序的耗时比较 2.选择排序 2.1直接选择排序 2.2堆排序 2.3直接选择排序与堆排序的耗时比较 3.交换排序 3.1冒泡排序…...

linux 大小写转换

var"TM_card_INFo" # 把变量中的第一个字符换成大写 echo ${var^} # 把变量中的所有小写字母&#xff0c;全部替换为大写 echo ${var^^} # 把变量中的第一个字符换成小写 echo ${var,} # 把变量中的所有大写字母&#xff0c;全部替换为小写 echo ${var,,} 参考…...

Linux——传输层协议

目录 一再谈端口号 1端口号范围划分 2两个问题 3理解进程与端口号的关系 二UDP协议 1格式 2特点 3进一步理解 3.1关于UDP报头 3.2关于报文 4基于UDP的应用层协议 三TCP协议 1格式 2TCP基本通信 2.1关于可靠性 2.2TCP通信模式 3超时重传 4连接管理 4.1建立…...

centos系列,yum部署jenkins2.479.1,2024年长期支持版本

centos系列&#xff0c;yum部署jenkins2.479.1&#xff0c;2024年长期支持版本 0、介绍 注意&#xff1a;jenkins建议安装LTS长期支持版本&#xff0c;而不是安装每周更新版本&#xff0c;jenkins安装指定版本 openjdk官网下载 Index of /jenkins/redhat-stable/ | 清华大学开…...

正则表达式-“三剑客”(grep、sed、awk)

1.3正则表达式 正则表达式描述了一种字符串匹配的模式&#xff0c;可以用来检查一个串是否含有某种子串&#xff0c;将匹配的子串替换或者从某个串中取出符号某个条件的子串等&#xff0c;在linux中代表自定义的模式模版&#xff0c;linux工具可以用正则表达式过滤文本。Linux…...

数智时代的新航向:The Open Group 2024生态系统架构·可持续发展年度大会邀您共筑AI数字新时代

在全球可持续发展和数字化转型双重驱动下&#xff0c;企业正面临着前所未有的挑战与机遇。如何在激烈的市场竞争中&#xff0c;实现业务增长的同时&#xff0c;履行社会责任&#xff0c;达成可持续发展的目标&#xff1f;The Open Group 2024生态系统架构可持续发展年度大会将于…...

TensorFlow 的核心概念

TensorFlow 是一个开源的机器学习框架&#xff0c;由 Google 开发和维护。它提供了一个强大的工具集&#xff0c;用于构建和训练各种机器学习模型。 TensorFlow 的核心概念是计算图&#xff08;Computational Graph&#xff09;。计算图由节点&#xff08;Nodes&#xff09;和…...

SpringBoot教程(二十四) | SpringBoot实现分布式定时任务之Quartz(动态新增、修改等操作)

SpringBoot教程&#xff08;二十四&#xff09; | SpringBoot实现分布式定时任务之Quartz&#xff08;动态新增、修改等操作&#xff09; 前言数据库脚本创建需要被调度的方法创建相关实体类创建业务层接口创建业务层实现类控制层类测试结果 前言 我这边的SpringBoot的版本为2…...

Matlab详细学习教程 MATLAB使用教程与知识点总结

Matlab语言教程 章节目录 一、Matlab简介与基础操作 二、变量与数据类型 三、矩阵与数组操作 四、基本数学运算与函数 五、图形绘制与数据可视化 六、控制流与逻辑运算 七、脚本与函数编写 八、数据导入与导出 九、Matlab应用实例分析 一、Matlab简介与基础操作 重点内容知识…...

【ELKB】Kibana使用

搭建好ELKB后访问地址&#xff1a;http://localhost:5601 输入账号密码登录以后 左侧导航有home、Analysis、Enterprise search 、Observability、Security、Management home&#xff1a;首页Analysis&#xff1a;工具来分析及可视化数据Enterprise search&#xff1a;企业级搜…...

ChatGPT免费使用:人工智能在现代社会中的作用

随着人工智能技术的不断发展&#xff0c;越来越多的应用程序和工具开始使用GPT作为其语言模型。但是&#xff0c;这些应用程序和工具是否收费&#xff1f;如果是免费的&#xff0c;那么他们是如何盈利的&#xff1f;在本文中&#xff0c;我们将探讨ChatGPT免费使用的背后原理&a…...

腾讯音乐:从 Elasticsearch 到 Apache Doris 内容库升级,统一搜索分析引擎,成本直降 80%

导读&#xff1a; 为满足更严苛数据分析的需求&#xff0c;腾讯音乐借助 Apache Doris 替代了 Elasticsearch 集群&#xff0c;统一了内容库数据平台的内容搜索和分析引擎。并基于 Doris 倒排索引和全文检索的能力&#xff0c;支持了复杂的自定义标签计算&#xff0c;实现秒级查…...

CubeMX的FreeRTOS学习

一、FreeRTOS的介绍 什么是FreeRTOS&#xff1f; Free即免费的&#xff0c;RTOS的全称是Real Time Operating system,中文就是实时操作系统。 注意&#xff1a;RTOS不是指某一个确定的系统&#xff0c;而是指一类的操作系统。比如&#xff1a;us/OS&#xff0c;FreeRTOS&…...

C语言初始:数据类型和变量

、 一.数据类型介绍 人有黄人白人黑人&#xff0c;那么数据呢&#xff1f; 我们大家可以看出谁是黄种人&#xff0c;谁是白种人&#xff0c;谁是黑种人&#xff0c;这是因为他们是类似的。 数据也是有类型的&#xff0c;就譬如整数类型&#xff0c;字符类型&#xff0c;浮点…...

Linux shellcheck工具

安装工具 通过linux yum源下载&#xff0c;可能因为yum源的问题找不到软件包&#xff0c;或者下载的软件包版本太旧。 ShellCheck的源代码托管在GitHub上(推荐下载方式)&#xff1a; GitHub - koalaman/shellcheck: ShellCheck, a static analysis tool for shell scripts 对下…...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

OpenLayers 可视化之热力图

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 热力图&#xff08;Heatmap&#xff09;又叫热点图&#xff0c;是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

学校招生小程序源码介绍

基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码&#xff0c;专为学校招生场景量身打造&#xff0c;功能实用且操作便捷。 从技术架构来看&#xff0c;ThinkPHP提供稳定可靠的后台服务&#xff0c;FastAdmin加速开发流程&#xff0c;UniApp则保障小程序在多端有良好的兼…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具

文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

NFT模式:数字资产确权与链游经济系统构建

NFT模式&#xff1a;数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新&#xff1a;构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议&#xff1a;基于LayerZero协议实现以太坊、Solana等公链资产互通&#xff0c;通过零知…...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建

华为云FlexusDeepSeek征文&#xff5c;DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色&#xff0c;华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型&#xff0c;能助力我们轻松驾驭 DeepSeek-V3/R1&#xff0c;本文中将分享如何…...

是否存在路径(FIFOBB算法)

题目描述 一个具有 n 个顶点e条边的无向图&#xff0c;该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序&#xff0c;确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数&#xff0c;分别表示n 和 e 的值&#xff08;1…...

AI书签管理工具开发全记录(十九):嵌入资源处理

1.前言 &#x1f4dd; 在上一篇文章中&#xff0c;我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源&#xff0c;方便后续将资源打包到一个可执行文件中。 2.embed介绍 &#x1f3af; Go 1.16 引入了革命性的 embed 包&#xff0c;彻底改变了静态资源管理的…...

HashMap中的put方法执行流程(流程图)

1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中&#xff0c;其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下&#xff1a; 初始判断与哈希计算&#xff1a; 首先&#xff0c;putVal 方法会检查当前的 table&#xff08;也就…...

2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)

安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…...