循环缓冲区(循环队列)的优化技巧

循环缓冲区(循环队列)的优化技巧

循环缓冲区作为一种FIFO的数据结构,应用非常广泛。在小红书上偶然间看到评论区里有分享一种针对开辟空间和位运算,提高存取效率的方法,非常新颖。在此记录一下。

传统的循环缓冲区

传统的循环缓冲区通常开辟一块连续的数据空间。使用两个指针,head和tail指针,分别代表即将写入的索引即将读取的索引

  • head == tail时,代表缓冲区空
  • (head + 1) % N == tail时,代表缓冲区满,这浪费了一个元素的存储空间,当这个条件符合的时候,实际上head指向的空间还是可以存入一个元素,但存入后会导致head == tail,与判空的方式冲突了,虽然也有办法避免这个问题,但大部分应用一般也不介意浪费到这个元素的空间。

2次幂大小的空间的循环缓冲区

此类循环缓冲区有以下三个不同

  • 缓冲区空间大小必须是2次幂大小,即2,4,8,16等,因为要用到位运算特性
  • 同样使用两个指针write和read指针,但分别代表的是累计写入的次数累计读取的次数,所以当write == head时,代表缓冲区空(即读取次数和写入次数相同);当write - head == N时,代表缓冲区满(即没有更多写入的位置),因此避免了浪费空间的问题。这里需要注意的是,一定要让指针的数据类型为无符号型,不会出现UB的溢出回环等问题
  • 写入的读取的索引位置为read & (N - 1)write & (N - 1),相当于取模运算,但是位运算更快

ChatGPT给出的一种实现方法

#define SIZE 8  // 必须是 2 的幂
int buffer[SIZE];
unsigned int read = 0, write = 0;

bool enqueue(int value) {
    if (write - read == SIZE) return false;  // 满
    buffer[write & (SIZE - 1)] = value;
    write++;
    return true;
}

bool dequeue(int *value) {
    if (write == read) return false;  // 空
    *value = buffer[read & (SIZE - 1)];
    read++;
    return true;
}
posted @ 2025-05-27 16:06  青灰色的风  阅读(19)  评论(0)    收藏  举报