环形缓冲区

在嵌入式软件开发中,经常会遇到这样的场景,创建一个临时缓冲区用于存放待读取的数据。这时候,环形缓冲区是一个不错的选择。

所谓环形缓冲区就是一段有限的内存空间,并且有两个指针分别代表读、写指向这一块内存,读指针表示读的位置,写指针则表示写的位置。当这块内存被写满的时候,写指针会回到内存的起始位置写,读操作也是如此,读到最后的位置会回到内存起始位置读取,要实现这样的指针移动功能,移动指针操作就并非单纯的++操作,而是

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

运行结果:
环形缓冲区运行结果

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值