libevent里面的尾队列TAILQ
一、TAILQ的队列头
TAILQ把整个队列头单独抽象为一个结构体TAILQ_HEAD,如下:
#ifndef TAILQ_HEAD
#define TAILQ_HEAD(name, type) \
struct name { \
struct type *tqh_first; /* first element*/ \
struct type **tqh_last; /* address of last next element*/ \
}
#endif
tqh_first是一个一级指针,指向队列的第一个元素;tqh_last是一个二级指针,它指向最后一个元素中的tqe_next的地址。
上面的定义中不需要在最后的}后添加分行(;)
TAILQ_HEAD(queue_head_s, queue_entry_s) queue_head_t;
宏展开后就成了:
struct queue_head_s {
struct queue_entry_s *tqh_first;
struct queue_entry_s **tqh_last;
} queue_head_t;
初始化队列头:
#define TAILQ_FIRST(head) ((head)->tqh_first)
#define TAIL_INIT(head) do { \
TAILQ_FIRST((head)) = NULL; \
head->last = &TAILQ_FIRST((head)); \
} while(0)
二、TAILQ的队列元素
#ifndef TAILQ_ENTRY
#define TAILQ_ENTRY(type) \
struct { \
struct type *tqe_next; /*next element*/ \
struct type **tqe_prev; /*address of previous next element*/ \
}
#endif
和前面的TAILQ_HEAD相比,TAILQ_ENTRY参数里面没有了name,即没有结构体名,所以该结构体只能作为一个匿名结构体,所以它一般都是另外一个结构体或者联合体的成员。如:
struct queue_entry_s {
int element;
TAILQ_ENTRY(queue_entry_s) entry;
};
展开后就变成
struct queue_entry_s {
int element;
struct { struct queue_entry_s *tqe_next; struct queue_entry_s *tqe_prev; } entry;
};
三、插入队列元素
考虑到队列是先进先出,这里研究从尾部插入新节点, 从头部删除节点。
1. 尾部插入
head: 队列头结点
elm: 实际节点对象
field: 队列节点元素
#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)
TAILQ_INSERT_TAIL(head, queue_entry, entry);
展开后变成:
do {
(queue_entry)->entry.tqe_next = NULL;
(queue_entry)->entry.tqe_prev = (head)->tqh_last;
*(head)->tqh_last = (queue_entry);
(head)->tqh_last =& ((queue_entry)->entry.tqe_next);
} while(0);
1) (head)->tqh_last 指向最后一个节点的next指针,所以在队列最后添加一个节点的时候,
*(head)->tqh_last = elem
2) (head)->tqh_last 指向最后一个节点的next指针
(head)->tqh_last =& ((queue_entry)->entry.tqe_next);
初始化状态,一个已经初始化的队列头,和一个待插入的队列元素
对新插入的元素的指针进行赋值,tqe_prev指向前一个next指针的地址,在尾部插入的情况下,就是tqh_last,因为tqh_last指向最后一个next指针的地址
将tqe_prev赋值为tqh_last,则tqe_prev和tqh_last所指的都是tqh_first.
2.更新tqh_first
添加第一个元素的时候,tqh_last就是tqh_first,所以更新tqh_first可以统一为更新(tqh_last)
3.更新tqh_last
4.调整一下变成
再次插入新的一个元素
step 0
step 1
step 2
四、删除队列元素
#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)->filed.tqe_prev = (elm)->filed.tqe_next; \
} while(0)
队列的一个特点就是先进先出,那么考虑删除第一个节点元素
已知如下队列,从中删除元素1的节点
step1
step2
step 3
最后还应该调用另外的释放函数来释放elm节点
五、队列中的第一个元素
#define TAILQ_FIRST(head) ((head)->tqh_first)
六、当前元素的下一个元素
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
七、列表队列中的每一个元素
#define TAILQ_FOREACH(var, head, field) \
for ((var) = TAILQ_FIRST(head); \
(var) != NULL; \
(var) = TAILQ_NEXT(var, field))
八、实例
gwwu@hz-dev2.aerohive.com:~/test/tailq>more queue.h
#ifndef __QUEUE_H__
#define __QUEUE_H__
#ifndef TAILQ_HEAD
#define TAILQ_HEAD(name, type) \
struct name { \
struct type *tqh_first; /* first element*/ \
struct type **tqh_last; /* address of last next element*/ \
}
#endif
#define TAILQ_FIRST(head) ((head)->tqh_first)
#define TAILQ_INIT(head) do { \
(head)->tqh_first = NULL; \
(head)->tqh_last = &((head)->tqh_first); \
} while(0)
#ifndef TAILQ_ENTRY
#define TAILQ_ENTRY(type) \
struct { \
struct type *tqe_next; \
struct type **tqe_prev; \
}
#endif
#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_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_NEXT(elm, field) ((elm)->field.tqe_next)
#define TAILQ_FOREACH(var, head, field) \
for((var) = TAILQ_FIRST(head); \
(var) != NULL; \
(var) = TAILQ_NEXT(var, field))
#endif
gwwu@hz-dev2.aerohive.com:~/test/tailq>more int_queue.c
#include <stdio.h>
#include <stdlib.h>
#include "queue.h"
struct queue_entry_t {
int value;
TAILQ_ENTRY(queue_entry_t) entry;
};
TAILQ_HEAD(queue_head_t, queue_entry_t); //define queue_head_t structure
int main(int argc, char *argv[])
{
struct queue_head_t queue_head;
struct queue_entry_t *p;
int i;
TAILQ_INIT(&queue_head);
for (i = 0; i < 3; i++) {
p = (struct queue_entry_t*)malloc(sizeof(struct queue_entry_t));
if (p == NULL) {
printf("malloc queue entry(%d) failed\n", i);
return -1;
}
p->value = i;
TAILQ_INSERT_TAIL(&queue_head, p, entry);
}
TAILQ_FOREACH(p, &queue_head, entry) {
printf("the %d node\n", p->value);
}
TAILQ_FOREACH(p, &queue_head, entry) {
TAILQ_REMOVE(&queue_head, p, entry);
}
return 0;
}
编译运行:
gwwu@hz-dev2.aerohive.com:~/test/tailq>gcc -g int_queue.c -o int_queue -Wall
gwwu@hz-dev2.aerohive.com:~/test/tailq>./int_queue
the 0 node
the 1 node
the 2 node