仪陇家园分类信息网、仪陇生活网、仪陇家园网

搜索

sys/queue.h分析(图片复制不过来,查看原文)

[复制链接]
seo 发表于 2022-5-31 13:32:26 | 显示全部楼层 |阅读模式
sys/queue.h分析(图片复制不过来,查看原文)发布时间:2022/5/31 13:09:30
            
                                                       
                                                       
            
        
        
               
                    这两天有兴趣学习使用了下系统头文件sys/queue.h中的链表/队列的实现,感觉实现的很是优美,关键是以后再也不需要自己实现这些基本的数据结构了,哈哈!

我的系统环境是



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

测试程序tailq.c如下:

#include   
#include   
#include   
  
struct _Data {  
     int                 value;  
     TAILQ_ENTRY(_Data)  tailq_entry;  
};  
  
int main(int argc, const char *argv[])  
{  
     /* 1. 初始化队列 */  
#if 0  
     TAILQ_HEAD(tailq_head, _Data)   head = TAILQ_HEAD_INITIALIZER(head);  
#else  
     TAILQ_HEAD(tailq_head, _Data)   head;  
     TAILQ_INIT(&head);  
#endif  
     int i;  
     struct _Data *pdata = NULL;  
  
     /* 2. 在队列末尾插入data1 */  
     struct _Data *data1 = (struct _Data *)calloc(1, sizeof(struct _Data));  
     data1->value = 1;  
     TAILQ_INSERT_TAIL(&head, data1, tailq_entry);  
     /* 3. 在队列末尾插入data2 */  
     struct _Data *data2 = (struct _Data *)calloc(1, sizeof(struct _Data));  
     data2->value = 2;  
     TAILQ_INSERT_TAIL(&head, data2, tailq_entry);  
     /* 4. 在data1之后插入data3 */  
     struct _Data *data3 = (struct _Data *)calloc(1, sizeof(struct _Data));  
     data3->value = 3;  
     TAILQ_INSERT_AFTER(&head, data1, data3, tailq_entry);  
     /* 5. 在data2之前插入data4 */  
     struct _Data *data4 = (struct _Data *)calloc(1, sizeof(struct _Data));  
     data4->value = 4;  
     TAILQ_INSERT_BEFORE(data2, data4, tailq_entry);  
     /* 6. 在队列首部插入data5 */  
     struct _Data *data5 = (struct _Data *)calloc(1, sizeof(struct _Data));  
     data5->value = 5;  
     TAILQ_INSERT_HEAD(&head, data5, tailq_entry);  
     /* 遍历队列 */  
     TAILQ_FOREACH(pdata, &head, tailq_entry) {  
         printf("pdata->value1 = %d\n", pdata->value);      
     }  
     puts("");  
     /* 7. 删除data5 */  
     TAILQ_REMOVE(&head, data5, tailq_entry);  
     free(data5);    /* TAILQ_REMOVE宏只是从队列中删除该节点,因此还需手动free */
  
     TAILQ_FOREACH(pdata, &head, tailq_entry) {  
         printf("pdata->value1 = %d\n", pdata->value);      
     }  
     puts("");  
  
     /* 正序遍历尾队列 */  
     /* 方法一 */  
     TAILQ_FOREACH(pdata, &head, tailq_entry) {  
         printf("pdata->value1 = %d\n", pdata->value);      
     }  
     puts("");  
     /* 方法二 */  
     for (pdata = TAILQ_FIRST(&head); pdata;   
                     pdata = TAILQ_NEXT(pdata, tailq_entry)) {  
         printf("pdata->value1 = %d\n", pdata->value);      
     }  
  
     puts("");  
  
     /* 逆序遍历尾队列 */  
     /* 方法一 */  
     TAILQ_FOREACH_REVERSE(pdata, &head, tailq_head, tailq_entry) {  
         printf("pdata->value1 = %d\n", pdata->value);      
     }  
     puts("");  
     /* 方法二 */  
     for (pdata = TAILQ_LAST(&head, tailq_head); pdata;   
             pdata = TAILQ_PREV(pdata, tailq_head, tailq_entry)) {  
         printf("pdata->value1 = %d\n", pdata->value);      
         TAILQ_REMOVE(&head, pdata, tailq_entry);  
         free(pdata);  
     }  
  
     if (TAILQ_EMPTY(&head)) {  
         printf("the tail queue is empty now.\n");     
     }  
  
     exit(EXIT_SUCCESS);  
}

代码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,也可以使用上述的几个访问方法来遍历。

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




















               
        
        
   
            
        
        
回复

使用道具 举报

全部回复0 显示全部楼层

发表回复

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

楼主

审核员

热门推荐

联系客服 关注微信 下载APP 返回顶部 返回列表