通用队列及其应用

2020-03-07 17:53发布

本帖最后由 会笑的星星 于 2020-1-8 20:09 编辑

队列在我们编程的过程中经常会使用到,有时候一个项目中可能会使用多个不同的队列,为此我们可能需要编写多个针对不同队列的代码。虽然不同队列的数据内容、数据长度各不相同,但是出队与入队之类的操作却是一模一样的。因此,如果我们只是因为存储的内容或者长度不一样而编写多个不同的队列操作代码就会出现很多重复代码。为此,我设计了一个通用队列,仅使用一组接口就能操作多个不同的队列,而且具有比较好的适用性。
先上代码,再举几个例子。由于代码并不复杂,我只做了一些简单的注释,如果有不明白请提问。
  1. //app_queue.h

  2. #ifndef _APP_QUEUE_
  3. #define _APP_QUEUE_

  4. //#include "stdint.h"

  5. typedef unsigned char uint8_t;
  6. typedef unsigned int uint16_t;

  7. typedef enum
  8. {
  9.   Q_NONE,
  10.   Q_NON_NONE,
  11.   Q_S_NONE,
  12.   Q_S_OK,
  13. }Q_status_t;

  14. //如果你的队列很长,或者队列中每个元素占用的字节较多,最好把q_t的类型改为uint16_t.
  15. //原则上:当队列长度*队列中一个元素所占用的字节数量 小于 256时,q_t的类型使用uint8_t即可.
  16. //             当队列长度*队列中一个元素所占用的字节数量 大于 255时,q_t的类型必须使用uint16_t.
  17. typedef uint8_t q_t;
  18. typedef q_t *pq_t;

  19. typedef uint8_t *pq_data_t;

  20. typedef struct
  21. {
  22.   q_t head;
  23.   q_t end;
  24.   q_t q_len;
  25. }pos_t;

  26. //函数功能:初始化队列.
  27. //p_q      : 队列指针
  28. //q_max    : 队列元素个数
  29. //a_size   : 队列中一个元素的字节数量
  30. void app_queue_init(pq_t p_q, q_t q_max,uint8_t a_size);

  31. //函数功能:入队操作,将p_src指向的数据以及其后a_size个字节数据入队.
  32. void app_enqueue(pq_t p_q, pq_data_t p_src, uint8_t a_size);

  33. //函数功能:出队操作,从p_q指向的队列弹出一个元素,这个元素的首地址
  34. //         保存在p_des中,这个元素的字节数量为a_size.
  35. void app_dequeue(pq_t p_q, pq_data_t p_des, uint8_t a_size);

  36. //函数功能:判断队列是否为空.

  37. //函数返回: Q_NONE      --- 表示队列为空
  38. //          Q_NON_NONE  --- 表示队列非空
  39. uint8_t app_queue_none(pq_t p_q);

  40. //函数功能:判断p_q指向的队列中是否存在与目标元素相同的元素,如果有
  41. //         返回Q_S_OK,否则返回Q_S_NONE.
  42. //         所谓的目标元素,指的是p_src指向,大小为a_size的一个数据块

  43. //函数返回:Q_S_NONE --- 不相同
  44. //         Q_S_OK --- 相同
  45. uint8_t app_queue_search(pq_t p_q, pq_data_t p_src, uint8_t a_size);

  46. //函数功能:清空队列.
  47. void app_queue_clr(pq_t p_q);


  48. #endif
复制代码
  1. //app_queue.c

  2. #include "app_queue.h"
  3. #include "string.h"

  4. void app_queue_init(pq_t p_q, q_t q_max,uint8_t a_size)
  5. {
  6.   pos_t p;
  7.   uint8_t len;

  8.   len = sizeof(pos_t);
  9.   
  10.   memcpy(&p, p_q, len);
  11.   p.q_len = a_size * q_max;
  12.   memcpy(p_q, &p, len);
  13. }

  14. void app_queue_clr(pq_t p_q)
  15. {
  16.   pos_t p;
  17.   uint8_t len;

  18.   len = sizeof(pos_t);
  19.   
  20.   memcpy(&p, p_q, len);
  21.   p.end = 0;
  22.   p.head = 0;
  23.   memcpy(p_q, &p, len);
  24. }

  25. unsigned char app_queue_none(pq_t p_q)
  26. {
  27.   pos_t p;

  28.   memcpy(&p, p_q, sizeof(pos_t));

  29.   if(p.head == p.end)
  30.   {
  31.     return Q_NONE;
  32.   }
  33.   
  34.   return Q_NON_NONE;
  35. }

  36. void app_enqueue(pq_t p_q, pq_data_t p_src, uint8_t a_size)
  37. {
  38.   pos_t p;
  39.   uint8_t len;

  40.   len = sizeof(pos_t);

  41.   memcpy(&p, p_q, len);
  42.   
  43.   if(p.head == (p.end + a_size) % p.q_len)
  44.   {
  45.     return ;
  46.   }

  47.   memcpy((uint8_t *)(p_q+3)+p.end, p_src, a_size);
  48.   p.end = (p.end + a_size) % p.q_len;

  49.   memcpy(p_q, &p, len);
  50. }

  51. void app_dequeue(pq_t p_q, pq_data_t p_des, uint8_t a_size)
  52. {
  53.   pos_t p;
  54.   uint8_t len;

  55.   len = sizeof(pos_t);
  56.   memcpy(&p, p_q, len);
  57.    
  58.   if(p.head == p.end)
  59.   {
  60.     return ;
  61.   }

  62.   memcpy(p_des, (uint8_t *)(p_q+3)+p.head, a_size);
  63.   p.head = (p.head + a_size) % p.q_len;   
  64.   
  65.   memcpy(p_q, &p, len);
  66. }

  67. uint8_t app_queue_search(pq_t p_q, pq_data_t p_src, uint8_t a_size)
  68. {
  69.   pos_t p;
  70.   uint8_t *ps,*pt,*pm;
  71.   uint8_t i;

  72.   memcpy(&p, p_q, sizeof(pos_t));
  73.   
  74.   ps = (uint8_t *)(p_q+3);

  75.   while(p.head != p.end)
  76.   {
  77.     pt = ps + p.head;
  78.     pm = p_src;
  79.         
  80.     for(i = 0; i < a_size; i++)
  81.     {
  82.        if(*pt++ != *pm++) break;
  83.     }
  84.         
  85.     if(i == a_size)
  86.     {
  87.       return Q_S_OK;
  88.     }
  89.     else
  90.     {
  91.        p.head += a_size;
  92.        if(p.head >= p.q_len)
  93.        {
  94.          p.head = 0;               
  95.        }
  96.     }
  97.   }
  98.   
  99.   return Q_S_NONE;
  100. }
