原文:https://blog.csdn.net/yannanxiu/article/details/52199178
概述
一种更好的计算队尾指针的方法。
队尾指针新算法
一个新的计算队尾指针的公式:
设模拟环形队列的线性表长度是N,队头指针为head,队尾指针为tail,则每增加一条记录,就可以用一下方法计算新的队尾指针:
tail = (tail + 1) % N思考
但是,我在移植到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 <2 || array==NULL){
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_cnt<=0) {
printf("BUF EMPTY!\n");
return false;
}
//最多只能读取r->length长度数据
if (length > r->length) {
length = r->length;
}
int i;
for (i = 0; i{
r->fill_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 < length; i++) {
RINGBUF_Put(&BUFF, i);
}
char data;
length = 5;
for (int i = 0; i < length; i++) {
RINGBUF_Get(&BUFF, &data, 1); //从BUFF读取1个字节
printf("每次读取1个字节:buf pop : %d \r\n", data); //打印该字节
}
printf("\n2、一次性读取测试\r\n");
length = 5;
for (int i = 0; i < length; i++) {
RINGBUF_Put(&BUFF, '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 < length; i++){
RINGBUF_Put(&BUFF, '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 : 42、一次性读取测试
一次性读取5个字节:buf pop : 123453、放入超过缓冲区长度(BUF_LEN+1)数据测试:
BUF FULL!
一次性读取(BUF_LEN+1)个字节测试:buf pop : 123454、读取空缓冲区测试:
BUF EMPTY!
请按任意键继续…后话
由于存在几种队尾指向元素的方式,以上代码是还可以在修改优化一下的。
《算》的代码是不需要考虑队列是否满了,他只需要直接覆盖旧的元素即可,我的需求是需要判断队列是否填满,以免旧的元素被覆盖。
版权声明:本文来源网络,免费传达知识,版权归原作者所有。如涉及作品版权问题,请联系我进行删除。
‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧ END ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧
关注我的微信公众号,回复“加群”按规则加入技术交流群。
点击“阅读原文”查看更多分享,欢迎点分享、收藏、点赞、在看。