一、简介
环形缓冲区是一种循环使用的线性数据结构,核心是用「数组 + 读写指针」模拟循环存储,解决普通线性缓冲区 "写满后无法复用已读空间" 的 "假满" 问题。
核心特点:
- 循环结构:数组末尾与开头逻辑相连,读写指针到达数组尾部后自动绕回头部;
- 读写指针 :
read_idx(读指针,指向待读取数据)、write_idx(写指针,指向待写入位置); - 判空 / 判满逻辑 (常用两种方式):
- 预留 1 个空位置:
(write_idx + 1) % capacity == read_idx表示满,read_idx == write_idx表示空; - 额外加计数变量:
count == 0空,count == capacity满(本次代码用第一种,更简洁);
- 预留 1 个空位置:
- 适用场景:生产者 - 消费者模型(如串口通信、日志缓存、多线程数据传输),需高效读写、固定容量的场景。
二、代码示例
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;
}
三、效果
