在閱讀libdispatch源碼時,會出現LIST_HEAD,LIST_ENTRY等鏈表定義,在libdispatch項目里無法直接跳轉到它們的定義處,因此對它們的實現會比較模糊,經查找發現它們定在<sys/queue.h>頭文件里面。
LIST_HEAD,LIST_ENTRY通常用來定義雙向鏈表。
一些宏定義
#if defined(__clang__) && defined(__cplusplus)
#define __MISMATCH_TAGS_PUSH \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wmismatched-tags\"")
#define __MISMATCH_TAGS_POP \
_Pragma("clang diagnostic pop")
#else
#define __MISMATCH_TAGS_PUSH
#define __MISMATCH_TAGS_POP
#endif
#if defined(__clang__)
#define __NULLABILITY_COMPLETENESS_PUSH \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wnullability-completeness\"")
#define __NULLABILITY_COMPLETENESS_POP \
_Pragma("clang diagnostic pop")
#else
#define __NULLABILITY_COMPLETENESS_PUSH
#define __NULLABILITY_COMPLETENESS_POP
#endif
__MISMATCH_TAGS_PUSH:編譯器忽略未匹配的標識符警告
__NULLABILITY_COMPLETENESS_PUSH: 編譯器忽略未指定變量可空或不可空警告
LIST_HEAD
LIST_HEAD(name, type)用來定義鏈表頭
結構體
- name:結構體的名稱(可省略)
- type:鏈表節點類型
#define LIST_HEAD(name, type) \
__MISMATCH_TAGS_PUSH \
__NULLABILITY_COMPLETENESS_PUSH \
struct name { \
struct type *lh_first; /* first element */ \
} \
__NULLABILITY_COMPLETENESS_POP \
__MISMATCH_TAGS_POP
LIST_HEAD_INITIALIZER
LIST_HEAD_INITIALIZER(head) 鏈表表頭初始化,簡單的初始化為NULL
#define LIST_HEAD_INITIALIZER(head) \
{ NULL }
LIST_ENTRY
用來定義鏈表節點,le_next指向下一個節點,le_prev指向前一個節點的le_next地址。這樣的好處就是我們不必通過上一個節點拿到le_next了,就可以直接操作le_next。
#define LIST_ENTRY(type) \
__MISMATCH_TAGS_PUSH \
__NULLABILITY_COMPLETENESS_PUSH \
struct { \
struct type *le_next; /* next element */ \
struct type **le_prev; /* address of previous next element */ \
} \
__NULLABILITY_COMPLETENESS_POP \
__MISMATCH_TAGS_POP
LIST_NEXT
先看一下LIST_NEXT的定義,返回節點elm的le_next指針。
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
LIST_CHECK_HEAD、LIST_CHECK_NEXT、LIST_CHECK_PREV
LIST_CHECK_HEAD、LIST_CHECK_NEXT、LIST_CHECK_PREV:字面是意思檢查頭指針、下一個指針,前一個指針是否有效,頭文件里面沒有明確定義,可以在使用時候自行定義。
#define LIST_CHECK_HEAD(head, field)
#define LIST_CHECK_NEXT(elm, field)
#define LIST_CHECK_PREV(elm, field)
LIST_EMPTY、LIST_FIRST
LIST_EMPTY:鏈表頭的lh_first指針置空。
LIST_FIRST:鏈表第一個元素的指針。
#define LIST_EMPTY(head) ((head)->lh_first == NULL)
#define LIST_FIRST(head) ((head)->lh_first)
LIST_FOREACH
遍歷鏈表的for循環語句頭的簡寫,需要加上花括號。(var為鏈表第一個節點指針,var不為空, 本次循環結束后var為下一個節點指針)
var:for語句里面的自動變量。
head:鏈表頭。
field:var里面表示鏈表前后索引的成員名。
#define LIST_FOREACH(var, head, field) \
for ((var) = LIST_FIRST((head)); \
(var); \
(var) = LIST_NEXT((var), field))
下面是libdispatch里面的源碼示例:
LIST_FOREACH(dmn, dmb, dmn_list) {
if (dmn->dmn_ident == ident && dmn->dmn_filter == filter) {
break;
}
}
LIST_FOREACH_SAFE
LIST_FOREACH_SAFE相比LIST_FOREACH,先把下個節點指針取出來,等這次循環后直接把下個節點指針賦值給var,這樣就可以避免在循環體里面修改了當前var下個節點指針的指向,造成循環結束或者錯亂。
#define LIST_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = LIST_FIRST((head)); \
(var) && ((tvar) = LIST_NEXT((var), field), 1); \
(var) = (tvar))
LIST_INIT
初始化鏈表,將鏈表頭下一個指針指向空。
#define LIST_INIT(head) do { \
LIST_FIRST((head)) = NULL; \
} while (0)
LIST_INSERT_AFTER
LIST_INSERT_AFTER:在鏈表節點listelm的后面插入節點elm。
- 首先
(elm)->field.le_next
賦值為(listelm)->field.le_next
,如果listelm的原下一個節點不為空,原下一個節點(listelm)->field.le_next->field.le_prev
等于((elm)->field.le_next)
地址值。 -
(listelm)->field.le_next
新指向elm
。 -
(elm)->field.le_prev
賦值為(listelm)->field.le_next
的地址。
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
LIST_CHECK_NEXT(listelm, field); \
if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\ //
LIST_NEXT((listelm), field)->field.le_prev = \
&LIST_NEXT((elm), field); \ //
LIST_NEXT((listelm), field) = (elm); \
(elm)->field.le_prev = &LIST_NEXT((listelm), field); \
} while (0)
LIST_INSERT_BEFORE
LIST_INSERT_BEFORE:在鏈表節點listelm的前面插入節點elm。
- 首先
(elm)->field.le_prev
賦值為(listelm)->field.le_prev
。 - 然后
(elm)->field.le_next
新指向listelm
。 -
*(listelm)->field.le_prev***
指向elm
。le_prev是原上一個節點le_next的指針,那么```*(listelm)->field.le_prev ***就是上一個節點的le_next指向。 -
(listelm)->field.le_prev
賦值為elm
的le_next地址。
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
LIST_CHECK_PREV(listelm, field); \
(elm)->field.le_prev = (listelm)->field.le_prev; \
LIST_NEXT((elm), field) = (listelm); \
*(listelm)->field.le_prev = (elm); \
(listelm)->field.le_prev = &LIST_NEXT((elm), field); \
} while (0)
LIST_INSERT_HEAD
LIST_INSERT_HEAD:在鏈表頭部插入節點elm。
- 首先
(elm)->field.le_next
賦值為(head)->lh_first
,如果鏈表不為空,head的原下一個節點(head)->lh_first->field.le_prev
等于((elm)->field.le_next)
地址值。 -
(head)->lh_first
指向elm
。 -
(elm)->field.le_prev
賦值為(head)->lh_first
的地址。
#define LIST_INSERT_HEAD(head, elm, field) do { \
LIST_CHECK_HEAD((head), field); \
if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \
LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
LIST_FIRST((head)) = (elm); \
(elm)->field.le_prev = &LIST_FIRST((head)); \
} while (0)
LIST_REMOVE
LIST_REMOVE:在鏈表中移除elm。
- 如果
(elm)->field.le_next
不為NULL,那么(elm)->field.le_next->field.le_prev = (elm)->field.le_prev;
下個節點的le_prev等于當前節點的le_prev。 - 當前節點上一個節點的next指向當前節點的下個節點。
-
(head)->lh_first
指向elm
。 -
(elm)->field.le_prev
賦值為(head)->lh_first
的地址。 - 把當前節點的le_next、le_prev值置為-1,
#define TRASHIT(x) do {(x) = (void *)-1;} while (0)
#define LIST_REMOVE(elm, field) do { \
LIST_CHECK_NEXT(elm, field); \
LIST_CHECK_PREV(elm, field); \
if (LIST_NEXT((elm), field) != NULL) \
LIST_NEXT((elm), field)->field.le_prev = \
(elm)->field.le_prev; \
*(elm)->field.le_prev = LIST_NEXT((elm), field); \
TRASHIT((elm)->field.le_next); \
TRASHIT((elm)->field.le_prev); \
} while (0)
LIST_SWAP
LIST_SWAP:更換兩個鏈表的全部節點。
-
swap_tmp
指向(head1)->lh_first
-
(head1)->lh_first
賦值為(head2)->lh_first
-
(head2)->lh_first
賦值為swap_tmp
- 如果
head1
節點不為NULL
,head1
第一個節點的le_prev
,指向head1
的lh_first
- 如果
head2
節點不為NULL
,head2
第一個節點的le_prev
,指向head2
的lh_first
#define LIST_SWAP(head1, head2, type, field) \
__MISMATCH_TAGS_PUSH \
__NULLABILITY_COMPLETENESS_PUSH \
do { \
struct type *swap_tmp = LIST_FIRST((head1)); \
LIST_FIRST((head1)) = LIST_FIRST((head2)); \
LIST_FIRST((head2)) = swap_tmp; \
if ((swap_tmp = LIST_FIRST((head1))) != NULL) \
swap_tmp->field.le_prev = &LIST_FIRST((head1)); \
if ((swap_tmp = LIST_FIRST((head2))) != NULL) \
swap_tmp->field.le_prev = &LIST_FIRST((head2)); \
} while (0) \
__NULLABILITY_COMPLETENESS_POP \
__MISMATCH_TAGS_POP
--- END ---