【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;
}

三、效果

相关推荐
阿白的白日梦1 小时前
Windows下c/c++编译器MinGW-w64下载和安装
c语言·后端
张np1 小时前
java基础-ArrayList
java·开发语言
zmzb01031 小时前
C++课后习题训练记录Day42
开发语言·c++·算法
不会编程的小寒1 小时前
C / C++ 面试题
java·开发语言
坐吃山猪1 小时前
Electron02-Hello
开发语言·javascript·ecmascript
Mr Lee_1 小时前
Smali 文件生成dex装箱算法整合
开发语言·python·算法
姓刘的哦2 小时前
RK3568开发板运行Qt
开发语言·qt
刘晓倩2 小时前
Python3的Sequence
开发语言·python
消失的旧时光-19432 小时前
彻底理解 synchronized:实例锁、类锁与自定义锁的原理和最佳实践
java·开发语言