当前位置:   article > 正文

sys/queue.h分析_sys queue.h

sys queue.h

这两天有兴趣学习使用了下系统头文件sys/queue.h中的链表/队列的实现,感觉实现的很是优美,关键是以后再也不需要自己实现这些基本的数据结构了,哈哈!

我的系统环境是

正好需要使用队列,那么本篇就以其中的尾队列(tail queue)为例,结合实际的测试程序和示意图(亿图软件)来说明。

测试程序tailq.c如下:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <sys/queue.h>
  4. struct _Data {
  5. int value;
  6. TAILQ_ENTRY(_Data) tailq_entry;
  7. };
  8. int main(int argc, const char *argv[])
  9. {
  10. /* 1. 初始化队列 */
  11. #if 0
  12. TAILQ_HEAD(tailq_head, _Data) head = TAILQ_HEAD_INITIALIZER(head);
  13. #else
  14. TAILQ_HEAD(tailq_head, _Data) head;
  15. TAILQ_INIT(&head);
  16. #endif
  17. int i;
  18. struct _Data *pdata = NULL;
  19. /* 2. 在队列末尾插入data1 */
  20. struct _Data *data1 = (struct _Data *)calloc(1, sizeof(struct _Data));
  21. data1->value = 1;
  22. TAILQ_INSERT_TAIL(&head, data1, tailq_entry);
  23. /* 3. 在队列末尾插入data2 */
  24. struct _Data *data2 = (struct _Data *)calloc(1, sizeof(struct _Data));
  25. data2->value = 2;
  26. TAILQ_INSERT_TAIL(&head, data2, tailq_entry);
  27. /* 4. 在data1之后插入data3 */
  28. struct _Data *data3 = (struct _Data *)calloc(1, sizeof(struct _Data));
  29. data3->value = 3;
  30. TAILQ_INSERT_AFTER(&head, data1, data3, tailq_entry);
  31. /* 5. 在data2之前插入data4 */
  32. struct _Data *data4 = (struct _Data *)calloc(1, sizeof(struct _Data));
  33. data4->value = 4;
  34. TAILQ_INSERT_BEFORE(data2, data4, tailq_entry);
  35. /* 6. 在队列首部插入data5 */
  36. struct _Data *data5 = (struct _Data *)calloc(1, sizeof(struct _Data));
  37. data5->value = 5;
  38. TAILQ_INSERT_HEAD(&head, data5, tailq_entry);
  39. /* 遍历队列 */
  40. TAILQ_FOREACH(pdata, &head, tailq_entry) {
  41. printf("pdata->value1 = %d\n", pdata->value);
  42. }
  43. puts("");
  44. /* 7. 删除data5 */
  45. TAILQ_REMOVE(&head, data5, tailq_entry);
  46. free(data5); /* TAILQ_REMOVE宏只是从队列中删除该节点,因此还需手动free */
  47. TAILQ_FOREACH(pdata, &head, tailq_entry) {
  48. printf("pdata->value1 = %d\n", pdata->value);
  49. }
  50. puts("");
  51. /* 正序遍历尾队列 */
  52. /* 方法一 */
  53. TAILQ_FOREACH(pdata, &head, tailq_entry) {
  54. printf("pdata->value1 = %d\n", pdata->value);
  55. }
  56. puts("");
  57. /* 方法二 */
  58. for (pdata = TAILQ_FIRST(&head); pdata;
  59. pdata = TAILQ_NEXT(pdata, tailq_entry)) {
  60. printf("pdata->value1 = %d\n", pdata->value);
  61. }
  62. puts("");
  63. /* 逆序遍历尾队列 */
  64. /* 方法一 */
  65. TAILQ_FOREACH_REVERSE(pdata, &head, tailq_head, tailq_entry) {
  66. printf("pdata->value1 = %d\n", pdata->value);
  67. }
  68. puts("");
  69. /* 方法二 */
  70. for (pdata = TAILQ_LAST(&head, tailq_head); pdata;
  71. pdata = TAILQ_PREV(pdata, tailq_head, tailq_entry)) {
  72. printf("pdata->value1 = %d\n", pdata->value);
  73. TAILQ_REMOVE(&head, pdata, tailq_entry);
  74. free(pdata);
  75. }
  76. if (TAILQ_EMPTY(&head)) {
  77. printf("the tail queue is empty now.\n");
  78. }
  79. exit(EXIT_SUCCESS);
  80. }
代码github地址: https://github.com/astrotycoon/sys-queue.h


我们首先来看一下这个尾队列的定义:


注意,其中的tqe_prev指向的不是前一个元素,而是前一个元素中的tqe_next,这样定义的一个好处就是*tqe_prev就是自身的地址,**tqe_prev就是自身。

好,现在就顺着我的测试程序来一步步看如何使用这个尾队列吧!

第一步是初始化步骤。关于初始化我们有两种方法:使用宏TAILQ_HEAD_INITIALIZER或者使用宏TAILQ_INIT,这两者都是可以的,唯一的区别是传递给宏TAILQ_INIT的是地址,而传递给宏TAILQ_HEAD_INITIALIZER的不是,这点需要引起我们的注意。


初始化后的数据结构怎样的呢? 我们看下示意图:


接下来的两个步骤(步奏2和步奏3)都是在这个队列的尾部追加元素(data1和data2),使用的是宏TAILQ_INSERT_TAIL:


那么队列的变化过程是这样的,请看示意图:



接下来的操作是在data1之前插入data3,使用的是宏TAILQ_INSERT_AFTER:


形象的示意图如下:


整理后的示意图如下:


紧接着的操作是在data2之前插入data4,使用的是宏TAILQ_INSERT_BEFORE:


形象的示意图如下:


整理后的示意图如下:


现在在队列首部插入data5,使用的是宏TAILQ_INSERT_HEAD:


形象的示意图如下:


整理后的示意图如下:


删除数据data5使用是宏TAILQ_REMOVE:


现在的队列布局如下:


好了,基本的操作就这么多,接下来我们看看提供的几个数据元素访问方法:


前三个很简单,一看就懂,我们重点分析下TAILQ_LAST和TAILQ_PREV。

TAILQ_LAST的目的是获取队列中的最后一个元素的地址,注意是地址哦!(head)->tqh_last代表的是最后一个元素中tqe_next的地址,通过强转之后,((struct headname *)((head)->tqh_last))->tqh_last实际上就是最后一个元素中的tqe_prev,而文章一开始介绍定义的时候就说过,*tqe_prev代表的是自身元素的地址,所以TAILQ_LAST最后获取的就是最后一个元素的地址,宏TAILQ_PREV的道理是一样的。

OK,测试程序接下来就是遍历整个队列,并打印出数据,可以使用提供的宏TAILQ_FOREACH,也可以使用上述的几个访问方法来遍历。


好了,其实本文没啥内容,对我个人而言就主要是想熟悉下亿图这个软件,哈哈




参考链接:

queue.h之尾队列

关于libevent与FreeBSD内核中TAILQ队列的理解

深入理解FreeBSD中的TAILQ

libevent源码分析-- queue.h中TAILQ_QUEUE的理解

queue.h(Circular queue 循环队列) 

libevent源码之TAILQ详解

《 QEMU代码中的QLIST

Libevent源码分析-----TAILQ_QUEUE队列

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/爱喝兽奶帝天荒/article/detail/918185
推荐阅读
相关标签
  

闽ICP备14008679号