请在提交支持请求之前仔细阅读此读书我,并检查以前回答的问题之间是否已经存在答案:请勿滥用GitHub问题跟踪器。
根据我的计算,该库本身的内存消耗约为0.5kb :580字节(最大)和8个字节。显然,这并不考虑用于存储物品本身的空间。
声明缓冲区时,应指定必须处理的数据类型和缓冲区容量:这两个参数将影响缓冲区消耗的内存。
# include < CircularBuffer.hpp >
CircularBuffer<byte, 100 > bytes; // uses 538 bytes
CircularBuffer< int , 100 > ints; // uses 638 bytes
CircularBuffer< long , 100 > longs; // uses 838 bytes
CircularBuffer< float , 100 > floats; // uses 988 bytes
CircularBuffer< double , 100 > doubles; // uses 988 bytes
CircularBuffer< char , 100 > chars; // uses 538 bytes
CircularBuffer< void *, 100 > pointers; // uses 638 bytes
请注意:上面报告的内存使用情况包括图书馆代码使用的程序内存,堆内存少得多,并且与缓冲区的大小和类型相媲美。
让我们开始清楚情况:库不支持在缓冲区中间插入数据。您可以通过unshift()
操作或通过push()
操作将数据添加到第一个元素之前,或者通过unshift()操作添加数据。您可以继续添加超出缓冲区最大容量的数据,但是您将丢失最不重要的信息:
unshift()
增加了头部,因此增加容量会导致尾部的元素被覆盖并丢失push()
增加了尾巴,因此增加容量会导致头部的元素被覆盖并丢失如果添加没有造成任何信息丢失,则unshift()
和push()
返回true
如果发生覆盖物,则false: false
:
CircularBuffer< int , 5 > buffer; // buffer capacity is 5
// all of the following return true
buffer.unshift( 1 ); // [1]
buffer.unshift( 2 ); // [2,1]
buffer.unshift( 3 ); // [3,2,1]
buffer.push( 0 ); // [3,2,1,0]
buffer.push( 5 ); // [3,2,1,0,5]
// buffer is now at full capacity, from now on any addition returns false
buffer.unshift( 2 ); // [2,3,2,1,0] returns false
buffer.unshift( 10 ); // [10,2,3,2,1] returns false
buffer.push(- 5 ); // [2,3,2,1,-5] returns false
与数据添加类似,可以通过pop()
操作在尾部或通过shift()
操作从头部执行数据检索:两者都会导致读取的元素从缓冲区中删除。
在实际缓冲区大小之外的读取数据具有不确定的行为,并且是用户有责任使用下一节中列出的其他操作来防止这种边界违规行为。根据数据类型和分配方法,库的行为会有所不同,但是如果您不观看步骤,则可以安全地假设您的程序将崩溃。
也可以使用非破坏性的阅读操作:
first()
返回元素头部last()
返回尾部的元素[]
运算符读取缓冲区中的任何元素CircularBuffer< char , 50 > buffer; // ['a','b','c','d','e','f','g']
buffer.first(); // ['a','b','c','d','e','f','g'] returns 'a'
buffer.last(); // ['a','b','c','d','e','f','g'] returns 'g'
buffer.pop(); // ['a','b','c','d','e','f'] returns 'g'
buffer.pop(); // ['a','b','c','d','e'] returns 'f'
buffer.shift(); // ['b','c','d','e'] returns 'a'
buffer.shift(); // ['c','d','e'] returns 'b'
buffer[ 0 ]; // ['c','d','e'] returns 'c'
buffer[ 1 ]; // ['c','d','e'] returns 'd'
buffer[ 2 ]; // ['c','d','e'] returns 'e'
buffer[ 10 ]; // ['c','d','e'] returned value is unpredictable
buffer[ 15 ]; // ['c','d','e'] returned value is unpredictable
isEmpty()
才返回true
isFull()
则返回true
size()
返回当前存储在缓冲区中的元素的数量;它应与[]
运算符结合使用以避免边界违规:第一个元素索引始终为0
(如果缓冲区不是空),最后一个元素索引始终是size() - 1
available()
返回在饱和缓冲区之前可以添加的元素数量capacity()
返回缓冲区可以存储的元素的数量,仅在用户定义时才完整,并且从不更改从1.3.0
删除,而被读取的纯构件可变capacity
代替clear()
将整个缓冲区重置为其初始状态(但是请注意,如果您在缓冲区中动态分配的对象,则该对象使用的内存不会发布:迭代缓冲区内容,并将对象相应地释放到其分配方法))copyToArray(array)
将缓冲区的内容复制到标准数组array
。阵列必须足够大才能将当前的所有元素保存在缓冲区中。copyToArray(conversionFn, array)
将缓冲区的内容复制到标准数组array
对每个元素上执行功能的内容,通常是类型转换。阵列必须足够大才能将当前的所有元素保存在缓冲区中。 从版本1.3.0
开始,库能够根据缓冲区容量自动检测索引的哪些数据类型:
65535
的缓冲区,那么您的索引将是一个unsigned long
255
的缓冲区的unsigned int
byte
就足够了此外,您可以将带有小索引的相同代码缓冲区和带有正常索引的缓冲区混合:以前是不可能的。
CircularBuffer< char , 100 > optimizedBuffer; // reduced memory footprint, index type is uint8_t (a.k.a. byte)
CircularBuffer< long , 500 > normalBuffer; // standard memory footprint, index type is unit16_t (a.k.a. unsigned int)
CircularBuffer< int , 66000 > hugeBuffer; // extended memory footprint, index type is unit32_t (a.k.a. unsigned long)
为了获得上述优化的最大优势,任何时候都需要参考缓冲区索引,您应该使用最合适的类型:使用decltype
指示符可以轻松实现这一点,例如以下示例:
// the iterator variable i is of the correct type, even if
// we don't know what's the buffer declared capacity
for (decltype(buffer):: index_t i = 0 ; i < buffer.size(); i++) {
avg += buffer[i] / buffer. size ();
}
如果您愿意,您可以别名索引类型并参考这样的别名:
using index_t = decltype(buffer):: index_t ;
for ( index_t i = 0 ; i < buffer.size(); i++) {
avg += buffer[i] / buffer. size ();
}
以下仅适用于1.3.0
之前的版本。
默认情况下,库使用unsigned int
索引,最多可容纳65535
个项目,但是您很少需要如此庞大的商店。
您可以在#include
指令之前将库索引切换到定义CIRCULAR_BUFFER_XS
acro的byte
类型:这将库本身使用的内存减少36
字节,但允许您在执行索引的访问时可能会挤出更多,如果您可以使用较小的数据类型来完成任何操作。
# define CIRCULAR_BUFFER_XS
# include < CircularBuffer.h >
CircularBuffer< short , 100 > buffer;
void setup () { }
void loop () {
// here i should be declared of type byte rather than unsigned int
// in order to maximize the effects of the optimization
for (byte i = 0 ; i < buffer. size () - 1 ; i++) {
Serial. print (buffer[i]);
}
}
请注意:此宏开关迫使缓冲区使用8位数据类型作为内部索引,因为所有缓冲区都将限于最大容量255
。
该库的确有助于与中断定义CIRCULAR_BUFFER_INT_SAFE
宏开关的中断,该开关将volatile
修饰符引入count
变量,从而使整个库以禁用某些编译器优化的价格更加友好。 #define
语句需要在#include
语句之前放置在某个地方:
# define CIRCULAR_BUFFER_INT_SAFE
# include < CircularBuffer.h >
CircularBuffer< unsigned long , 10 > timings;
void count () {
timings. push ( millis ());
}
void setup () {
attachInterrupt ( digitalPinToInterrupt ( 2 ), count, RISING);
}
void loop () {
Serial. print ( " buffer size is " ); Serial. println (timings. size ());
delay ( 250 );
}
请注意,这并不能使图书馆中断安全,但确实有助于其在中断驱动的公司中使用。
库的examples
夹中有多个示例:
copyToArray()
函数的使用。 如果您使用此库来存储动态分配的对象,请不要使用clear()
方法,因为不会执行内存DealLocation:您需要迭代缓冲区内容并相应地将内存发布到使用的分配方法,要么通过delete
(如果您已经使用了new
)或free
(在malloc
的情况下):
while (!buffer.isEmpty()) {
// pick the correct one
delete buffer. pop ();
free (buffer. pop ());
}
同样适用于pop()
和shift()
操作,因为任何动态分配的对象仅与缓冲区分离,但是它使用的内存并未自动释放(请参见Object.ino示例)
Record* record = new Record(millis(), sample); // a dynamically allocated object
buffer.push(record);
// somewhere else
if (!buffer.isEmpty()) {
Record* current = buffer. pop ();
Serial. println (current. value ());
delete current; // if you don't do this the object memory is lost!!!
}
copyToArray(array)
和copyToArray(array, convertFn)
.hpp
.h
标头扩展名来添加与nano 33 ble的兼容性shift()
和pop()
操作滥用以弄乱缓冲区[]
运算符,以防止边界访问abort()
是特定于AVR的以下大多数主要改进是由Erlkoenig90贡献的:谢谢Niklas!
capacity()
有利于恒定实例属性capacity
EventLogging
和Interrupts
示例CIRCULAT_BUFFER_XS
宏开关以支持自动索引类型标识UINT32_MAX
)CIRCULAR_BUFFER_INT_SAFE
memset
clear()
函数pop()
函数pop()
和shift()
实现capacity()
函数debug()
函数,默认情况下处理前处理器禁用