【Linux系统编程】第三十一弹---深入理解静态库:从零开始制作与高效使用的完全指南
✨个人主页: 熬夜学编程的小林
💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】【Linux系统编程】
目录
1、静态库
1.1、怎么做静态库
1.2、怎么使用静态库
1、静态库
1.1、怎么做静态库
在Linux环境下,通常使用GCC(GNU Compiler Collection)编译器来编译源代码,并使用
ar(archiver)工具来创建静态库。
-
编写源代码:首先,你需要有一些源代码文件,比如 x.c ,y.c ,z.c
-
编译源代码为对象文件:使用GCC编译器将源代码编译为目标文件(.o文件)。
-
创建静态库:使用
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系统编程】第三十一弹---深入理解静态库:从零开始制作与高效使用的完全指南
✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】 目录 1、静态库 1.1、怎么做静态库 1.2、怎么使用静态库 1、静态库 1.1、怎么做静态库 在Linux环境下,通常使用GCC&am…...
FFmpeg 简介及其下载安装步骤
目录 一、FFmpeg 简介 二、FFmpeg 安装步骤 2.1 打开官网 2.2 选择FFmpeg系统版本 2.3 下载FFmpeg压缩包 2.4 将下载好的压缩包进行解压 2.5 设置环境变量 2.5.1 在搜索栏中搜索【环境变量】,然后单击将其打开 2.5.2 找到系统变量中的【Path】,点…...
使用CSS+SVG实现加载动画
使用CSSSVG实现加载动画 效果展示 CSS知识点 SVG元素使用SVG相关CSS属性运用 整体页面布局 <section><div class"box"><div class"loader"><svg><circle cx"40" cy"40" r"40"></circl…...
物联网(IoT)的未来发展:智能互联时代的到来
物联网(IoT)的未来发展:智能互联时代的到来 物联网(IoT)正在迅速改变我们与世界互动的方式。无论是智能家居、智慧城市,还是工业自动化,物联网技术通过设备互联、数据采集和智能控制࿰…...
斯坦福 CS229 I 机器学习 I 构建大型语言模型 (LLMs)
1. Pretraining -> GPT3 1.1. Task & loss 1.1.1. 训练 LLMs 时的关键点 对于 LLMs 的训练来说,Architecture(架构)、Training algorithm/loss(训练算法/损失函数)、Data(数据)、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^} # 把变量中的所有小写字母,全部替换为大写 echo ${var^^} # 把变量中的第一个字符换成小写 echo ${var,} # 把变量中的所有大写字母,全部替换为小写 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系列,yum部署jenkins2.479.1,2024年长期支持版本 0、介绍 注意:jenkins建议安装LTS长期支持版本,而不是安装每周更新版本,jenkins安装指定版本 openjdk官网下载 Index of /jenkins/redhat-stable/ | 清华大学开…...
正则表达式-“三剑客”(grep、sed、awk)
1.3正则表达式 正则表达式描述了一种字符串匹配的模式,可以用来检查一个串是否含有某种子串,将匹配的子串替换或者从某个串中取出符号某个条件的子串等,在linux中代表自定义的模式模版,linux工具可以用正则表达式过滤文本。Linux…...
数智时代的新航向:The Open Group 2024生态系统架构·可持续发展年度大会邀您共筑AI数字新时代
在全球可持续发展和数字化转型双重驱动下,企业正面临着前所未有的挑战与机遇。如何在激烈的市场竞争中,实现业务增长的同时,履行社会责任,达成可持续发展的目标?The Open Group 2024生态系统架构可持续发展年度大会将于…...
TensorFlow 的核心概念
TensorFlow 是一个开源的机器学习框架,由 Google 开发和维护。它提供了一个强大的工具集,用于构建和训练各种机器学习模型。 TensorFlow 的核心概念是计算图(Computational Graph)。计算图由节点(Nodes)和…...
SpringBoot教程(二十四) | SpringBoot实现分布式定时任务之Quartz(动态新增、修改等操作)
SpringBoot教程(二十四) | SpringBoot实现分布式定时任务之Quartz(动态新增、修改等操作) 前言数据库脚本创建需要被调度的方法创建相关实体类创建业务层接口创建业务层实现类控制层类测试结果 前言 我这边的SpringBoot的版本为2…...
Matlab详细学习教程 MATLAB使用教程与知识点总结
Matlab语言教程 章节目录 一、Matlab简介与基础操作 二、变量与数据类型 三、矩阵与数组操作 四、基本数学运算与函数 五、图形绘制与数据可视化 六、控制流与逻辑运算 七、脚本与函数编写 八、数据导入与导出 九、Matlab应用实例分析 一、Matlab简介与基础操作 重点内容知识…...
【ELKB】Kibana使用
搭建好ELKB后访问地址:http://localhost:5601 输入账号密码登录以后 左侧导航有home、Analysis、Enterprise search 、Observability、Security、Management home:首页Analysis:工具来分析及可视化数据Enterprise search:企业级搜…...
ChatGPT免费使用:人工智能在现代社会中的作用
随着人工智能技术的不断发展,越来越多的应用程序和工具开始使用GPT作为其语言模型。但是,这些应用程序和工具是否收费?如果是免费的,那么他们是如何盈利的?在本文中,我们将探讨ChatGPT免费使用的背后原理&a…...
腾讯音乐:从 Elasticsearch 到 Apache Doris 内容库升级,统一搜索分析引擎,成本直降 80%
导读: 为满足更严苛数据分析的需求,腾讯音乐借助 Apache Doris 替代了 Elasticsearch 集群,统一了内容库数据平台的内容搜索和分析引擎。并基于 Doris 倒排索引和全文检索的能力,支持了复杂的自定义标签计算,实现秒级查…...
CubeMX的FreeRTOS学习
一、FreeRTOS的介绍 什么是FreeRTOS? Free即免费的,RTOS的全称是Real Time Operating system,中文就是实时操作系统。 注意:RTOS不是指某一个确定的系统,而是指一类的操作系统。比如:us/OS,FreeRTOS&…...
C语言初始:数据类型和变量
、 一.数据类型介绍 人有黄人白人黑人,那么数据呢? 我们大家可以看出谁是黄种人,谁是白种人,谁是黑种人,这是因为他们是类似的。 数据也是有类型的,就譬如整数类型,字符类型,浮点…...
Linux shellcheck工具
安装工具 通过linux yum源下载,可能因为yum源的问题找不到软件包,或者下载的软件包版本太旧。 ShellCheck的源代码托管在GitHub上(推荐下载方式): GitHub - koalaman/shellcheck: ShellCheck, a static analysis tool for shell scripts 对下…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
Java 8 Stream API 入门到实践详解
一、告别 for 循环! 传统痛点: Java 8 之前,集合操作离不开冗长的 for 循环和匿名类。例如,过滤列表中的偶数: List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...
【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...
《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...
python执行测试用例,allure报乱码且未成功生成报告
allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…...
处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的
修改bug思路: 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑:async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...
Linux 中如何提取压缩文件 ?
Linux 是一种流行的开源操作系统,它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间,使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的,要在 …...
打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用
一、方案背景 在现代生产与生活场景中,如工厂高危作业区、医院手术室、公共场景等,人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式,存在效率低、覆盖面不足、判断主观性强等问题,难以满足对人员打手机行为精…...
Proxmox Mail Gateway安装指南:从零开始配置高效邮件过滤系统
💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「storms…...
