传送门 ==>> AutoSAR入门和实战系列总目录
文章目录
- 概述
- 使用
- 循环缓冲区机制
循环缓冲区、循环队列、循环缓冲区或环形缓冲区是一种数据结构,它使用单个固定大小的缓冲区,就好像它是端到端连接的一样。这种结构很容易用于缓冲数据流。
概述
循环缓冲区首先是空的,并具有设定的长度。下图中是一个 7 个元素缓冲区:
假设 1 写在一个循环缓冲区的中心(循环缓冲区中,起始位置并不重要):
然后假设将另外两个元素 2 和 3 添加到循环缓冲区 它们被放在 1 之后:
如果删除了两个元素,则循环缓冲区中最旧的两个值将被删除。循环缓冲区使用 FIFO(先进先出)逻辑。在示例中,1 和 2 最先进入循环缓冲区,它们最先被删除,缓冲区内留下 3。
如果缓冲区有 7 个元素,那么缓冲区是满的:
循环缓冲区的一个属性是当它已满并执行后续写入时,它会开始覆盖最旧的数据。在当前示例中,添加了另外两个元素——A 和 B,它们覆盖了3 和 4:
或者是另一种选项,管理缓冲区的例程如果禁止覆盖数据,则要返回错误或引发异常。数据是否被覆盖取决于缓冲区例程或使用循环缓冲区的应用程序的需求。
最后,如果现在删除了两个元素 ,则删除B后面的两个元素,此时的缓冲区为:
使用
循环缓冲区的有用属性是它不需要在使用元素时对其元素进行洗牌。(如果使用非循环缓冲区,则有必要在消耗一个元素时移动所有元素。)换句话说,循环缓冲区非常适合作为 FIFO(先进先出)缓冲区,而标准,非循环缓冲区非常适合作为LIFO(后进先出)缓冲区。
循环缓冲为具有固定最大大小的队列提供了一个很好的实现策略。如果队列采用最大尺寸,那么循环缓冲区是一个完全理想的实现;所有队列操作都是常数时间。但是,扩展循环缓冲区需要移位内存,成本相对较高。对于任意扩展的队列,链表方法可能更受欢迎。
在某些情况下,可以使用覆盖循环缓冲区,例如在多媒体中。如果缓冲区用作生产者-消费者问题中的有界缓冲区,那么如果消费者(例如声卡)无法暂时跟上,生产者(例如音频生成器)可能希望覆盖旧数据.
循环缓冲区机制
可以使用指一个针和三个整数来实现循环缓冲区:
- 内存中的缓冲区开始
- 缓冲容量(长度)
- 写入缓冲区索引(结束)
- 从缓冲区读取索引(开始)
此图像显示了长度 = 7字节的缓冲区,填充了三个字节:
此图像显示了一个满的缓冲区,其中四个元素(编号 1 到 4)已被覆盖:
开始时,索引 end 和 start 设置为 0。循环缓冲区写操作将一个元素写入end索引位置,end索引递增到下一个缓冲区位置。循环缓冲区读取操作从start索引位置读取一个元素,并将start索引递增到下一个缓冲区位置。
开始和结束索引不足以告诉缓冲区满或空状态,一种解决方案是使用另一个整数计数,在写入操作时递增,在读取操作时递减。然后检查空意味着测试计数等于 0,检查满满意味着测试计数等于长度。
以下源代码是 C 实现。函数 put() 将一个项目放入缓冲区,函数 get() 从缓冲区中获取一个项目:
#define BUFLEN 3
int buf[BUFLEN]; /* array to be treated as circular buffer of BUFLEN integers */
int end = 0; /* write index */
int start = 0; /* read index */
void put(int item)
{
buf[end++] = item;
end %= BUFLEN;
}
int get()
{
int item = buf[start++];
start %= BUFLEN;
return item;
}