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

环形缓冲区原理与C语言实现ringbuffer

目录

一、环形缓冲区(Circular Buffer)原理

二、环形缓冲区结构示意图

写入数据过程

读取数据过程

关键逻辑总结

环形缓冲区的循环特性

三、应用场景

四、C语言实现环形缓冲区

五、编程应用实例

六、关键总结


一、环形缓冲区(Circular Buffer)原理

环形缓冲区(又称循环缓冲区)是一种线性数据结构,逻辑上首尾相连,通过 读写指针 或 头尾索引 管理数据的写入和读取。核心特性如下:

  1. 固定容量:缓冲区大小预先分配,不可动态扩展。

  2. 循环覆盖:当缓冲区满时,新数据覆盖旧数据(可配置为阻塞或丢弃)。

  3. 高效操作:读写操作时间复杂度为O(1),无需移动数据。

核心组件

  • 缓冲区数组:存储数据的连续内存区域。

  • 写指针(head):指向下一个可写入的位置。

  • 读指针(tail):指向下一个可读取的位置。

  • 容量(size):缓冲区总容量(通常预留一个位置区分“满”和“空”)。

状态判断

  • head == tail

  • (head + 1) % size == tail(通过预留一个位置实现)

二、环形缓冲区结构示意图

通过这种图形化表示,可以清晰理解环形缓冲区的循环覆盖机制高效的内存复用特性

假设缓冲区容量为 5 个元素(实际分配 6 个位置,通过“空一格”判断满状态):

索引: 0   1   2   3   4   5   (物理内存连续)
      +---+---+---+---+---+---+
      |   |   |   |   |   |   |
      +---+---+---+---+---+---+
       ↑                   ↑
      tail               head

 初始状态:head = tail = 0,缓冲区为空。

写入数据过程

步骤1:写入数据 A, B, C

索引: 0   1   2   3   4   5   
      +---+---+---+---+---+---+
      | A | B | C |   |   |   |
      +---+---+---+---+---+---+
       ↑       ↑
      tail    head (head=3)
  • head 移动到索引3,tail 仍在索引0。

  • 缓冲区未满head 和 tail 不连续。

 步骤2:继续写入 D, E

索引: 0   1   2   3   4   5   
      +---+---+---+---+---+---+
      | A | B | C | D | E |   |
      +---+---+---+---+---+---+
       ↑               ↑
      tail            head (head=5)
  • head 移动到索引5,tail 仍在索引0。

  • 缓冲区未满head 和 tail 不连续。

步骤3:继续写入 F(覆盖旧数据) 

索引: 0   1   2   3   4   5   
      +---+---+---+---+---+---+
      | F | B | C | D | E |   |
      +---+---+---+---+---+---+
           ↑           ↑
          tail        head (head=0)
  • 缓冲区已满(head + 1) % 6 = 1,等于 tail(原tail=0,写入后tail=1)。

  • head 循环回到索引0,tail 被推动到索引1,旧数据 A 被覆盖。

读取数据过程

步骤1:读取数据 B, C, D, E

索引: 0   1   2   3   4   5   
      +---+---+---+---+---+---+
      | F | B | C | D | E |   |
      +---+---+---+---+---+---+
                       ↑   ↑
                      tail head (tail=5, head=0)
  • 读取顺序:B → C → D → E(tail 从1移动到5)。

  • 缓冲区未空head != tail

步骤2:继续读取 F

索引: 0   1   2   3   4   5   
      +---+---+---+---+---+---+
      | F |   |   |   |   |   |
      +---+---+---+---+---+---+
           ↑               ↑
          head            tail (head=0, tail=0)
  • tail 移动到索引0,与 head 重合,缓冲区为空。

关键逻辑总结

 

环形缓冲区的循环特性

索引: 0 ← 5
      ↓   ↑
      1   4
      ↓   ↑
      2 → 3
  • 逻辑循环:当指针到达索引5时,下一个位置回到索引0,形成环形。

 

三、应用场景

  1. 数据流缓冲

    • 实时音频/视频流处理,平衡生产者和消费者的速度差异。

  2. 通信协议

    • 串口、网络通信中暂存接收或待发送的数据包。

  3. 日志系统

    • 循环记录日志,避免内存耗尽。

  4. 多线程编程

    • 作为线程安全的队列,实现生产者-消费者模型。

四、C语言实现环形缓冲区

