循环缓冲区的实现

circular buffer implementation

本文关键字:实现 缓冲区 循环缓冲 循环      更新时间:2023-10-16

是的,我又来了一个非常直接的实现,就像这样:

    // write data always! if buffer is already full, overwrite old data!
    void Put( const CONTENT_TYPE &data )
    {
        buffer[ inOffset++] = data;
        inOffset%=size;
        // was data overwritten, skip it by increment read offset
        if ( inOffset == outOffset ) 
        {
            outOffset++;
            outOffset%=size;
            std::cout << "Overwrite" << std::endl;
        }
    }
    CONTENT_TYPE Pull()
    {
        CONTENT_TYPE data = buffer[ outOffset++ ];
        outOffset %= size;
        return data;
    }

但是这个简单的算法只利用了缓冲区中大小为1的一个元素!

如果我想避免这种情况,我只能找到一个解决方案,即添加另一个计数器变量,这会浪费我sizeof(counter_var) - sizeof(element)字节。

问:有没有不浪费内存的解决方案?它看起来很简单,但我不能抓住它:-)

注释:有更多的代码行来保护空读和其他东西,但这对问题不重要。它没有标记为c++因为算法不依赖于语言,如果我给出一个c++代码示例

可以使用两个整数,如果一个是索引,另一个是元素计数,则填充所有槽,然后转换为动态查找第二个索引:

void put(const ELEMENT& element) {
  if (nElements == size) throw "put: buffer full";
  buffer[(start + nElements++) % size] = element;
}
ELEMENT get() {
  if (nElements == 0) throw "get: buffer empty";
  ELEMENT& value = buffer[start];
  start = (start + 1) % size;
  --nElements;
  return value;
}

当然你也可以用if (foo > size) foo -= size;替换mod操作

你可以通过在不同的时间点做模运算来解决这个问题;假设我们在每次访问后增加读和写指针。如果我们现在在递增之后立即对读指针取模,并在读取之前对写指针取模,则整个缓冲区的|write-read|将是缓冲区的长度,而不需要任何特殊情况处理。为了工作,你的写指针应该总是使用 % buffer_length,但存储 % (2 * buffer_length)

我不是特别喜欢Mark的回答,因为处理特殊情况通常不是一个好主意,就像在你通常使用size_t(即无符号整数)的地方引入负前哨值一样少。

您可以为其中一个偏移量使用一个特殊的前哨值,例如-1,以指示缓冲区是满的还是空的。这会使检查和修改偏移量的代码变得复杂。

// write data always! if buffer is already full, overwrite old data!
void Put( const CONTENT_TYPE &data )
{
    buffer[ inOffset++] = data;
    inOffset%=size;
    // was data overwritten, skip it by setting read offset to sentinel
    if ( inOffset == outOffset || outOffset == -1 ) 
    {
        outOffset = -1;
        std::cout << "Overwrite" << std::endl;
    }
}
CONTENT_TYPE Pull()
{
    if (outOffset == -1)
        outOffset = inOffset;
    CONTENT_TYPE data = buffer[ outOffset++ ];
    outOffset %= size;
    return data;
}
bool IsEmpty()
{
    return outOffset == inOffset;
}