<C语言>数据结构:环形缓冲区RingBuffer

数据结构:环形缓冲区

​ 当有大量数据的时候,我们不能存储所有的数据,那么计算机处理数据的时候,只能先处理先来的,那么处理完后呢,就会把数据释放掉,再处理下一个。那么,已经处理的数据的内存就会被浪费掉。因为后来的数据只能往后排队,如过要将剩余的数据都往前移动一次,那么效率就会低下了,肯定不现实,所以,环形队列就出现了。环形缓冲区是一项很好的技术,不用频繁的分配内存,而且在大多数情况下,内存的反复使用也使得我们能用更少的内存块做更多的事。
在这里插入图片描述

头文件

//ringbuffer.h
#ifndef _RINGBUFFER_H_
#define _RINGBUFFER_H_

#include "string.h"
#include "stdint.h"

/*
 *声明缓冲区结构体
 *param 1:数据缓存地址
 *param 2:掩码
 *param 3:读指针、写指针
 */
struct Ringbuffer {
    uint8_t *data;
    uint16_t mask;
    uint16_t put_ptr, get_ptr;
};
//初始化缓冲区,绑定内存地址
void        Ringbuffer_init(struct Ringbuffer *r, uint8_t *a,uint16_t size);
//清空缓冲区
void        Ringbuffer_clear(struct Ringbuffer *r);
//向缓冲区插入数据,写指针+1
uint8_t     Ringbuffer_push(struct Ringbuffer *r, uint8_t c);
//从缓冲区获取数据,读指针+1
uint8_t     Ringbuffer_pop(struct Ringbuffer *r);
//返回缓冲区总长度
uint16_t    Ringbuffer_size(struct Ringbuffer *r);
//返回缓冲区当前存储的元素的个数
uint16_t    Ringbuffer_elements(struct Ringbuffer *r);
//返回当前读指针后第n个位置的一个字节,读指针不移动
uint8_t     Ringbuffer_get_n(struct Ringbuffer *r,uint16_t n);
//获取当前读指针后长度为n的字符串,读指针移动相应长度
uint8_t     Ringbuffer_get_str(struct Ringbuffer *r,uint8_t* dst,uint16_t length);

#endif

源文件

//ringbuffer.c
#include "ringbuffer.h"

//绑定内存位置,将读、写指针设为0,掩码设置为内存长度-1
//注意:内存长度需要为2的指数
void Ringbuffer_init(struct Ringbuffer *r, uint8_t *dataptr, uint16_t size)
{
    r->data = dataptr;
    r->mask = size - 1;
    r->put_ptr = 0;
    r->get_ptr = 0;
}
//清空环形缓冲区
void Ringbuffer_clear(struct Ringbuffer *r)
{
    r->put_ptr = 0;
    r->get_ptr = 0;
    memset(r->data,0,r->mask);
}

//向缓冲区插入数据,写指针+1
uint8_t Ringbuffer_push(struct Ringbuffer *r, uint8_t c)
{
    //检查当前内存是否有足够的位置插入新的数据,仅if判断为真时读写指针处在同一位置,
    //此时缓冲区没有足够空间写入新的数据
    if(((r->put_ptr - r->get_ptr) & r->mask) == r->mask) {
        return 0;
    }
    //将数据存储到写指针位置
    r->data[r->put_ptr] = c;
    //写指针+1,如果写指针移动到内存末尾,则将写指针移动回0位置
    r->put_ptr = (r->put_ptr + 1) & r->mask;
    return 1;
}

uint8_t Ringbuffer_pop(struct Ringbuffer *r)
{
    uint8_t c;
    //检查是否有可读取的内容
    if(((r->put_ptr - r->get_ptr) & r->mask) > 0) {
        c = r->data[r->get_ptr];
    //读指针+1,如果读指针移动到内存末尾,则将读指针移动回0位置
        r->get_ptr = (r->get_ptr + 1) & r->mask;
        return c;
    } 
    else 
    {
        return -1;
    }
}

//返回缓冲区总长度
uint16_t Ringbuffer_size(struct Ringbuffer *r)
{
    return r->mask + 1;
}

//返回缓冲区当前存储的元素的个数
uint16_t Ringbuffer_elements(struct Ringbuffer *r)
{
    return (r->put_ptr - r->get_ptr) & r->mask;
}

//返回当前读指针后第n个位置的一个字节,读指针不移动
uint8_t Ringbuffer_get_n(struct Ringbuffer *r,uint16_t n)
{
    //检查是否有可读取的内容以及读取内容长度是否满足
    if(n<Ringbuffer_elements(r))
        return r->data[r->get_ptr+n-1];
    else
        return 0;
}

//获取当前读指针后长度为n的字符串,读指针移动相应长度
uint8_t Ringbuffer_get_str(struct Ringbuffer *r,uint8_t* dst,uint16_t length)
{
    if(Ringbuffer_elements(r)>length)
    {
        int i;
        for ( i= 0; i < length; i++)
        {
            dst[i]=Ringbuffer_pop(r);
        }
        return 1;  
    }
    else
    {
        return 0;
    }  
}


使用例程:

​ 该例程假设在数据接收端使用环形缓冲区接收usart中断的数据,在定时器中断中循环检测缓冲区收到的内容

//1声明结构体
struct  Ringbuffer	RX_Ringbuffer;
//2申请内存,长度为RX_Buffer_Size
uint8_t	RX_Buffer[RX_Buffer_Size];
//3初始化
Ringbuffer_init(&RX_Ringbuffer, RX_Buffer, RX_Buffer_Size);
//4串口中断函数中将数据放入环形缓冲区
Ringbuffer_push(&RX_Ringbuffer,data);
//5定时器中断函数中分析获取到的数据
uint8_t  count=0;
uint8_t  data[4]={0};
bool dataGotFlag = false;
while(!dataGotFlag)
{
    count++;
    //数据包以0x02开头, 以0x03开头结尾,长度为12
    if(Ringbuffer_pop(&Ultrasonic_RX_Ringbuffer)==0x02)
    {
      if(Ringbuffer_get_n(&_RX_Ringbuffer,12)==0x03)
      {
        //将获取到的字段放入变量保存
        Ringbuffer_get_str(&RX_Ringbuffer,data[0],4);
        dataGotFlag=true;
      }
      else
      {   
        if(count>=RX_Buffer_Size)
          memset(data,0,4);
          break;;
      }
    }
    else
    {
      //循环次数超过缓冲区大小则返回
      if(count>=RX_Buffer_Size)
        memset(data,0,4);
        break;;
    }
}

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值