1. 数据结构定义

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

typedef struct {
    int *buffer;    // 缓冲区数组
    int size;       // 缓冲区总容量(含预留位)
    int head;       // 写指针
    int tail;       // 读指针
} CircularBuffer;

2. 初始化与释放

// 创建环形缓冲区(实际可存储capacity个元素)
CircularBuffer* cb_create(int capacity) {
    CircularBuffer *cb = (CircularBuffer*)malloc(sizeof(CircularBuffer));
    cb->size = capacity + 1;  // 多分配1个位置用于判断满状态
    cb->buffer = (int*)malloc(cb->size * sizeof(int));
    cb->head = 0;
    cb->tail = 0;
    return cb;
}

// 释放缓冲区内存
void cb_free(CircularBuffer *cb) {
    free(cb->buffer);
    free(cb);
}

3. 状态判断

// 判断缓冲区是否为空
bool cb_is_empty(CircularBuffer *cb) {
    return cb->head == cb->tail;
}

// 判断缓冲区是否已满
bool cb_is_full(CircularBuffer *cb) {
    return (cb->head + 1) % cb->size == cb->tail;
}

4. 数据写入与读取

// 写入数据(覆盖旧数据模式)
void cb_write(CircularBuffer *cb, int data) {
    // 缓冲区满时覆盖最旧数据
    if (cb_is_full(cb)) {
        cb->tail = (cb->tail + 1) % cb->size; // 移动读指针丢弃旧数据
    }
    cb->buffer[cb->head] = data;
    cb->head = (cb->head + 1) % cb->size;
}

// 读取数据(返回是否成功)
bool cb_read(CircularBuffer *cb, int *data) {
    if (cb_is_empty(cb)) return false; // 缓冲区空
    *data = cb->buffer[cb->tail];
    cb->tail = (cb->tail + 1) % cb->size;
    return true;
}

五、编程应用实例

示例:模拟实时数据采集与处理

int main() {
    // 创建容量为5的环形缓冲区(实际存储5个元素)
    CircularBuffer *cb = cb_create(5);

    // 模拟写入10个数据(覆盖模式)
    printf("写入数据: ");
    for (int i = 0; i < 10; i++) {
        cb_write(cb, i);
        printf("%d ", i);
    }
    printf("\n");

    // 读取并打印缓冲区内容
    printf("读取数据: ");
    int data;
    while (cb_read(cb, &data)) {
        printf("%d ", data);
    }
    printf("\n");

    cb_free(cb);
    return 0;
}

输出

写入数据: 0 1 2 3 4 5 6 7 8 9 
读取数据: 5 6 7 8 9 

解释

  • 缓冲区容量为5,初始写入0-4后已满。

  • 继续写入5-9时,覆盖旧数据0-4,最终缓冲区保留5-9。

  • 读取时按写入顺序输出5-9。

六、关键总结

通过合理设计环形缓冲区,可以在资源受限的嵌入式系统或高性能应用中实现高效、稳定的数据缓冲管理。 


http://www.kler.cn/a/528913.html

相关文章:

  • 计算满足特定条件的素数在全体素数中的密度极限值,并将该极限值乘以10^7后向下取整的解题思路
  • Python3 【装饰器】项目实战:5个新颖的学习案例
  • 说说Redis的内存淘汰策略?
  • TVM调度原语完全指南:从入门到微架构级优化
  • 【Rust自学】18.3. 模式(匹配)的语法
  • 【漫话机器学习系列】073.黑塞矩阵(Hessian Matrix)
  • python算法和数据结构刷题[4]:查找算法和排序算法
  • Versal - 基础4(VD100+Versal IBERT)
  • C++解决输入空格字符串的三种方法
  • 智慧园区管理系统推动企业智能运维与资源优化的全新路径分析
  • 【Leetcode 热题 100】64. 最小路径和
  • 图书管理系统 Axios 源码__编辑图书
  • 增删改查(CRUD)操作
  • 新手从零开始使用飞牛fnOS搭建家庭数据管理中心体验NAS系统
  • pytorch基于 Transformer 预训练模型的方法实现词嵌入(tiansz/bert-base-chinese)
  • 【Linux】22.进程间通信(1)
  • webrtc编译需要常用环境变量以及相关名词解释
  • Leetcode::81. 搜索旋转排序数组 II
  • DRM系列三:drm core模块入口
  • 40. SPI实验