【问题标题】:TAILQ_INSERT_TAIL macro <sys/queue.h>TAILQ_INSERT_TAIL 宏 <sys/queue.h>
【发布时间】:2018-07-07 08:49:46
【问题描述】:

谁能解释一下 TAILQ_INSERT_TAIL 宏的最后两行:

#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 (/*CONSTCOND*/0)

我知道,一般来说,为了在队列尾部插入一些节点,我们需要做 4 件事: 1) 在节点中指向下一个节点的空指针。 (宏的第一行) 2)在队列的最后一个元素中添加指向节点的指针(宏的第二行) 3) 在节点链接指针中指向前一个节点到队列中的最后一个元素。 4) 将队列头结构指针从队列中的最后一个元素更新到我们的节点。

1 和 2 点我在宏观上理解得很清楚,但是 3 和 4 有点误解,尤其是我看不懂这样的代码:

*(head)->tqh_last = (elm);

调用队列中的最后一个节点并被当前节点覆盖? 最后一个节点会是什么?

【问题讨论】:

    标签: c linux linux-kernel macros queue


    【解决方案1】:

    (head)-&gt;tqh_last是一个指向另一个指针的指针,看这里,最后一行:

    (head)-&gt;tqh_last = &amp;(elm)-&gt;field.tqe_next;

    所以,如果我们把最后两行交换一下,你就可以理解了

    (head)->tqh_last = &(elm)->field.tqe_next;
    *(head)->tqh_last = (elm);
    

    编辑

    参考&lt;sys/queue.h&gt;

    /*
     * Tail queue definitions.
     */
    #define _TAILQ_HEAD(name, type, qual)                                   \
    struct name {                                                           \
        qual type *tqh_first;           /* first element */                 \
        qual type *qual *tqh_last;      /* addr of last next element */     \
    }
    #define TAILQ_HEAD(name, type)  _TAILQ_HEAD(name, struct type,)
    
    #define TAILQ_HEAD_INITIALIZER(head)                                    \
            { NULL, &(head).tqh_first }
    
    1. qual type *qual *tqh_last;它有两颗星
    2. #define TAILQ_HEAD_INITIALIZER(head) { NULL, &amp;(head).tqh_first },它将thq_last设置为tqh_first的地址,这是一个指针,所以tqh_last是一个指向指针的指针

    参考

    查看 Linus 的 answer 的类似问题(很多,只需在页面中搜索 在光谱的另一端

    【讨论】:

    • '(head)->tqh_last' 是指向队列中最后一个节点的指针,好吗?所以 '*(head)->tqh_last' 获取存储在 '(head)->tqh_last' 中的数据?
    • @WhiteBear 它不仅仅是指向最后一个节点的指针,而是指向最后一个节点的成员(指向指针的指针)。见&lt;sys/queue.h&gt;TAILQ_HEAD的定义
    【解决方案2】:

    也许现在回答为时已晚,但我正在搜索相同的问题解决方案,并在查看代码时在下面发现。

    他们通过下面的代码在做什么是

    *(head)->tqh_last = (elm); 
    

    这有点难以理解,实际上这段代码所做的是保存新的节点地址,但由于这是一个双指针,而这个位置实际上是由 tqh_first 双指针保存的。 (检查 TAILQ_INIT)

    因此,对于第一次插入调用,它将使用新节点更新头部数据的 thq_first。如果你注意到了,实际上 TAILQ_INIT 正在把最后一个指针分配给第一个指针的地址。

       #define  TAILQ_INIT(head) do {                   \
            (head)->tqh_first = NULL;                   \
            (head)->tqh_last = &(head)->tqh_first;      \ <- This line
       } while (0);
    

    但在 TAILQ_INSERT_TAIL 之后,我们将使用 它自己的节点的结束指针。

    (head)->tqh_last = &(elm)->field.tqe_next;
    

    这是因为对于下一次迭代,我们希望将 tqe_next 与新节点链接。

    如果你看 TAILQ_FOR_EACH 的方式,那么你就会明白上面的逻辑为什么他们会这样。第一个元素由 HEAD 数据结构指向,但下一个元素由节点本身指向。

    #define TAILQ_FOREACH(var, head, field)                 \
        for (var = TAILQ_FIRST(head); var; var = TAILQ_NEXT(var, field))
    

    注意 - 这个 head->tqh_last 在每个 API 中都会发生变化,并且基于此它会生效。上面只是一个示例,说明在我们第一次使用 INIT 然后在 TAIL 处插入的情况下如何使用指针。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-04-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-02
      • 1970-01-01
      • 2019-01-11
      • 1970-01-01
      相关资源
      最近更新 更多