循环队列

1、引

  • 对于嵌入式编程,经常要用的的外围接口就是串口了,在串口中断中,一般只会收发,而不会做其它的事,收发的数据一般都来自一个队列里。

2、定义

  • 队列一般定义为先进先出的容器,入队时把数据插入到队尾,出队时将数据从队头取出——-FIFO.。

  • 一般队列都有以下的属性和方法:

    • front
    • back

    • empty

    • size
    • push_back
    • pop_front

3、C语言实现

结构体

#define SIZE 1024 
struct Queue
{
    int front,back; //头、尾指针
    unsigned char data[SIZE]; // 缓冲区大小

    bool (*empty)(Queue*); //判空
    int (*size)(Queue*);   //剩余大小
    bool (*push_back)(Queue*,unsigned char); // 入队
    bool (*pop_front)(Queue*,unsigned char*);// 出队
};

模块内部的接口定义

static bool empty(Queue* q);
static int size(Queue*q);
static bool push_back(Queue* q,unsigned char d);
static bool pop_front(Queue* q,unsigned char* d);

//对外的初始化接口

void init(Queue* q)
{
    q->back = 0 ;
    q->front = 0;

    q->size = &size;
    q->empty = ∅
    q->pop_front = &pop_front;
    q->push_back = &push_back;
}

//方法的实现

static bool empty(Queue* q)
{
    return q->back == q->front;
}

static int size(Queue*q)
{
   return (q->back - q->front + SIZE)%SIZE;
}

static bool push_back(Queue* q,unsigned char d)
{
    if((q->back+1)%SIZE == q->front)
    {
        return false;
    }
    else
    {
        q->data[q->back] = d;
        q->back = (q->back+1)%SIZE;

        return true;
    }
}

static bool pop_front(Queue* q,unsigned char* d)
{
    if(q->empty(q))
    {
        return false;
    }
    else
    {
        *d = q->data[q->front];
        q->front = (q->front+1)%SIZE;
        return true;
    }
}

使用:

    Queue queue;    
    init(&queue);

    //入队
    for(int i = 0 ; i < 10;i++)
        queue.push_back(&queue,i);

    //出队    
    unsigned char d;
    bool ret =  queue.pop_front(&queue,&d) ;

4、节省空间

这里我们定义了一个长度为SIZE的缓冲区,入队时的判断是如下:

    if((q->back+1)%SIZE == q->front)//判满
    {
        return false;
    }

如果定义了SIZE个大小的空间,我们只能使用SIZE-1个空间的大小,使用一个空间来判满。

这对于空间比较充裕的系统来说没有毛病,如果数组的元素是结构体,而且这个结构体还很大时,这就显得相当的浪费了,比如在接收CAN数据帧时LINUX内核里定义了如下结构体:

*/
struct can_frame {
 canid_t can_id;/* 32 bit CAN_ID + EFF/RTR/ERR flags */
 __u8     can_dlc; /* data length code: 0 .. 8 */
 __u8      data[8] __attribute__((aligned(8)));
};

这个结构体是8字节对齐的。

这里为了节省空间可以加多一个标志位:

这个标志位的作用如何运作呢?

初始为:0
如果 (q->back == q->front) && (q->flag == 0); 则是空
如果 (q->back == q->front) && (q->flag == 1); 则是满

入队时:q->back == q->front 刚满了。
出队时:q->back == q->front 刚空了。

需要注意是:满后第一次出队,空时的最后一次出队

5、 Version 2

#define SIZE 5
#define VER2
struct Queue
{
    int front,back;
#ifdef VER2
    int flag; //新加的标志
#endif
    unsigned char data[SIZE];

    bool (*empty)(Queue*);
    bool (*full)(Queue*);
    int (*size)(Queue*);
    bool (*push_back)(Queue*,unsigned char);
    bool (*pop_front)(Queue*,unsigned char*);
};

初始化:

void init(Queue* q)
{
#ifdef VER2
    q->back = 0 ;
    q->front = 0;
    q->flag = 0; //初始化为0
#else
    q->back = 0 ;
    q->front = 0;
#endif
    q->size = &size;
    q->empty = &empty;
    q->full = &full;
    q->pop_front = &pop_front;
    q->push_back = &push_back;
}

空与满的判断:

static bool empty(Queue* q)
{
#ifdef VER2
    return (q->back == q->front) && (q->flag == 0);//here
#else
    return q->back == q->front;
#endif
}

static bool full(Queue* q)
{
#ifdef VER2
    return (q->back == q->front) && (q->flag == 1);//here
#else
    return (q->back+1)%SIZE == q->front
#endif
}

入队与出队操作:

static bool push_back(Queue* q,unsigned char d)
{
#ifdef VER2
    if(q->full(q)) return false;
    else
    {
        q->data[q->back] = d;
        q->back = (q->back+1)%SIZE;

        if(q->back == q->front && q->flag == 0)//入队时,头尾相接则满了
        {
            q->flag = 1;
        }


        return true;
    }
#else
    if(q->full(q))
    {
        return false;
    }
    else
    {
        q->data[q->back] = d;
        q->back = (q->back+1)%SIZE;

        return true;
    }
#endif
}

static bool pop_front(Queue* q,unsigned char* d)
{
#ifdef VER2
    if(q->empty(q))
    {
        return false;
    }
    else
    {
        int tmp = q->front; //这里存一个临时变量
        *d = q->data[q->front];
        q->front = (q->front+1)%SIZE;

        //出最后一个数据的时候要注意,不然头指针会跑到尾指针后面去了。
        //也要保证满了后的第一次出队正常
        if(tmp == q->back && q->flag == 0) 
            q->front = q->back;

        q->flag = 0;

        return true;
    }

#else
    if(q->empty(q))
    {
        return false;
    }
    else
    {
        *d = q->data[q->front];
        q->front = (q->front+1)%SIZE;
        return true;
    }
#endif
}

6、总结

  1. 简单,基础
  2. 尾指针指向下一个空的位置

7、参考资料

https://en.wikipedia.org/wiki/Queue
https://www.tutorialspoint.com/data_structures_algorithms/dsa_queue.htm

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值