简体   繁体   English

循环缓冲区的实现

[英]circular buffer implementation

Yes again, I come again with that very straight forward implementation which is something like this: 再次是,我再次提出了非常简单的实现,如下所示:

    // 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;
    }

But this simple algorithm utilizes only size-1 one elements of the buffer! 但是,这种简单的算法仅利用大小为1的缓冲区元素!

If I want to avoid that, I only found a solution with adding another counter variable, which wastes me sizeof(counter_var) - sizeof(element) bytes. 如果我想避免这种情况,我只找到了添加另一个计数器变量的解决方案,这浪费了我sizeof(counter_var)-sizeof(element)个字节。

Q: Is there a solution which did not waste memory? 问:有没有不浪费内存的解决方案? It looks so terrible simple but I can't catch it :-) 它看起来很简单,但我抓不到:-)

Remark: There are some more lines of code to protect for empty reads and other stuff, but this is not important to the question. 备注:还有更多的代码行可以保护空读取和其他内容,但这对这个问题并不重要。 And it is not tagged c++ because the algorithm did not depend on the language, also if I give a c++ code example. 而且它没有被标记为c ++,因为该算法不依赖于语言,即使我举一个c ++代码示例也是如此。

You can use two integers and fill all slots if one is an index and the other an element count, then convert to find the second index on the fly: 您可以使用两个整数并填充所有插槽(如果一个是索引,另一个是元素计数),然后立即进行转换以找到第二个索引:

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;
}

Of course you can replace the mod operations with if (foo > size) foo -= size; 当然,您可以用if (foo > size) foo -= size;替换mod操作if (foo > size) foo -= size; if you like. 如果你喜欢。

You'd just deal with that by using different points in time at which you do the modulo operation; 您可以通过使用不同的时间点进行模运算来解决这个问题。 assume we increase the read and write pointers after every access. 假设我们在每次访问后都增加了读写指针。 If we now do the read pointer's modulo instantly after increasing, and the write pointer's modulo just right before reading, the |write-read| 如果现在我们在增加之后立即执行读指针的模,而就在读之前立即写写指针的模,则| write-read | of a full buffer would be the length of the buffer, without any special case handling. 完整缓冲区的长度将是缓冲区的长度,无需任何特殊情况处理。 For that to work, your write pointer should always be used % buffer_length , but stored % (2 * buffer_length) . 为此,您的写指针应始终使用 % buffer_length ,但应存储 % (2 * buffer_length)

I don't especially like Mark's answer, because handling things as special cases is usually not a good idea, as little as introducing negative sentinel values is in a place where you'd typically used size_t (ie an unsigned integer). 我不特别喜欢Mark的答案,因为按特殊情况处理事物通常不是一个好主意,在您通常使用size_t (即无符号整数)的地方,引入负的前哨值就少了。

You could use a special sentinel value for one of the offsets, such as -1, to indicate that the buffer is full or empty. 您可以对偏移量之一使用特殊的前哨值,例如-1,以指示缓冲区已满或为空。 This will complicate your code for checking and modifying the offset. 这会使检查和修改偏移量的代码复杂化。

// 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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM