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

102.环形缓冲区之读指针与写指针:原理、实现与完整代码

一、环形缓冲区的核心概念

环形缓冲区本质上是一个首尾相连的数组,通过两个关键指针实现数据的读写管理:

  • 写指针(pw):标记下一个数据写入的位置
  • 读指针(pr):标记下一个数据读取的位置

这两个指针是环形缓冲区的灵魂,所有的读写操作、状态判断都围绕它们展开。

二、读写指针的工作原理

1. 初始化状态

缓冲区为空时,写指针和读指针都指向数组的起始位置(下标0)。

2. 写入数据流程

  • 第一次写入数据(如01)后,写指针pw右移一格
  • 继续写入02、03……直到pw走到数组最后一格
  • 此时继续写入新数据(如09),pw会自动跳回数组下标0,实现环形回绕,不会发生数组越界
  • 再次写入10时,pw继续向后移动

3. 读取数据流程

  • 读指针pr逐格向右移动,每读取一格,对应位置的数据清空
  • pr读完06后,pw和pr同时移动,模拟日常环形缓冲区运行时的一般情况
  • 当缓冲区内部所有数据全部读取完毕时,pw和pr会指向同一个下标位置

三、状态判断:空与满的判定

1. 空状态判断

当pw等于pr时,环形缓冲区为空。这是因为所有数据都已被读取完毕,两个指针重新重合。

2. 满状态判断

环形缓冲区采用“牺牲一格”方案,即数组中始终预留一个空白格子,用于区分空和满状态。

  • 直观判断:pw加一等于pr时,缓冲区为满
  • 边界情况处理:当pw在数组最后一位(下标7),pr在下标0时,单纯数字8不等于0,此时等式pw+1=pr不成立,但缓冲区实际上已经存满
  • 通用公式:(pw + 1) % buffer_size == pr,这是全场景通用无bug的判满条件

四、指针的实现代码

1. 写指针实现

// 方法一:先自增再取模 pw++; pw = pw % buffer_size; // 方法二:一行代码同时实现自增和取模 pw = (pw + 1) % buffer_size;

2. 读指针实现

// 方法一:先自增再取模 pr++; pr = pr % buffer_size; // 方法二:一行代码同时实现自增和取模 pr = (pr + 1) % buffer_size;

这两种写法实现的功能完全一致,都能保证指针在数组范围内循环移动。

五、完整的环形缓冲区实现

1. 头文件定义

#ifndef RING_BUFFER_H #define RING_BUFFER_H #include <stdint.h> #include <stdbool.h> #define BUFFER_SIZE 8 // 缓冲区大小,实际可用空间为BUFFER_SIZE - 1 typedef struct { uint8_t buffer[BUFFER_SIZE]; // 数据缓冲区 uint8_t pw; // 写指针 uint8_t pr; // 读指针 } RingBuffer_t; // 初始化环形缓冲区 void RingBuffer_Init(RingBuffer_t *rb); // 向环形缓冲区写入数据 bool RingBuffer_Write(RingBuffer_t *rb, uint8_t data); // 从环形缓冲区读取数据 bool RingBuffer_Read(RingBuffer_t *rb, uint8_t *data); // 判断环形缓冲区是否为空 bool RingBuffer_IsEmpty(RingBuffer_t *rb); // 判断环形缓冲区是否已满 bool RingBuffer_IsFull(RingBuffer_t *rb); #endif // RING_BUFFER_H

2. 功能实现

#include "ring_buffer.h" // 初始化环形缓冲区 void RingBuffer_Init(RingBuffer_t *rb) { rb->pw = 0; rb->pr = 0; } // 向环形缓冲区写入数据 bool RingBuffer_Write(RingBuffer_t *rb, uint8_t data) { // 检查缓冲区是否已满 if (RingBuffer_IsFull(rb)) { return false; } // 写入数据 rb->buffer[rb->pw] = data; // 更新写指针 rb->pw = (rb->pw + 1) % BUFFER_SIZE; return true; } // 从环形缓冲区读取数据 bool RingBuffer_Read(RingBuffer_t *rb, uint8_t *data) { // 检查缓冲区是否为空 if (RingBuffer_IsEmpty(rb)) { return false; } // 读取数据 *data = rb->buffer[rb->pr]; // 更新读指针 rb->pr = (rb->pr + 1) % BUFFER_SIZE; return true; } // 判断环形缓冲区是否为空 bool RingBuffer_IsEmpty(RingBuffer_t *rb) { return rb->pw == rb->pr; } // 判断环形缓冲区是否已满 bool RingBuffer_IsFull(RingBuffer_t *rb) { return (rb->pw + 1) % BUFFER_SIZE == rb->pr; }

