链表之CIRCLEQ
文章目錄
- 頭文件
- 例子
- 代碼分析
- CIRCLEQ_ENTRY 和 CIRCLEQ_HEAD
- CIRCLEQ_HEAD_INITIALIZER
- CIRCLEQ_FIRST 和 CIRCLEQ_LAST
- CIRCLEQ_PREV 和 CIRCLEQ_NEXT
- CIRCLEQ_INIT
- CIRCLEQ_EMPTY
- CIRCLEQ_INSERT_TAIL
- CIRCLEQ_INSERT_HEAD
- CIRCLEQ_INSERT_AFTER
- CIRCLEQ_INSERT_BEFORE
- CIRCLEQ_FOREACH
- CIRCLEQ_FOREACH_REVERSE
- CIRCLEQ_REMOVE
頭文件
頭文件來(lái)自我最近看的項(xiàng)目:apache-mynewt-core-1.9.0\sys\sys\include\sys\queue.h
僅截取部分。
/** Circular queue declarations.*/ #define CIRCLEQ_HEAD(name, type) \ struct name { \struct type *cqh_first; /* first element */ \struct type *cqh_last; /* last element */ \ }#define CIRCLEQ_HEAD_INITIALIZER(head) \{ (void *)&(head), (void *)&(head) }#define CIRCLEQ_ENTRY(type) \ struct { \struct type *cqe_next; /* next element */ \struct type *cqe_prev; /* previous element */ \ }/** Circular queue functions.*/ #define CIRCLEQ_EMPTY(head) ((head)->cqh_first == (void *)(head))#define CIRCLEQ_FIRST(head) ((head)->cqh_first)#define CIRCLEQ_FOREACH(var, head, field) \for ((var) = CIRCLEQ_FIRST((head)); \(var) != (void *)(head) || ((var) = NULL); \(var) = CIRCLEQ_NEXT((var), field))#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \for ((var) = CIRCLEQ_LAST((head)); \(var) != (void *)(head) || ((var) = NULL); \(var) = CIRCLEQ_PREV((var), field))#define CIRCLEQ_INIT(head) do { \CIRCLEQ_FIRST((head)) = (void *)(head); \CIRCLEQ_LAST((head)) = (void *)(head); \ } while (0)#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \CIRCLEQ_NEXT((elm), field) = CIRCLEQ_NEXT((listelm), field); \CIRCLEQ_PREV((elm), field) = (listelm); \if (CIRCLEQ_NEXT((listelm), field) == (void *)(head)) \CIRCLEQ_LAST((head)) = (elm); \else \CIRCLEQ_PREV(CIRCLEQ_NEXT((listelm), field), field) = (elm);\CIRCLEQ_NEXT((listelm), field) = (elm); \ } while (0)#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \CIRCLEQ_NEXT((elm), field) = (listelm); \CIRCLEQ_PREV((elm), field) = CIRCLEQ_PREV((listelm), field); \if (CIRCLEQ_PREV((listelm), field) == (void *)(head)) \CIRCLEQ_FIRST((head)) = (elm); \else \CIRCLEQ_NEXT(CIRCLEQ_PREV((listelm), field), field) = (elm);\CIRCLEQ_PREV((listelm), field) = (elm); \ } while (0)#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \CIRCLEQ_NEXT((elm), field) = CIRCLEQ_FIRST((head)); \CIRCLEQ_PREV((elm), field) = (void *)(head); \if (CIRCLEQ_LAST((head)) == (void *)(head)) \CIRCLEQ_LAST((head)) = (elm); \else \CIRCLEQ_PREV(CIRCLEQ_FIRST((head)), field) = (elm); \CIRCLEQ_FIRST((head)) = (elm); \ } while (0)#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \CIRCLEQ_NEXT((elm), field) = (void *)(head); \CIRCLEQ_PREV((elm), field) = CIRCLEQ_LAST((head)); \if (CIRCLEQ_FIRST((head)) == (void *)(head)) \CIRCLEQ_FIRST((head)) = (elm); \else \CIRCLEQ_NEXT(CIRCLEQ_LAST((head)), field) = (elm); \CIRCLEQ_LAST((head)) = (elm); \ } while (0)#define CIRCLEQ_LAST(head) ((head)->cqh_last)#define CIRCLEQ_NEXT(elm,field) ((elm)->field.cqe_next)#define CIRCLEQ_PREV(elm,field) ((elm)->field.cqe_prev)#define CIRCLEQ_REMOVE(head, elm, field) do { \if (CIRCLEQ_NEXT((elm), field) == (void *)(head)) \CIRCLEQ_LAST((head)) = CIRCLEQ_PREV((elm), field); \else \CIRCLEQ_PREV(CIRCLEQ_NEXT((elm), field), field) = \CIRCLEQ_PREV((elm), field); \if (CIRCLEQ_PREV((elm), field) == (void *)(head)) \CIRCLEQ_FIRST((head)) = CIRCLEQ_NEXT((elm), field); \else \CIRCLEQ_NEXT(CIRCLEQ_PREV((elm), field), field) = \CIRCLEQ_NEXT((elm), field); \ } while (0)例子
#include <stddef.h> #include <stdio.h> #include <stdlib.h> #include "bsd_queue.h" // 就是我上面貼的代碼struct entry {int data;CIRCLEQ_ENTRY(entry) entries; /* Queue */ };CIRCLEQ_HEAD(circlehead, entry);int main(void) {struct entry *n1, *n2, *n3, *np;struct circlehead head; /* Queue head */int i;CIRCLEQ_INIT(&head); /* Initialize the queue */n1 = malloc(sizeof(struct entry)); /* Insert at the head */CIRCLEQ_INSERT_HEAD(&head, n1, entries);n1 = malloc(sizeof(struct entry)); /* Insert at the tail */CIRCLEQ_INSERT_TAIL(&head, n1, entries);n2 = malloc(sizeof(struct entry)); /* Insert after */CIRCLEQ_INSERT_AFTER(&head, n1, n2, entries);n3 = malloc(sizeof(struct entry)); /* Insert before */CIRCLEQ_INSERT_BEFORE(&head, n2, n3, entries);CIRCLEQ_REMOVE(&head, n2, entries); /* Deletion */free(n2);/* Forward traversal */i = 0;CIRCLEQ_FOREACH(np, &head, entries)np->data = i++;/* Reverse traversal */CIRCLEQ_FOREACH_REVERSE(np, &head, entries)printf("%i\n", np->data);/* Queue deletion */n1 = CIRCLEQ_FIRST(&head);while (n1 != (void *)&head) {n2 = CIRCLEQ_NEXT(n1, entries);free(n1);n1 = n2;}CIRCLEQ_INIT(&head);exit(EXIT_SUCCESS); }代碼分析
我們一點(diǎn)一點(diǎn)看上面的例子。
先來(lái)一張我自己繪制的示意圖。紅色表示我覺得不太“正?!钡牡胤?#xff0c;指針的類型不匹配,需要強(qiáng)制轉(zhuǎn)換。
CIRCLEQ_ENTRY 和 CIRCLEQ_HEAD
struct entry {int data;CIRCLEQ_ENTRY(entry) entries; /* Queue */ };CIRCLEQ_HEAD(circlehead, entry);宏展開是:
struct entry {int data;struct { struct entry *cqe_next; struct entry *cqe_prev; } entries; }; struct circlehead { struct entry *cqh_first; struct entry *cqh_last; };和其他鏈表不同,這里沒有用二級(jí)指針,cqe_prev 和 cqh_last 都是 struct entry * 類型。(為什么呢)
我前面寫的文章都是研究宏展開后的代碼,這次我們換一個(gè)思路,直接看宏的定義
CIRCLEQ_HEAD_INITIALIZER
#define CIRCLEQ_HEAD_INITIALIZER(head) \{ (void *)&(head), (void *)&(head) }初始化一個(gè)鏈表后,表頭的兩個(gè)指針都指向自己。
CIRCLEQ_FIRST 和 CIRCLEQ_LAST
#define CIRCLEQ_FIRST(head) ((head)->cqh_first) #define CIRCLEQ_LAST(head) ((head)->cqh_last)這兩個(gè)很好理解,不解釋了。
CIRCLEQ_PREV 和 CIRCLEQ_NEXT
#define CIRCLEQ_PREV(elm,field) ((elm)->field.cqe_prev) #define CIRCLEQ_NEXT(elm,field) ((elm)->field.cqe_next)field 就是最開始定義的 entries
CIRCLEQ_INIT
#define CIRCLEQ_INIT(head) do { \CIRCLEQ_FIRST((head)) = (void *)(head); \CIRCLEQ_LAST((head)) = (void *)(head); \ } while (0)初始化一個(gè)鏈表后,表頭的兩個(gè)指針都指向自己。如下圖
CIRCLEQ_EMPTY
#define CIRCLEQ_EMPTY(head) ((head)->cqh_first == (void *)(head))CIRCLEQ_INSERT_TAIL
#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \CIRCLEQ_NEXT((elm), field) = (void *)(head); \CIRCLEQ_PREV((elm), field) = CIRCLEQ_LAST((head)); \if (CIRCLEQ_FIRST((head)) == (void *)(head)) \CIRCLEQ_FIRST((head)) = (elm); \else \CIRCLEQ_NEXT(CIRCLEQ_LAST((head)), field) = (elm); \CIRCLEQ_LAST((head)) = (elm); \ } while (0)把 elm 插入到鏈表的尾部,都需要改變哪些指針呢?
代碼第 2、3 行分別解決了 1、2
代碼第 4、5 行解決了 3 的括號(hào)中的情況
第 7 行解決了 3,elm 前面的那個(gè)節(jié)點(diǎn)就是原來(lái)最后的節(jié)點(diǎn),CIRCLEQ_LAST(head),它的 cqe_next 就是 CIRCLEQ_NEXT(CIRCLEQ_LAST((head)), field)
第 8 行解決了 4
CIRCLEQ_INSERT_HEAD
#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \CIRCLEQ_NEXT((elm), field) = CIRCLEQ_FIRST((head)); \CIRCLEQ_PREV((elm), field) = (void *)(head); \if (CIRCLEQ_LAST((head)) == (void *)(head)) \CIRCLEQ_LAST((head)) = (elm); \else \CIRCLEQ_PREV(CIRCLEQ_FIRST((head)), field) = (elm); \CIRCLEQ_FIRST((head)) = (elm); \ } while (0)把 elm 插入到鏈表的頭部,都需要改變哪些指針呢?
第 2 行解決了 1
第 3 行解決了 2
第 4-5 行解決了 4 的括號(hào)中的情況
第 7 行解決了 4
第 8 行解決了 3
代碼 4-7 行說(shuō)明,因?yàn)闆]有用二級(jí)指針,所以頭插的時(shí)候需要分情況考慮。但是不用二級(jí)指針也有好處,就是代碼更好理解。
CIRCLEQ_INSERT_AFTER
#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \CIRCLEQ_NEXT((elm), field) = CIRCLEQ_NEXT((listelm), field); \CIRCLEQ_PREV((elm), field) = (listelm); \if (CIRCLEQ_NEXT((listelm), field) == (void *)(head)) \CIRCLEQ_LAST((head)) = (elm); \else \CIRCLEQ_PREV(CIRCLEQ_NEXT((listelm), field), field) = (elm);\CIRCLEQ_NEXT((listelm), field) = (elm); \ } while (0)要把 elm 插入到 listelm 元素的后面,要修改哪些指針呢?
2-3 行分別解決了 1、2
4-5 行解決了 4 括號(hào)里的情況
第 7 行解決了 4
第 8 行解決了 3
CIRCLEQ_INSERT_BEFORE
#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \CIRCLEQ_NEXT((elm), field) = (listelm); \CIRCLEQ_PREV((elm), field) = CIRCLEQ_PREV((listelm), field); \if (CIRCLEQ_PREV((listelm), field) == (void *)(head)) \CIRCLEQ_FIRST((head)) = (elm); \else \CIRCLEQ_NEXT(CIRCLEQ_PREV((listelm), field), field) = (elm);\CIRCLEQ_PREV((listelm), field) = (elm); \ } while (0)要把 elm 插入到 listelm 元素的前面,要修改哪些指針呢?
2-3 行分別解決了 1、2
4-5 行解決了 4 括號(hào)里的情況
第 7 行解決了 4
第 8 行解決了 3
CIRCLEQ_FOREACH
#define CIRCLEQ_FOREACH(var, head, field) \for ((var) = CIRCLEQ_FIRST((head)); \(var) != (void *)(head) || ((var) = NULL); \(var) = CIRCLEQ_NEXT((var), field))順序遍歷,只要 (var) != (void *)(head) 就會(huì)往下進(jìn)行,如果等于 head,根據(jù) || 運(yùn)算符的求值規(guī)則,會(huì)執(zhí)行 (var) = NULL,這個(gè)表達(dá)式的結(jié)果為 false,于是終止 for 循環(huán)
CIRCLEQ_FOREACH_REVERSE
#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \for ((var) = CIRCLEQ_LAST((head)); \(var) != (void *)(head) || ((var) = NULL); \(var) = CIRCLEQ_PREV((var), field))逆序遍歷,和順序遍歷類似,不解釋了。
CIRCLEQ_REMOVE
#define CIRCLEQ_REMOVE(head, elm, field) do { \if (CIRCLEQ_NEXT((elm), field) == (void *)(head)) \CIRCLEQ_LAST((head)) = CIRCLEQ_PREV((elm), field); \else \CIRCLEQ_PREV(CIRCLEQ_NEXT((elm), field), field) = \CIRCLEQ_PREV((elm), field); \if (CIRCLEQ_PREV((elm), field) == (void *)(head)) \CIRCLEQ_FIRST((head)) = CIRCLEQ_NEXT((elm), field); \else \CIRCLEQ_NEXT(CIRCLEQ_PREV((elm), field), field) = \CIRCLEQ_NEXT((elm), field); \ } while (0)移除節(jié)點(diǎn) elm,需要修改那些指針呢?
elm 前面那個(gè)節(jié)點(diǎn)的 field.cqe_next
elm 后面那個(gè)節(jié)點(diǎn)的 field.cqe_prev
對(duì)于 1,如果 elm 前面的節(jié)點(diǎn)是 head,那就需要修改 CIRCLEQ_FIRST(head)
對(duì)于 2, 如果 elm 后面沒有節(jié)點(diǎn)了,那就需要修改 CIRCLEQ_LAST(head)
代碼 2-3 行解決了 4
5-6 行解決了 2
代碼 7-8 行解決了 3
10-11 解決了 1
【end】
總結(jié)
以上是生活随笔為你收集整理的链表之CIRCLEQ的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 链表之TAILQ
- 下一篇: 中国K12在线教育市场调研及用户消费行为