美文网首页
queue.h中TAILQ_QUEUE的解析

queue.h中TAILQ_QUEUE的解析

作者: chyyyccyy | 来源:发表于2019-01-13 10:28 被阅读0次

      queue.h最早来源于FreeBSD,出自某远古大神之手,里面定义了5种队列,singly-linked lists, lists, simple queues, tail queues, and circular queues。今天我们重点看下里面的tail queues。

    /*  $OpenBSD: queue.h,v 1.16 2000/09/07 19:47:59 art Exp $  */
    /*  $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $   */
    

       我们来看下代码结构:

    /*
     * Tail queue definitions.
     */
    #define TAILQ_HEAD(name, type)                      \
    struct name {                               \
        struct type *tqh_first; /* first element */         \
        struct type **tqh_last; /* addr of last next element */     \
    }
    
    #define TAILQ_HEAD_INITIALIZER(head)                    \
        { NULL, &(head).tqh_first }
    
    #define TAILQ_ENTRY(type)                       \
    struct {                                \
        struct type *tqe_next;  /* next element */          \
        struct type **tqe_prev; /* address of previous next element */  \
    }
    
    /*
     * tail queue access methods
     */
    #define TAILQ_FIRST(head)       ((head)->tqh_first)
    #define TAILQ_END(head)         NULL
    #define TAILQ_NEXT(elm, field)      ((elm)->field.tqe_next)
    #define TAILQ_LAST(head, headname)                  \
        (*(((struct headname *)((head)->tqh_last))->tqh_last))
    /* XXX */
    #define TAILQ_PREV(elm, headname, field)                \
        (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
    #define TAILQ_EMPTY(head)                       \
        (TAILQ_FIRST(head) == TAILQ_END(head))
    
    #define TAILQ_FOREACH(var, head, field)                 \
        for((var) = TAILQ_FIRST(head);                  \
            (var) != TAILQ_END(head);                   \
            (var) = TAILQ_NEXT(var, field))
    
    #define TAILQ_FOREACH_REVERSE(var, head, headname, field)       \
        for((var) = TAILQ_LAST(head, headname);             \
            (var) != TAILQ_END(head);                   \
            (var) = TAILQ_PREV(var, headname, field))
    
    /*
     * Tail queue functions.
     */
    #define TAILQ_INIT(head) do {                       \
        (head)->tqh_first = NULL;                   \
        (head)->tqh_last = &(head)->tqh_first;              \
    } while (0)
    
    #define TAILQ_INSERT_HEAD(head, elm, field) do {            \
        if (((elm)->field.tqe_next = (head)->tqh_first) != NULL)    \
            (head)->tqh_first->field.tqe_prev =         \
                &(elm)->field.tqe_next;             \
        else                                \
            (head)->tqh_last = &(elm)->field.tqe_next;      \
        (head)->tqh_first = (elm);                  \
        (elm)->field.tqe_prev = &(head)->tqh_first;         \
    } while (0)
    
    #define TAILQ_INSERT_TAIL(head, elm, field) do {            \
        (elm)->field.tqe_next = NULL;                   \
        (elm)->field.tqe_prev = (head)->tqh_last;           \
        *(head)->tqh_last = (elm);                  \
        (head)->tqh_last = &(elm)->field.tqe_next;          \
    } while (0)
    
    #define TAILQ_INSERT_AFTER(head, listelm, elm, field) do {      \
        if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
            (elm)->field.tqe_next->field.tqe_prev =         \
                &(elm)->field.tqe_next;             \
        else                                \
            (head)->tqh_last = &(elm)->field.tqe_next;      \
        (listelm)->field.tqe_next = (elm);              \
        (elm)->field.tqe_prev = &(listelm)->field.tqe_next;     \
    } while (0)
    
    #define TAILQ_INSERT_BEFORE(listelm, elm, field) do {           \
        (elm)->field.tqe_prev = (listelm)->field.tqe_prev;      \
        (elm)->field.tqe_next = (listelm);              \
        *(listelm)->field.tqe_prev = (elm);             \
        (listelm)->field.tqe_prev = &(elm)->field.tqe_next;     \
    } while (0)
    
    #define TAILQ_REMOVE(head, elm, field) do {             \
        if (((elm)->field.tqe_next) != NULL)                \
            (elm)->field.tqe_next->field.tqe_prev =         \
                (elm)->field.tqe_prev;              \
        else                                \
            (head)->tqh_last = (elm)->field.tqe_prev;       \
        *(elm)->field.tqe_prev = (elm)->field.tqe_next;         \
    } while (0)
    
    #define TAILQ_REPLACE(head, elm, elm2, field) do {          \
        if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL)   \
            (elm2)->field.tqe_next->field.tqe_prev =        \
                &(elm2)->field.tqe_next;                \
        else                                \
            (head)->tqh_last = &(elm2)->field.tqe_next;     \
        (elm2)->field.tqe_prev = (elm)->field.tqe_prev;         \
        *(elm2)->field.tqe_prev = (elm2);               \
    } while (0)
    

      我们再看个例子:

     #include <stdio.h>
     #include <stdlib.h>
     struct QUEUE_ITEM {
        int value;
        TAILQ_ENTRY(QUEUE_ITEM) entries;
    };
    TAILQ_HEAD(TAIL_QUEUE, QUEUE_ITEM) queue_head;
    int main(int argc, char **argv) {
        struct QUEUE_ITEM *item;
        struct QUEUE_ITEM *tmp_item;
    
        TAILQ_INIT(&queue_head);
        int i = 0;
        for (i = 5; i < 10; i += 2) {
            item = (struct QUEUE_ITEM*)malloc(sizeof(QUEUE_ITEM));
            item->value = i;
            TAILQ_INSERT_TAIL(&queue_head, item, entries);
        }
    
        struct QUEUE_ITEM *ins_item;
        ins_item = (struct QUEUE_ITEM*)malloc(sizeof(QUEUE_ITEM));
    
        ins_item->value = 100;
        TAILQ_INSERT_BEFORE(item, ins_item, entries);
    
    
        tmp_item = TAILQ_FIRST(&queue_head);
        printf("first element is %d\n", tmp_item->value);
    
        tmp_item = TAILQ_NEXT(tmp_item, entries);
        printf("next element is %d\n", tmp_item->value);
    
        tmp_item = TAILQ_NEXT(tmp_item, entries);
        printf("next element is %d\n", tmp_item->value);
    
        tmp_item = TAILQ_NEXT(tmp_item, entries);
        printf("next element is %d\n", tmp_item->value);
    
        getchar();
    }
    

      内存分布如图:


    无标题.png

      我们先看TAILQ_HEAD:
    tqh_first为队列第一个元素的地址;
    tqh_last为最后一个元素tqe_next的地址;
    tqh_last指向的指针为0;
      再看TAILQ_ENTRY:
    tqe_next为队列下一个元素的地址;
    tqe_prev为队列上一个元素tqe_next的地址;
    tqe_prev指向的指针为当前元素的地址;

      于是,我们在脑子里形成了对这个队列的大致结构。

      下面我们逐一深度分析队列的操作。

    #define TAILQ_HEAD_INITIALIZER(head) { NULL, &(head).tqh_first }
    #define TAILQ_INIT(head) do {                       \
        (head)->tqh_first = NULL;                   \
        (head)->tqh_last = &(head)->tqh_first;              \
    } while (0)
    

      这两个宏代表了队列的初始化,将tqh_first赋值为NULL,再将tqh_last 赋值为tqh_first的地址,如此做,当第一次添加元素时,对tqh_last 指向的元素操作既是对tqh_first操作。

    #define TAILQ_FIRST(head)       ((head)->tqh_first)
    #define TAILQ_END(head)         NULL
    #define TAILQ_NEXT(elm, field)      ((elm)->field.tqe_next)
    #define TAILQ_LAST(head, headname)                  \
        (*(((struct headname *)((head)->tqh_last))->tqh_last))
    /* XXX */
    #define TAILQ_PREV(elm, headname, field)                \
        (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
    

      TAILQ_LAST,TAILQ_PREV这两个宏可能比较难理解。
      先看TAILQ_LAST,我们思考下,如何获取最后一个元素的地址,从图上看,最后一个元素的tqe_prev指向的值即是最后一个元素的地址,由文章前面可知,最后一个元素的tqe_next值为(head)->tqh_last,如何从tqe_next获取到tqe_prev指向的值,因为TAILQ_ENTRY与TAILQ_HEAD的内存布局是一样的,稍加思考,于是就有了这个宏表达式。TAILQ_PREV类似。
      这里要注意,TAILQ_ENTRY与TAILQ_HEAD的内存布局是一样的,为什么TAILQ_LAST和TAILQ_PREV中不将(head)->tqh_last)转换为TAILQ_HEAD而不是转换为TAILQ_ENTRY是因为TAILQ_ENTRY这个结构体是直接定义在你用户定义的结构体中,无法获取他的类型来进行转换。
      其他的宏都比较简单,直来直去,比较好理解,这里就不详述了。以后有问题再作记录。

    相关文章

      网友评论

          本文标题:queue.h中TAILQ_QUEUE的解析

          本文链接:https://www.haomeiwen.com/subject/phykdqtx.html