最近開始學習libevent源碼, 發現其存儲各類event的隊列用的是OpenBSD: queue.h的帶有尾節點信息的隊列實現(帶有尾節點是顯然的, 這樣插入節點的時候不用從頭開始遍歷, 只要o(1)時間便可), 該實現用一系列宏實現的隊列數據結構的抽象和範型, 發現其實現的確實很精妙, 因而就深刻進去學習其實現, 以備在之後的項目中使用。數據結構
tail queue涉及如下的數據結構:佈局
#define TAILQ_HEAD(name, type) \ struct name { \ struct type *tqh_first; /* first element */ \ struct type **tqh_last; /* addr of last next element */ \ } #define TAILQ_ENTRY(type) \ struct { \ struct type *tqe_next; /* next element */ \ struct type **tqe_prev; /* address of previous next element */ \ }
看看libevent中是如何使用的:學習
TAILQ_HEAD (event_list, event); //聲明瞭event_list這種數據結構 //擴展開就爲 struct event_list { \ struct event *tqh_first; /* first element */ \ struct event **tqh_last; /* addr of last next element */ \ }
很明顯咱們知道TAILQ_HEAD是用來聲明event_list這種數據結構, 而這個鏈表的節點類型爲struct event, tqh_first表示該鏈表的第一個節點, tqh_last表示最後一個節點的TAILQ_ENTRY字段中tqe_next值的地址(見下文)。指針
再看看TAILQ_ENTRY:code
struct event { 。。。 TAILQ_ENTRY (event) ev_next; TAILQ_ENTRY (event) ev_timeout_next; TAILQ_ENTRY (event) ev_add_next; 。。。 } //擴展開爲 struct event { 。。。 struct { struct event *tqe_next; struct event **tqe_prev; } ev_next; struct { struct event *tqe_next; struct event **tqe_prev; } ev_timeout_next; struct { struct event *tqe_next; struct event **tqe_prev; } ev_add_next; 。。。 }
由此可知TAILQ_ENTRY是在鏈表的實際節點結構中儲存鏈表先後繼節點信息的數據類型。tqe_next表示下一個節點指針, tqe_prev表示前一個節點的TAILQ_ENTRY字段中tqe_next值的地址。隊列
並且咱們發現:一、上面這兩種結構中他們的內存分配是同樣的 2 第二個字段tqe_prev、tqh_last都是個二級指針, 而不是前一個節點的指針或尾巴節點的指針(要是咱們實現一個待尾巴節點信息的鏈表, 估計咱們都會這樣實現, 二級指針可讓某些臨界條件下的操做不用特殊處理, 《c與指針》的鏈表實現那一章有很好的說明), tqe_prev表示前一個節點的TAILQ_ENTRY字段中tqe_next值的地址, 實際上也是TAILQ_ENTRY字段的地址。內存
下面給出此鏈表的結構圖:element
下面來看一下鏈表相關元素的訪問:源碼
#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, field, headname) \ for((var) = TAILQ_LAST(head, headname); \ (var) != TAILQ_END(head); \ (var) = TAILQ_PREV(var, headname, field))
#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)參考鏈表結構圖, 很容易理解, 這裏就很少作說明了。