3. 测试代码

#include <stdio.h> #include "ring_buffer.h" int main() { RingBuffer_t rb; uint8_t data; // 初始化环形缓冲区 RingBuffer_Init(&rb); // 写入数据 for (uint8_t i = 0; i < 7; i++) { if (RingBuffer_Write(&rb, i)) { printf("写入数据: %d\n", i); } else { printf("缓冲区已满,无法写入数据: %d\n", i); } } // 尝试写入第8个数据(应该失败) if (RingBuffer_Write(&rb, 7)) { printf("写入数据: 7\n"); } else { printf("缓冲区已满,无法写入数据: 7\n"); } // 读取数据 printf("\n读取数据:\n"); while (!RingBuffer_IsEmpty(&rb)) { if (RingBuffer_Read(&rb, &data)) { printf("读取数据: %d\n", data); } else { printf("缓冲区为空,无法读取数据\n"); } } // 尝试读取数据(应该失败) if (RingBuffer_Read(&rb, &data)) { printf("读取数据: %d\n", data); } else { printf("缓冲区为空,无法读取数据\n"); } return 0; }

六、总结

环形缓冲区的核心优势在于读指针和写指针不会卡死在数组末尾,而是会自动循环回到头部,实现数据的连续读写。通过“牺牲一格”的方案,我们可以准确判断缓冲区的空和满状态,避免数据覆盖和丢失。

http://www.cnnetsun.cn/news/4106336.html

相关文章:

  • BBDown完整使用手册:让哔哩哔哩视频下载变成一行命令的事
  • 计算机网络学习笔记(六)---网络层与IP协议
  • AI家庭机器人技术解析:从ROS架构到嵌入式开发实践
  • 从零构建智能体:基于Coze平台的可视化AI助手开发实战
  • C语言基础知识-学习笔记
  • 基于大模型与持续学习的人形机器人叠衣系统实战解析
  • 光度立体成像全栈拆解 | 多视角光影法向量求解+梯度积分重建,助力漫反射工件微划痕凹坑褶皱高速高精度2.5D缺陷检测
  • 深挖C语言:深入理解指针(2)
  • 线性可调双路输出电源:原理、设计与噪声抑制全解析
  • 当“让用户满意”变成“让用户更不满意”:AI防御误触发的根源与破解之道
  • OptiCommPy模拟光马赫-曾德尔调制器
  • CAD绘图效率提升:从练习图29拆解系统绘图流程与高效命令组合
  • snpe-VGG案例(uv环境-全流程 教程)
  • 实测通勤15分钟出片全流程,不用电脑不用大内存手机
  • 基于树莓派的智能交互装置:从硬件搭建到AI对话引擎实现
  • 企业信息管理:从“罗生门”到一致性回应的体系构建
  • OPOR-Bench: Evaluating Large Language Models on Online Public Opinion Report Generation
  • AE字体动画核心技巧:从基础动画器到批量预设应用
  • 2026毕业论文AI生成工具打分:6款谁更靠谱
  • 百度网盘Mac版免费提速完整方案:1个开源插件解锁SVIP高速下载(附避坑指南)
  • 使用CD4051实现8路模拟信号复用:硬件设计与Arduino编程指南
  • 用Python打造智能双模机械键盘:从硬件焊接、CircuitPython编程到高级功能实现
  • 基于世界模型与持续学习的自适应机器人系统构建指南
  • meteor-collection-hooks教程:remove钩子实现级联删除的完整指南
  • 基于RP2040与MicroPython的Pi Day灯光项目:从硬件选型到交互实现
  • 基于UNIHIKER K10的财富雨互动游戏开发:触摸、声音与姿态控制
  • OneNote 页面编号终极指南:用 OneMore 三步搭出专业级文档结构
  • G-Helper 上手指南:用一款开源轻量工具,让华硕笔记本性能、静音与续航兼得
  • Autojs基础-Shell命令(shell)
  • 在ARM和国产芯片上部署容器化项目总是失败?这份跨架构实战指南帮你一次过关