循环队列

来源:互联网 发布:乐高ev3编程视频教程 编辑:程序博客网 时间:2024/05/17 07:42

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 VER2struct 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

原创粉丝点击