C 语言工业级通用组件手写 11:环形 FIFO 缓冲区
目录前言一、环形 FIFO 核心本质与应用场景1. 什么是环形缓冲区2. 解决的核心痛点3. 典型工业级落地场景二、核心实现原理1. 双指针循环机制2. 零拷贝读写3. 字节流式存储三、工业级设计规范1. 封装性设计2. 接口设计原则3. 鲁棒性要求4. 线程安全约束四、完整可复用源码1. 头文件 ring_fifo.h2. 实现文件 ring_fifo.c五、实战演示环形缓冲区读写示例六、工业级进阶优化方向1. 线程安全加锁封装2. 单次一字节读写接口3. peek 窥探接口4. 批量分段读写七、高频面试考点与易错坑点1. 经典面试问答2. 常见易错坑点总结前言在嵌入式串口接收、网络报文缓冲、音频数据流、传感器高频采集、异步数据收发场景中普通数组缓存存在溢出、覆盖、数据丢失、读写冲突等致命问题。普通数组无脑缓存必须手动判空、判满、移动数据代码臃肿、极易出错、无法高频运行。环形 FIFO循环缓冲区是工业流式数据缓存的标准解决方案。 本篇手写一套极简、零拷贝、全覆盖、读写分离、防溢出、可线程安全的工业级环形缓冲区源码干净、无冗余、可直接投产适配所有流式数据收发场景。一、环形 FIFO 核心本质与应用场景1. 什么是环形缓冲区环形 FIFO 是首尾相连的循环队列。依靠读指针(r)、写指针(w)实现循环读写无需数据搬迁、零拷贝、性能极致。核心特性先进先出、循环复用内存、无数据挪动、无内存碎片、读写高效。2. 解决的核心痛点解决数组缓存溢出覆盖满状态自动拦截写入杜绝数据丢失。解决数据搬迁耗时普通队列出队需要整体前移FIFO 零搬迁。解决高频数据流卡顿O (1) 读写百万级吞吐无压力。解决收发速率不匹配缓存削峰适配突发数据、高频采样。解决代码冗余混乱统一标准接口全局通用缓存组件。3. 典型工业级落地场景串口 / 485/CAN 接收缓存中断高速接收、主线程慢速解析。网络数据流缓冲TCP 流式分包、UDP 报文缓存。音视频采集音频帧、视频帧连续流式缓存。传感器高频采集高速 AD、姿态、雷达数据缓存。异步解耦架构生产快、消费慢场景削峰缓冲。二、核心实现原理1. 双指针循环机制write_ptr数据写入位置read_ptr数据读取位置缓冲区满(write 1) % size read缓冲区空write read预留一个空位区分空 / 满状态是工业最简、零 BUG 标准方案。2. 零拷贝读写读写直接操作数组指针不拷贝、不移动、不遍历性能拉满。3. 字节流式存储按字节缓存适配任意协议、任意报文、任意长度数据通用性最强。三、工业级设计规范1. 封装性设计FIFO 结构体完全封装缓存地址、读写指针、缓冲区大小。外部不可篡改内部状态保证读写闭环安全。2. 接口设计原则接口函数功能说明fifo_init初始化环形缓冲区fifo_write写入指定长度数据fifo_read读取指定长度数据fifo_available获取剩余可写空间fifo_used获取当前已缓存数据长度fifo_clear清空缓冲区3. 鲁棒性要求空指针校验、长度合法性校验。写满自动拒绝、读空自动返回。无越界、无覆盖、无数据错乱。纯 C 零依赖、裸机 / RTOS/Linux 全平台适配。4. 线程安全约束单生产单消费天然线程安全嵌入式中断写、主线程读标配。多生产多消费需外层加锁适配高并发场景。四、完整可复用源码1. 头文件 ring_fifo.h#ifndef RING_FIFO_H #define RING_FIFO_H #include stdint.h #include stddef.h #ifdef __cplusplus extern C { #endif /* 环形FIFO句柄 */ typedef struct { uint8_t *buf; size_t buf_size; size_t w_ptr; size_t r_ptr; } ring_fifo_t; /** * brief 初始化环形缓冲区 * param fifo FIFO句柄 * param buf 缓存数组地址 * param buf_size 缓存数组大小 */ void fifo_init(ring_fifo_t *fifo, uint8_t *buf, size_t buf_size); /** * brief 写入数据到FIFO * param fifo FIFO句柄 * param data 待写入数据 * param len 写入长度 * return 实际写入字节数 */ size_t fifo_write(ring_fifo_t *fifo, const uint8_t *data, size_t len); /** * brief 从FIFO读取数据 * param fifo FIFO句柄 * param data 接收缓存 * param len 读取长度 * return 实际读取字节数 */ size_t fifo_read(ring_fifo_t *fifo, uint8_t *data, size_t len); /** * brief 获取缓冲区剩余可写空间 */ size_t fifo_available(ring_fifo_t *fifo); /** * brief 获取缓冲区已存储数据长度 */ size_t fifo_used(ring_fifo_t *fifo); /** * brief 清空缓冲区 */ void fifo_clear(ring_fifo_t *fifo); #ifdef __cplusplus } #endif #endif2. 实现文件 ring_fifo.c#include ring_fifo.h #include string.h void fifo_init(ring_fifo_t *fifo, uint8_t *buf, size_t buf_size) { if (fifo NULL || buf NULL || buf_size 0) { return; } fifo-buf buf; fifo-buf_size buf_size; fifo-w_ptr 0; fifo-r_ptr 0; } size_t fifo_write(ring_fifo_t *fifo, const uint8_t *data, size_t len) { if (fifo NULL || data NULL || len 0) { return 0; } size_t avail fifo_available(fifo); if (avail 0) { return 0; } // 限制最大写入长度 len len avail ? avail : len; size_t size1 fifo-buf_size - fifo-w_ptr; if (size1 len) { memcpy(fifo-buf[fifo-w_ptr], data, len); } else { memcpy(fifo-buf[fifo-w_ptr], data, size1); memcpy(fifo-buf, data size1, len - size1); } fifo-w_ptr (fifo-w_ptr len) % fifo-buf_size; return len; } size_t fifo_read(ring_fifo_t *fifo, uint8_t *data, size_t len) { if (fifo NULL || data NULL || len 0) { return 0; } size_t used fifo_used(fifo); if (used 0) { return 0; } len len used ? used : len; size_t size1 fifo-buf_size - fifo-r_ptr; if (size1 len) { memcpy(data, fifo-buf[fifo-r_ptr], len); } else { memcpy(data, fifo-buf[fifo-r_ptr], size1); memcpy(data size1, fifo-buf, len - size1); } fifo-r_ptr (fifo-r_ptr len) % fifo-buf_size; return len; } size_t fifo_available(ring_fifo_t *fifo) { if (fifo NULL) { return 0; } // 预留1字节区分空满 return (fifo-buf_size - 1 fifo-r_ptr - fifo-w_ptr) % fifo-buf_size; } size_t fifo_used(ring_fifo_t *fifo) { if (fifo NULL) { return 0; } return (fifo-w_ptr fifo-buf_size - fifo-r_ptr) % fifo-buf_size; } void fifo_clear(ring_fifo_t *fifo) { if (fifo NULL) { return; } fifo-w_ptr 0; fifo-r_ptr 0; }五、实战演示环形缓冲区读写示例#include stdio.h #include ring_fifo.h #define FIFO_BUF_LEN 32 uint8_t fifo_buf[FIFO_BUF_LEN]; int main(void) { ring_fifo_t fifo; fifo_init(fifo, fifo_buf, FIFO_BUF_LEN); uint8_t w_data[] INDUSTRIAL_FIFO_11; uint8_t r_data[32] {0}; // 写入数据 size_t w_len fifo_write(fifo, w_data, sizeof(w_data)); printf(成功写入 %zu 字节\n, w_len); printf(当前已缓存%zu 字节\n, fifo_used(fifo)); // 读取数据 size_t r_len fifo_read(fifo, r_data, sizeof(r_data)); printf(成功读取 %zu 字节\n, r_len); printf(读取内容%s\n, r_data); return 0; }运行效果循环读写正常、边界无越界、缓冲区满自动截断、空读取直接返回零丢失、零错乱、零崩溃。六、工业级进阶优化方向1. 线程安全加锁封装搭配互斥锁支持多线程并发读写。2. 单次一字节读写接口适配串口中断单字节写入场景。3. peek 窥探接口只读不弹出适配协议头解析、预判断报文。4. 批量分段读写适配超大数据流高速吞吐。七、高频面试考点与易错坑点1. 经典面试问答Q1环形 FIFO 为什么要预留一个空位答解决空满状态歧义。读写指针相等既可以是空也可以是满预留一位可以完美区分是工业最简无 BUG 方案。Q2环形缓冲区相比普通数组的优势答无需数据搬迁、无溢出覆盖、循环复用内存、性能稳定、适配异步收发。Q3单中断写主线程读是否需要加锁答不需要。单生产单消费模式指针互不抢占天然线程安全是串口驱动标准写法。Q4FIFO 核心设计思想答以空间换时间、循环复用内存、解耦生产消费速率、零拷贝高效流转。2. 常见易错坑点不预留空位空满判断错乱读写指针不取模数组越界读取不判空、写入不判满导致数据异常频繁数据 memmove 挪动性能极差边界包裹读写处理错误尾部数据丢失总结环形 FIFO 缓冲区是嵌入式流式数据处理的第一组件。所有串口、网络、音频、采集工程100% 依赖环形缓存。本篇实现工业标准、边界全覆盖、零 BUG、可直接投产补齐整套组件库数据流转核心能力。创作不易如果对你有帮助欢迎点赞、收藏、转发。

