Chinaunix首页 | 论坛 | 博客

fx

  • 博客访问: 1372325
  • 博文数量: 115
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 3964
  • 用 户 组: 普通用户
  • 注册时间: 2013-05-02 14:36
文章分类
文章存档

2022年(2)

2019年(2)

2018年(10)

2017年(1)

2016年(50)

2015年(12)

2014年(9)

2013年(29)

分类: C/C++

2015-09-29 14:52:28

用数组实现循环队列时会碰到如何判断队列满和空两种状态,一种方法是直接在队列结构体中添加一个 queue_sizecurr_size两个元素来判断队列的满和空。

Struct queue{

         TYPE buff[SIZE];                 //模拟队列的数组。

         Unsigned int    read_pos;

         Unsigned int    write_pos;

         Unsigned int    queue_size;

         Unsigned int  curr_size;

}

那么判断队列的空满就很简单。 Queue_size==curr_size时队列就是满,curr_size==0就是空。

 

另一种的实现是不需要curr_size变量,通过牺牲一个数组的单元来判断队列的空和满,即(write_pos+1)%queue_size==read_pos 表示队列满。Write_pos==read_pos时表示队列空。

第二种方法更能体现循环队列的‘循环’特性。

 

不过这两种方法都有一个弊端,就是无论何时对read_poswrite_pos的增操作都需要做模运算。 read_pos=(read_pos+1)%queue_size。或者是不做模运算但是每次增操作都需要判断是否溢出,是便赋值为0

 

这里还有第三中方法。不过这个方法也不是完美的。它的唯一的要求就是队列的大小需要是2n次幂。 但是他不需要第一种方法需要一个curr_size或者第二种方法的少用一个数组单元来判断队列的空和满,同时更重要的避免的模运算(相当慢的操作)

实现如下

Struct queue{

         TYPE buff[SIZE]                                          //size2n次幂(n大于0)

         Unsigned int write_pos;                             

         Unsigned int read_pos;                     

         Unsigned int size_mask;                             // SIZE-1

}

 

判断队列的空满如下实现:

判空:  write_pos==read_pos;

判满:     (write_pos-read_pos)>size_mask

 

那么入队操作如下:

int  enqueue(TYPE data,Queue q){

         If((q.write_pos-q.read_pos)<=q.size_mask){

                   q.buf[ q.write_pos & q.size_mask ]=data;                       //利用mask2n次方减一(有效

                   q.write_pos++;                                                    //位都是1)巧妙的利用与运算,避免

                   return SUCCESS;                                                  //了模运算和write_pos的回卷

         }else{

                   return NOMEM

         }

}

 

出队操作:

Int  dequeue(TYPE *data,Queue q){

         If((q.write_pos-q.read_pos)>0){

                   *data=q.buff[ q.read_pos & q.size_mask ];

                   q.read_pos++;

                   return SUCCESS;

         }else{

                   return NODATD

         }

}

 

log: 2015-12-18
该实现存在一个bug,当程序运行足够久后,或者说enqueue 调用的次数足够多时,最终会发生 wr下标回卷到0 这时候rd下标可能还未回卷。那么
wr-rd就是负值

由于  (q.write_pos-q.read_pos)<=q.size_mask  这三个变量都是无符号型的,上面的情况发生时,即wr-rd<0 被隐式转换为无符号型后数值可能很大从而是条件不成立。 但是此时可能队列并未满。从未导致,未满队列却不能入队数据

另外,如果一直调用enqueue操作。直到 wr回卷为0,此时wr = rd = 0,那么出队操作会因此判断队列时空的。到时有数据不能取,或者是继续调用enqueue导致之前的入队数据被覆盖。

 

阅读(4657) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

yunjie1672017-10-16 11:29:00

我看了下官方的这个fifo,发现读写指针确实是无符号型的,定义成这个感觉不太好吧