GCD源碼淺析:LIST_HEAD、LIST_ENTRY、LIST_INIT…

在閱讀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的定義,返回節點elmle_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節點不為NULLhead1第一個節點的le_prev,指向head1lh_first
  • 如果head2節點不為NULLhead2第一個節點的le_prev,指向head2lh_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 ---

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,002評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,400評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,136評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,714評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,452評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,818評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,812評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,997評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,552評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,292評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,510評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,035評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,721評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,121評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,429評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,235評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,480評論 2 379

推薦閱讀更多精彩內容