相关新闻

变频器的技术应用:接线与基本参数设置

变频器的技术应用:接线与基本参数设置

部分参考自《变频器与步进/伺服驱动技术完全精通教程》 化学工业出版社 图源:部分引自《台达 VFD-M变频器 使用手册》,其余引自互联网。 学习笔记,记录一下新变频器到手后,需要进行的配置工作。 说起来,变频器属于传动…

2026/10/14 11:04:31 阅读更多 →
GameVault错误处理与调试:常见问题排查与系统稳定性保障

GameVault错误处理与调试:常见问题排查与系统稳定性保障

GameVault错误处理与调试:常见问题排查与系统稳定性保障 【免费下载链接】gamevault-app Frontend for the self-hosted gaming platform for drm-free games 项目地址: https://gitcode.com/gh_mirrors/ga/gamevault-app GameVault是一款开源的自托管游戏平…

2026/10/14 11:04:26 阅读更多 →
从SVG到原生:NGraphics矢量图形格式转换全攻略

从SVG到原生:NGraphics矢量图形格式转换全攻略

从SVG到原生:NGraphics矢量图形格式转换全攻略 【免费下载链接】NGraphics NGraphics is a cross platform library for rendering vector graphics on .NET. It provides a unified API for both immediate and retained mode graphics using high quality native …

