你有没有遇到这样的痛点:串口接收一溜数据来了又停,突然一波又爆发,处理起来又卡又乱?Ring-Buffer(环形缓冲区)就是为了解决这种“数据流水线”设计的。下面聊聊它是啥、能干嘛、怎么用,还有优缺点一锅端。
搞清 Ring-Buffer 是啥
Ring-Buffer,本质上就是一段固定大小的数组,首尾连成一个圈。写指针往里丢,读指针往外取,永远不会跑到数组边界越界——因为你把它想象成一个环。圈大小必须是 2 的幂(比如 8、16、32、64……),可存放的最大数据量是 buf_size – 1 字节。
基本初始化很简单:
#include "ringbuffer.h"
char buff[64];
ring_buffer_t ring_buffer;
ring_buffer_init(&ring_buffer, buff, sizeof(buff)); // 最多存 63 字节
它为啥这么牛
-
• 低开销:只靠两个指针和数组,不用 malloc、free。
-
• 高性能:插入/取出都是 O(1) 操作,内存布局连续,Cache Friendly。
-
• 不会碎片:写满会自动覆盖最老数据(可选),或者拒绝写入。
总结一句话:用最小的代价,玩转数据缓存。
怎么安装和上手
-
1. 把源码(
ringbuffer.h+ringbuffer.c)拷到项目里。 -
2. 在你的 Makefile/CMakeLists 里加上这两个文件。
-
3.
#include "ringbuffer.h",写一段测试代码,跑起来就好了。
// 例子:简单队列 + 批量读取
ring_buffer_queue(&ring_buffer, 'A');
ring_buffer_queue_arr(&ring_buffer, "HELLO", 5);
char ch;
if (!ring_buffer_is_empty(&ring_buffer)) {
ring_buffer_dequeue(&ring_buffer, &ch);
// 处理 ch
}
char buf[10];
ring_buffer_size_t n = ring_buffer_dequeue_arr(&ring_buffer, buf, sizeof(buf));
// buf[0..n-1] 就是取出的数据
API 大集合 & 使用示例
|
函数 |
功能 |
|
ring_buffer_queue |
入队 1 字节 |
|
ring_buffer_queue_arr |
入队多字节 |
|
ring_buffer_dequeue |
出队 1 字节 |
|
ring_buffer_dequeue_arr |
出队多字节 |
|
ring_buffer_peek |
不取出,查看 index 处的数据 |
|
ring_buffer_is_empty / _is_full |
检查空/满 |
|
ring_buffer_num_items |
当前存了多少字节 |
场景一:串口接收
void UART_IRQHandler(void) {
char d = UART_ReadByte();
ring_buffer_queue(&ring_buffer, d);
}
场景二:主循环取数据
while (!ring_buffer_is_empty(&ring_buffer)) {
ring_buffer_dequeue(&ring_buffer, &d);
process(d);
}
优缺点一目了然
|
优点 |
缺点 |
|
零动态分配、无碎片 |
大小固定,不灵活 |
|
操作简单、速度快 |
必须是 2 的幂,使用前要算好 |
|
支持批量读写、peek 功能 |
写满后默认拒绝或覆盖,需根据需求选策略 |
|
代码体积小,易移植 |
不适合超大数据缓存、复杂并发场景 |
最后来点总结
Ring-Buffer 就像高速公路上的环形高架,你往里放车,堵不死自己也不怕越界。特别适合 UART、SPI、传感器数据采集,多任务间的数据交换,甚至简单的音视频缓存。它的灵活性来自“只要你给我 2ⁿ 大小,我就吃定你”,但是你也要承担“缓冲范围有限”的责任。总的来说,入门极快,效率杠杠的,够用又够爽。
项目地址:https://github.com/AndersKaloer/Ring-Buffer