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();
}
内存分布如图:
我们先看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这个结构体是直接定义在你用户定义的结构体中,无法获取他的类型来进行转换。
其他的宏都比较简单,直来直去,比较好理解,这里就不详述了。以后有问题再作记录。