news 2026/8/19 22:17:39

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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; }

六、总结

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/19 22:16:18

BBDown完整使用手册:让哔哩哔哩视频下载变成一行命令的事

BBDown完整使用手册&#xff1a;让哔哩哔哩视频下载变成一行命令的事 【免费下载链接】BBDown Bilibili Downloader. 一个命令行式哔哩哔哩下载器. 项目地址: https://gitcode.com/gh_mirrors/bb/BBDown 周末想躺在沙发上把追了一个月的纪录片一口气看完&#xff0c;结果…

作者头像 李华
网站建设 2026/8/19 22:15:45

计算机网络学习笔记(六)---网络层与IP协议

IP是TCP/IP协议族中的核心协议。所有TCP、 UDP、 ICMP和IGMP数据都通过IP数据报传输。 IP提供了一种尽力而为、无连接的数据报交付服务。 “尽力而为”的含义是不保证IP数据报能成功到达目的地&#xff0c;它不对自已尝试交付的数据报提供保证。当某些错误发生时,例如一台路由器…

作者头像 李华
网站建设 2026/8/19 22:14:25

AI家庭机器人技术解析:从ROS架构到嵌入式开发实践

当“AI家庭机器人”和“外骨骼”这两个听起来充满未来感的概念&#xff0c;从科技新闻走进线下实体店&#xff0c;并且由海尔这样的国民级家电品牌来落地时&#xff0c;它传递的信号远比一次简单的产品发布要强烈得多。这不仅仅是海尔开了一家新店&#xff0c;而是标志着AI与机…

作者头像 李华
网站建设 2026/8/19 22:13:57

从零构建智能体:基于Coze平台的可视化AI助手开发实战

如果你最近在关注AI应用开发&#xff0c;特别是想快速构建一个能理解你意图、执行复杂任务、还能持续学习的智能助手&#xff0c;那么“智能体”&#xff08;Agent&#xff09;这个概念一定绕不开。但当你真正动手时&#xff0c;可能会发现&#xff1a;市面上的Agent框架要么过…

作者头像 李华
网站建设 2026/8/19 22:13:20

C语言基础知识-学习笔记

1.IDE开发工具&#xff1a;1.1.常用开发工具很多&#xff0c;本次学习用的是Visual Studio&#xff0c;官网直接下载&#xff0c;免费使用&#xff0c;下载安装方便。2.C语言概述2.1.概述&#xff1a;通俗来说就是一种机器语言&#xff0c;人与机器交互的工具。C语言通过编译&a…

作者头像 李华
网站建设 2026/8/19 22:13:09

基于大模型与持续学习的人形机器人叠衣系统实战解析

最近在准备一个机器人相关的项目&#xff0c;看到第二届世界人形机器人运动会&#xff08;WHRC&#xff09;即将开赛的消息&#xff0c;其中“家政服务”赛项要求机器人在30分钟内完成收纳叠衣&#xff0c;这个挑战让我对背后的技术产生了浓厚兴趣。这不仅仅是机械臂的重复运动…

作者头像 李华