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阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容