queue.h中TAILQ_QUEUE的解析

  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 
 #include 
 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的解析)