复制代码应用例子:
  1. #include "app_queue.h"

  2. #define D_Q_MAX 10
  3. #define R_Q_MAX 20

  4. typedef struct
  5. {
  6.    uint8_t mac[4];
  7.    uint8_t type;
  8.   uint8_t event;
  9.   uint8_t extend1;
  10. }dev_evt_t;

  11. typedef struct
  12. {
  13. //注意:在定义队列时,首先需要定义一个pos_t类型的变量,这个变量是一个占位变量,
  14. //应用者只需要定义它,不需要对它操作。pos_t定义好后,紧接着定义你的队列。
  15.   pos_t pos;
  16.   dev_evt_t d_q[D_Q_MAX];
  17. }dev_q_t;

  18. typedef struct
  19. {
  20.   pos_t pos;
  21.   uint16_t h_q[R_Q_MAX];
  22. }rx_q_t;

  23. dev_q_t dev_q;
  24. rx_q_t rx_q;

  25. main()
  26. {
  27.   //初始化队列dev_q
  28.   app_queue_init((pq_t)&dev_q, D_Q_MAX, sizeof(dev_evt_t));

  29.   //数据
  30.   dev_evt_t dev,dev_read;
  31.   dev.mac[0] = 0x01;
  32.   dev.mac[1] = 0x02;
  33.   dev.mac[2] = 0x03;
  34.   dev.mac[3] = 0x04;
  35.   dev.type = 0x06;
  36.   dev.event = 0x07;

  37.   //搜索队列中是否有重复数据,如果有,新的数据不会压入队列
  38.   if(app_queue_search((pq_t)&dev_q,(pq_data_t)&dev, sizeof(dev_evt_t)) == Q_S_NONE)
  39.   {
  40.     dev_evt_t dev;
  41.     dev.mac[0] = 0x01;
  42.     dev.mac[1] = 0x02;
  43.     dev.mac[2] = 0x03;
  44.     dev.mac[3] = 0x04;
  45.     dev.type = 0x06;
  46.     dev.event = 0x07;
  47.     //把数据压入队列。注意,不要忘记了地址符。
  48.     app_enqueue((pq_t)&dev_q, (pq_data_t)&dev, sizeof(dev_evt_t));
  49.   }
  50.   
  51.   //出队
  52.   app_dequeue((pq_t)&dev_q, (pq_data_t)&dev_read, sizeof(dev_evt_t));
  53.   if(dev_read.type == 0x06 && dev_read.event == 0x07)
  54.   {
  55.     //这样做没有任何意义,这里只做为例子展示
  56.   }
  57.   
  58.   //同理,针对队列rx_q的操作与上述类似
  59.   //初始化队列rx_q
  60.   app_queue_init((pq_t)&rx_q, R_Q_MAX, sizeof(uint16_t));  
  61.   
  62.   uint16_t rx_evt,evt_read;
  63.   rx_evt = 0xff01;
  64.   //入队
  65.   app_enqueue((pq_t)&rx_q, (pq_data_t)&rx_evt, sizeof(uint16_t));
  66.   //出队
  67.   app_dequeue((pq_t)&rx_q, (pq_data_t)&evt_read, sizeof(uint16_t));
  68.   
  69.   if(evt_read == 0xff01)
  70.   {
  71.      
  72.   }
  73. }
复制代码这个队列是经过实际检验的,所以如果你想用在自己的项目中,请放心使用,最后附上源码文件。

app_queue.zip (1.46 KB, 下载次数: 12) 2020-1-8 19:07 上传 点击文件名下载附件


友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
10条回答
会笑的星星
1楼-- · 2020-03-07 21:35
沙发
tdh03z
2楼-- · 2020-03-08 01:57
 精彩回答 2  元偷偷看……
会笑的星星
3楼-- · 2020-03-08 07:33
tdh03z 发表于 2020-1-8 21:59
为啥要 搜索队列中是否有重复数据,队列只管压入和弹出吧,业务逻辑还是不用放在队列这块 ...

他们是一组相关的操作,就像操作链表一样,有添加,有删除,有查询。
yklstudent
4楼-- · 2020-03-08 10:33
最好搞个队列用于串口的范例,不然新人不容易理解
llllll008
5楼-- · 2020-03-08 15:45
有什么项目,是会用到队列的,我几乎没有遇到过要用队列的项目
会笑的星星
6楼-- · 2020-03-08 15:51
本帖最后由 会笑的星星 于 2020-1-9 09:50 编辑
llllll008 发表于 2020-1-9 08:11
有什么项目,是会用到队列的,我几乎没有遇到过要用队列的项目

需要处理非常多的并行事件的项目基本都用得着。比如一个主控设备管理者几十个甚至上百个从设备的项目就需要。

一周热门 更多>