2026/10/11 11:16:46 阅读更多 →

最新新闻

第2章:Linux 基础,pwd 命令

第2章:Linux 基础,pwd 命令

专栏导航 上一篇:第2章:Linux 基础,date 命令 回到目录 下一篇:第2章:Linux 基础,whoami 命令 本节前言 对于本节所讲解的知识,有可能,你会需要时不时地参考本专栏的其它文章。真…

2026/10/14 11:05:15 阅读更多 →
UE4 C++静态加载SkeletalMesh与动画蓝图完整实操

UE4 C++静态加载SkeletalMesh与动画蓝图完整实操

我们接着往下写。这是“基于C代码的UE4学习”系列第三十八篇,这次主题很聚焦:用C代码把 SkeletalMesh 加载到角色身上,并且静态加载一个动画蓝图。很多人在这个阶段会开始纠结——明明蓝图里拖个引用、拉个变量就行,为什么偏要用代…

2026/10/14 11:05:15 阅读更多 →
Kotlin泛型深度解析:类型擦除、型变与reified实战

Kotlin泛型深度解析:类型擦除、型变与reified实战

1. 从类型擦除说起:为什么Kotlin泛型值得单独开一个专题很多人学Kotlin泛型的时候,第一反应是"这不就是Java那套吗"。我一开始也这么想,直到在一个数据解析模块里踩了个坑——写了一个看似完美的泛型转换函数,编译通过&…

2026/10/14 11:05:15 阅读更多 →
重积分建模本质:从物理直觉到高维应用

重积分建模本质:从物理直觉到高维应用

1. 为什么重积分的应用远不止“算体积”——从物理建模到几何直觉的跃迁很多人学完二重积分,脑子里只留下一个公式:∬_D f(x,y) dσ。考试时套用极坐标换元、画个积分区域、算个数值,就以为掌握了。但我在带某高校数学分析实验课时发现&#…

2026/10/14 11:05:15 阅读更多 →
libxl 4.1.1带注册信息:C++离线生成Excel报表实践

libxl 4.1.1带注册信息:C++离线生成Excel报表实践

简介:libxl4.1.1 带注册信息资源包,聚焦高效轻量级 Excel 读写库的跨平台集成,面向需要将 Excel 处理能力嵌入应用的 C/C、C# 开发者,重点梳理 API 调用、依赖库、编译选项以及注册授权验证在 Windows 与 Linux 环境下的差异&…

2026/10/14 11:05:15 阅读更多 →
航空订票系统UML建模:用例图、时序图与类图实战指南

航空订票系统UML建模:用例图、时序图与类图实战指南

简介:航空订票系统是典型的信息管理系统设计案例,适合软件工程、系统分析与设计课程的学生,以及正在学习UML建模的开发者参考。资源以UML可视化建模为主线,覆盖用例图、类图、包图、顺序图、协作图、状态图、活动图、组件图与部署…

2026/10/14 11:04:13 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →