【C】简易的环形缓冲区代码示例

一、简介

环形缓冲区是一种循环使用的线性数据结构,核心是用「数组 + 读写指针」模拟循环存储,解决普通线性缓冲区 "写满后无法复用已读空间" 的 "假满" 问题。

核心特点:

  1. 循环结构:数组末尾与开头逻辑相连,读写指针到达数组尾部后自动绕回头部;
  2. 读写指针 :read_idx(读指针,指向待读取数据)、write_idx(写指针,指向待写入位置);
  3. 判空 / 判满逻辑 (常用两种方式):
    • 预留 1 个空位置:(write_idx + 1) % capacity == read_idx 表示满,read_idx == write_idx 表示空;
    • 额外加计数变量:count == 0 空,count == capacity 满(本次代码用第一种,更简洁);
  4. 适用场景:生产者 - 消费者模型(如串口通信、日志缓存、多线程数据传输),需高效读写、固定容量的场景。

二、代码示例

ring_buffer.h

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 环形缓冲区结构体
typedef struct {
    void* buffer;     // 存储数据的缓冲区数组
    size_t      item_size;  // 单个元素的大小(字节)
    size_t      capacity;   // 缓冲区最大容量(元素个数)
    size_t      head;       // 头指针:下一个出队元素的索引
    size_t      tail;       // 尾指针:下一个入队元素的索引
    size_t      count;      // 当前已存储的元素个数(简化判空/判满逻辑)
} RingBuffer;


int RingBuffer_Init(RingBuffer* RB, size_t capacity, size_t item_size);
void RingBuffer_Destroy(RingBuffer* RB);
int RingBuffer_Is_Empty(const RingBuffer* RB);
int RingBuffer_Is_Full(const RingBuffer* RB);
size_t RingBuffer_Get_Count(const RingBuffer* RB);
int RingBuffer_Enqueue(RingBuffer* RB, const void* item);
int RingBuffer_Dequeue(RingBuffer* RB, void* item);
void RingBuffer_Print_Int(const RingBuffer* RB);

ring_buffer.c

cpp 复制代码
#include "ring_buffer.h"



int RingBuffer_Init(RingBuffer* RB, size_t capacity, size_t item_size) {
    if (RB == NULL || capacity == 0 || item_size == 0) {
        return -1; // 入参非法
    }

    // 分配缓冲区内存(连续空间)
    RB->buffer = malloc(capacity * item_size);
    if (RB->buffer == NULL) {
        return -1; // 内存分配失败
    }

    RB->item_size = item_size;
    RB->capacity = capacity;
    RB->head = 0;       // 初始头指针指向0
    RB->tail = 0;       // 初始尾指针指向0
    RB->count = 0;       // 初始无元素

    return 0;
}


void RingBuffer_Destroy(RingBuffer* RB) {
    if (RB != NULL) {
        free(RB->buffer); // 释放缓冲区数组
        RB->buffer = NULL; // 避免野指针
    }
}


int RingBuffer_Is_Empty(const RingBuffer* RB) {
    return (RB != NULL && RB->count == 0) ? 1 : 0;
}

int RingBuffer_Is_Full(const RingBuffer* RB) {
    return (RB != NULL && RB->count == RB->capacity) ? 1 : 0;
}


size_t RingBuffer_Get_Count(const RingBuffer* RB) {
    return (RB != NULL) ? RB->count : 0;
}


int RingBuffer_Enqueue(RingBuffer* RB, const void* item) {
    if (RB == NULL || item == NULL || RingBuffer_Is_Full(RB)) {
        return -1; // 入参非法或缓冲区满
    }

    // 拷贝元素到缓冲区尾部(避免直接指针操作,支持任意类型)
    memcpy(
        (char*)RB->buffer + RB->tail * RB->item_size, // 尾指针位置
        item,
        RB->item_size
    );

    // 尾指针循环移动(取模避免越界)
    RB->tail = (RB->tail + 1) % RB->capacity;
    RB->count++; // 元素个数+1

    return 0;
}

int RingBuffer_Dequeue(RingBuffer* RB, void* item) {
    if (RB == NULL || item == NULL || RingBuffer_Is_Empty(RB)) {
        return -1; // 入参非法或缓冲区空
    }

    // 从缓冲区头部拷贝元素到接收缓冲区
    memcpy(
        item,
        (char*)RB->buffer + RB->head * RB->item_size, // 头指针位置
        RB->item_size
    );

    // 头指针循环移动(取模避免越界)
    RB->head = (RB->head + 1) % RB->capacity;
    RB->count--; // 元素个数-1

    return 0;
}


void RingBuffer_Print_Int(const RingBuffer* RB) {
    if (RB == NULL || RB->item_size != sizeof(int) || RingBuffer_Is_Empty(RB)) {
        printf("Buffer is empty or not int-type\n");
        return;
    }

    printf("Buffer (count=%zu, capacity=%zu): ", RB->count, RB->capacity);
    size_t idx = RB->head;
    for (size_t i = 0; i < RB->count; i++) {
        // 按整型解析并打印
        int val = *(int*)((char*)RB->buffer + idx * RB->item_size);
        printf("%d ", val);
        idx = (idx + 1) % RB->capacity;
    }
    printf("\n");
}

main.c

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "ring_buffer.h"

// ------------------------------ 测试用例 ------------------------------
int main() {
    RingBuffer RB;
    const size_t CAPACITY = 5;  // 缓冲区容量5个元素
    const size_t ITEM_SIZE = sizeof(int); // 元素类型为int

    // 1. 初始化缓冲区
    if (RingBuffer_Init(&RB, CAPACITY, ITEM_SIZE) != 0) {
        printf("Buffer init failed\n");
        return -1;
    }

    // 2. 入队测试(正常入队)
    int items[] = { 10, 20, 30, 40, 50 };
    for (size_t i = 0; i < CAPACITY; i++) {
        if (RingBuffer_Enqueue(&RB, &items[i]) == 0) {
            printf("Enqueue: %d\n", items[i]);
        }
        else {
            printf("Enqueue failed: %d\n", items[i]);
        }
    }
    RingBuffer_Print_Int(&RB); // 输出:10 20 30 40 50

    // 3. 满队入队测试(预期失败)
    int full_item = 60;
    if (RingBuffer_Enqueue(&RB, &full_item) == -1) {
        printf("Enqueue failed (buffer full): %d\n", full_item);
    }

    // 4. 出队测试(正常出队)
    int deitem;
    if (RingBuffer_Dequeue(&RB, &deitem) == 0) {
        printf("Dequeue: %d\n", deitem); // 输出:10
    }
    RingBuffer_Print_Int(&RB); // 输出:20 30 40 50

    // 5. 入队+出队混合测试
    int new_item = 60;
    if (RingBuffer_Enqueue(&RB, &new_item) == 0) {
        printf("Enqueue: %d\n", new_item); // 入队60,缓冲区再次满
    }
    RingBuffer_Print_Int(&RB); // 输出:20 30 40 50 60

    // 6. 连续出队测试
    while (!RingBuffer_Is_Empty(&RB)) {
        RingBuffer_Dequeue(&RB, &deitem);
        printf("Dequeue: %d\n", deitem); // 依次输出20、30、40、50、60
    }

    // 7. 空队出队测试(预期失败)
    if (RingBuffer_Dequeue(&RB, &deitem) == -1) {
        printf("Dequeue failed (buffer empty)\n");
    }

    // 8. 销毁缓冲区
    RingBuffer_Destroy(&RB);

    return 0;
}

三、效果

相关推荐
晓蛋3 天前
c语言指的是什么意思
c语言·编译器·编程开发·集成开发环境·程序实例
小羊没烦恼!3 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
傲世仙尊3 天前
目录即文件-Ext文件系统收尾篇
linux·c语言
牵猫散步的鱼儿3 天前
重载、重写(覆盖)、重定义区别
c语言
伞伞悦读3 天前
【第38期】Python 模块与包详解:import、from、模块搜索路径、包结构和 __init__
开发语言·python
phltxy3 天前
C 语言指针:从内存地址到灵活的数据访问
c语言
C语言小火车3 天前
C/C++ 为什么需要编译器?
开发语言·c++
phltxy3 天前
C 语言中的数据存储:从类型到二进制位
c语言
霍霍的袁3 天前
【C++】map 和 set 的使用 | 从用法到底层
开发语言·c++·学习·visual studio
孙启超3 天前
【AI开发之Rust】第 11 课:智能指针与内部可变性
开发语言·后端·rust