良许Linux教程网 干货合集 一种数组环形队列的数据结构

一种数组环形队列的数据结构

概述

提出了一种更好的计算队尾指针的方法。

队尾指针新算法

提出了一种全新的计算队尾指针的公式:

假设现有一个模拟环形队列的线性表,其长度为N,队头指针为head,队尾指针为tail。现在每增加一条记录,可以使用以下方法来计算新的队尾指针:
tail = (tail + 1) % N

image-20240105190201915
image-20240105190201915

环形队列示意图

思考

但是,我在移植到8266的代码时,发现有点问题。

第一,head和tail应该是存储该数组的下标而不是一个指向该数组元素的指针。如果tail是指针,那么 tail本质上是一个内存地址,*tail是指向该数组的某一元素。那么这算式tail = (tail + 1) % N其实是对某数组元素的内存地址+1,然后在用N求余。
所以head和tail应该是保存该数组的下标,而不是指向该数组元素的指针。

第二,由于原先的代码,其队尾指针总是指向最后一个入队元素的下一个元素,而《算》给出的队列,队尾指针总是指向最后一个入队的元素。如上图,一个N=12的环形队列,原先的代码tail是指向第8个,《算》是指向第7个,由于我是在8266的示例代码上修改的,所以《算》给出的队尾指针算法需要调整一下:

//元素入队之后
tail++;         //tail指向最后一个入队的下一个元素
tail=tail % N;  //重新计算tail的数值123

新的数据结构

那么我就开始定义新的数据结构了,原先的数据结构是这样的

typedef struct{
    uint8_t* p_o;               //指向原点的指针,用来数组首地址
    uint8_t* volatile p_r;      //读取指针,相当于head
    uint8_t* volatile p_w;      //写入指针,相到于tail
    volatile int32_t fill_cnt;  //队列计数
    int32_t size;               //缓冲区的大小
}RINGBUF;

新的数据结构:

typedef struct {
    char  *buf;             //指向队列数组的指针
    unsigned int length;    //数组长度
    unsigned int head;      //队头,存储数组下标
    unsigned int tail;      //队尾,存储数组下标
    int fill_cnt;           //队列计数
}RINGBUF;

判断是否空队列

一开始,本来想用if(head==tail)来判断队列是否为空的,但是由于tail保存的是入队元素的下一个数组下标,当队列填满的时候,tail的下标正好等于head,所以不能通过if(head==tail)来判断队列是否为空,

完整代码

下面是完整的数组环形队列代码,运行环境是VS2015,主函数里进行了简单的测试:

// RingBuf.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"

typedef struct {
    char  *buf;             //指向队列数组的指针
    unsigned int length;    //长度
    unsigned int head;      //队头
    unsigned int tail;      //队尾
    int fill_cnt;           //队列计数
}RINGBUF;

int RINGBUF_Init(RINGBUF* r, char array[], size_t len)
{
    if (len return false;
    }

    r->buf = array;
    r->length = len;
    r->fill_cnt = 0;
    r->head = r->tail = 0;
    return true;
}

int RINGBUF_Put(RINGBUF* r, char data)
{
    //当tail+1等于head时,说明队列已满
    if (r->fill_cnt >= r->length) {
        printf("BUF FULL!\n");
        return false;                  // 如果缓冲区满了,则返回错误
    }

    r->buf[r->tail] = data;
    r->tail++;
    r->fill_cnt++;
    //计算tail是否超出数组范围,如果超出则自动切换到0
    r->tail = r->tail % r->length;
    return true;
}

int RINGBUF_Get(RINGBUF* r, char *c, unsigned int length)
{
    //当tail等于head时,说明队列空
    if (r->fill_cntprintf("BUF EMPTY!\n");
        return false;
    }

    //最多只能读取r->length长度数据
    if (length > r->length) {
        length = r->length;
    }

    int i;
    for (i = 0; ifill_cnt--;
        *c = r->buf[r->head++];                 // 返回数据给*c
        *c++;
        //计算head自加后的下标是否超出数组范围,如果超出则自动切换到0
        r->head = r->head % r->length;
    }
    return true;
}



#define BUF_LEN 5
RINGBUF BUFF;
char buf[BUF_LEN];

int main()
{
    RINGBUF_Init(&BUFF, buf, sizeof(buf));

    printf("1、逐个读取数据测试\r\n");
    int length = 5;
    for (int i = 0; i for (int i = 0; i printf("每次读取1个字节:buf pop : %d \r\n", data);  //打印该字节
    }

    printf("\n2、一次性读取测试\r\n");
    length = 5;
    for (int i = 0; i '1' + i);
    }
    char data2[11] = { 0 };
    RINGBUF_Get(&BUFF, data2, 5);
    printf("一次性读取5个字节:buf pop : %s \r\n", data2);  //打印该字节

    printf("\n3、放入超过缓冲区长度(BUF_LEN+1)数据测试:\r\n");
    length = BUF_LEN + 1;
    for (int i = 0; i '1'+i);
    }

    char data3[BUF_LEN+1] = { 0 };
    RINGBUF_Get(&BUFF, data3, BUF_LEN + 1);
    printf("一次性读取(BUF_LEN+1)个字节测试:buf pop : %s \r\n", data3);  //打印该字节

    //4、测试读取空缓冲区
    printf("\n4、读取空缓冲区测试:\r\n");
    RINGBUF_Get(&BUFF, data3, 2); //从BUFF读取2个字节

    return 0;
}

控制台打印信息如下:

1、逐个读取数据测试
每次读取1个字节:buf pop : 0
每次读取1个字节:buf pop : 1
每次读取1个字节:buf pop : 2
每次读取1个字节:buf pop : 3
每次读取1个字节:buf pop : 4

2、一次性读取测试
一次性读取5个字节:buf pop : 12345

3、放入超过缓冲区长度(BUF_LEN+1)数据测试:
BUF FULL!
一次性读取(BUF_LEN+1)个字节测试:buf pop : 12345

4、读取空缓冲区测试:
BUF EMPTY!
请按任意键继续…

后话

由于存在几种队尾指向元素的方式,以上代码是还可以在修改优化一下的。
《算》的代码是不需要考虑队列是否满了,他只需要直接覆盖旧的元素即可,我的需求是需要判断队列是否填满,以免旧的元素被覆盖。

以上就是良许教程网为各位朋友分享的Linu系统相关内容。想要了解更多Linux相关知识记得关注公众号“良许Linux”,或扫描下方二维码进行关注,更多干货等着你 !

137e00002230ad9f26e78-265x300
本文由 良许Linux教程网 发布,可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。
良许

作者: 良许

良许,世界500强企业Linux开发工程师,公众号【良许Linux】的作者,全网拥有超30W粉丝。个人标签:创业者,CSDN学院讲师,副业达人,流量玩家,摄影爱好者。
上一篇
下一篇

发表评论

联系我们

联系我们

公众号:良许Linux

在线咨询: QQ交谈

邮箱: yychuyu@163.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

关注微博
返回顶部