在嵌入式软件开发中,经常会遇到这样的场景,创建一个临时缓冲区用于存放待读取的数据。这时候,环形缓冲区是一个不错的选择。
所谓环形缓冲区就是一段有限的内存空间,并且有两个指针分别代表读、写指向这一块内存,读指针表示读的位置,写指针则表示写的位置。当这块内存被写满的时候,写指针会回到内存的起始位置写,读操作也是如此,读到最后的位置会回到内存起始位置读取,要实现这样的指针移动功能,移动指针操作就并非单纯的++操作,而是
writePoint = (writePoint + 1) % BUFFSIZE; //读指针
readPoint = (readPoint + 1) % BUFFSIZE; //写指针
环形缓冲区代码的实现,关键在于判断缓冲区是空状态还是满状态。空状态表示没有数据可读取了,即环形缓冲区上的数据都是被读取过了,这时候读指针和写指针相同:
writePoint == readPoint; //相等表示缓冲区的数据都被读取过了
满状态则表示整个环形缓冲区的数据都没被读取过,这时候读指针位于内存的首地址,而写指针则位于缓冲区的最后一个元素的首地址
readPoint == (writePoint + 1) % BUFFSIZE;
下面的代码是基于面向对象思想来写的环形缓冲区:
#include <stdio.h>
#include <stdlib.h>
//容错判断宏
#define ERRP(con, ret, ...) do \
if (con){ \
printf(__VA_ARGS__); \
ret; \
}while(0)
//前向typedef
typedef struct _circleBuffer CIRCLEBUFFER;
//函数指针,作为结构体的"成员函数"
typedef int (*_isFull)(CIRCLEBUFFER* buff); //判断缓冲器是否满(所有数据都尚未被读取)
typedef int (*_isEmpty)(CIRCLEBUFFER* buff); //判断缓冲区是否为空(所有数据都被读取完毕)
typedef int (*_getData)(CIRCLEBUFFER* buff, void* OutputDat); //获取缓冲区上的数据
typedef int (*_putData)(CIRCLEBUFFER* buff, void* inputDat); //将数据写入缓冲区
//缓冲区的描述结构体
struct _circleBuffer{
void* buffer; //指向动态开辟饿缓冲区的首地址
int num; //指定开辟多少个数据元素的缓冲区
int size; //指定每个数据元素的大小
int readPos; //读指针
int writePos; //写指针
_isFull bufIsFull;
_isEmpty bufIsEmpty;
_getData getBufData;
_putData putBufData;
};
//销毁动态分配的环形缓冲区
void destroyCircleBuffer(CIRCLEBUFFER** buff)
{
CIRCLEBUFFER* my_buf = *buff;
free((*buff)->buffer);
free(my_buf);
*buff = NULL;
}
int isFull(CIRCLEBUFFER* buff)
{
//这个判断条件会使得缓冲区的最后一个数据位得不到填充,关系不大
return ((buff->writePos + 1) % buff->num == buff->readPos);
}
int isEmpty(CIRCLEBUFFER* buff)
{
return buff->writePos == buff->readPos;
}
int getData(CIRCLEBUFFER* buff, void* OutputDat)
{
if (buff->bufIsEmpty(buff)) //这里对函数指针的调用,记得要把对应的参数传入
return -1;
*((int* )OutputDat) = *((int* )(buff->buffer + buff->readPos * buff->size));
buff->readPos = (buff->readPos + 1) % buff->num;
return 0;
}
int putData(CIRCLEBUFFER* buff, void *inputDat)
{
//这个判断条件会使得缓冲区的最后一个数据位得不到填充,不过关系不大
if (buff->bufIsFull(buff)) //这里对函数指针的调用,记得要把对应的参数传入
return -1;
*((int *)(buff->buffer + buff->writePos * buff->size)) = *((int *)inputDat);
buff->writePos = (buff->writePos + 1) % buff->num;
return 0;
}
CIRCLEBUFFER* createCircleBuffer(int num, int size, _isFull isFull, _isEmpty isEmpty, _getData getData, _putData putData)
{
CIRCLEBUFFER* circleBuffer = NULL;
circleBuffer = (CIRCLEBUFFER* )malloc(sizeof(CIRCLEBUFFER));
ERRP(circleBuffer == NULL, return NULL, "malloc circleBuffer failed!!");
circleBuffer->num = num;
circleBuffer->size = size;
circleBuffer->bufIsFull = isFull;
circleBuffer->bufIsEmpty = isEmpty;
circleBuffer->readPos = 0;
circleBuffer->writePos = 0;
circleBuffer->getBufData = getData;
circleBuffer->putBufData = putData;
circleBuffer->buffer = malloc(circleBuffer->size * circleBuffer->num);
ERRP(circleBuffer == NULL, return NULL, "malloc buffer failed!!");
return circleBuffer;
ERR2:
free(circleBuffer);
ERR1:
return NULL;
}
int main(void)
{
int i = 0, ret = 0;
int OutBuf[200] = {};
CIRCLEBUFFER* circleBuffer = NULL;
//创建环形缓冲区,10个空间大小为4的数据成员空间
circleBuffer = createCircleBuffer(10, 4, isFull, isEmpty, getData, putData);
ERRP(circleBuffer == NULL, return -1, "circleBuffer is NULL!!\n");
//添加20个数据,实际只能添加9个,因为第10位被判断满的条件忽略了
for (i = 0; i < 20; i++)
{
ret = circleBuffer->putBufData(circleBuffer, (void* )&i);
ERRP(ret < 0, goto READ, "环形缓冲区已满,已经装了%d个数据\n", i);
}
READ:
for (i = 0; i < 2; i++) //读取2个
{
ret = circleBuffer->getBufData(circleBuffer, &OutBuf[i]);
ERRP(ret < 0, goto WRITE, "环形缓冲区已经没有数据,已经读取了%d个数据\n", i);
}
printf("读取了%d个数据\n", i);
WRITE:
for (i = 0; i < 15; i++) //写2个
{
ret = circleBuffer->putBufData(circleBuffer, (void* )&i);
ERRP(ret < 0, goto FREE, "环形缓冲区已满,已经装了%d个数据\n", i);
}
FREE:
destroyCircleBuffer(&circleBuffer); //释放堆空间
return 0;
}
运行结果: