【问题标题】:Why do the TAILQ_INSERT_* macros require an entry to be bound to a variable?为什么 TAILQ_INSERT_* 宏需要将条目绑定到变量?
【发布时间】:2018-02-10 14:52:02
【问题描述】:

使用sys/queue.h的尾队列实现时,为什么下面的代码会起作用:

item_t *item = mkitem(...);
TAILQ_INSERT_HEAD(&list, item, entry);

以下应该是等价的,但不是:

TAILQ_INSERT_HEAD(&list, mkitem(...), entry);

最小的工作示例

#include <stdlib.h>
#include <stdio.h>
#include <sys/queue.h>

typedef struct item item_t;
typedef TAILQ_HEAD(list_head, item) list_head_t;

struct item {
    int value;
    TAILQ_ENTRY(item) entry;
};

static list_head_t items = TAILQ_HEAD_INITIALIZER(items);

static item_t* mkitem(int i) {
    item_t *item = calloc(1, sizeof(item_t));
    item->value = i;
    return item;
}

static void print_tailq() {
    item_t *it;
    TAILQ_FOREACH(it, &items, entry) {
        printf("%d,", it->value);
    }
    printf("\n");
}

int main() {
    item_t *i1, *i2, *i3;

    i1 = mkitem(1);
    i2 = mkitem(2);
    i3 = mkitem(3);

    TAILQ_INSERT_HEAD(&items, i1, entry);
    print_tailq();
    TAILQ_INSERT_HEAD(&items, i2, entry);
    print_tailq();
    TAILQ_INSERT_TAIL(&items, i3, entry);
    print_tailq();

    /* However, this does not work: */
    TAILQ_INSERT_HEAD(&items, mkitem(4), entry);
    print_tailq();
    TAILQ_INSERT_HEAD(&items, mkitem(5), entry);
    print_tailq();
    TAILQ_INSERT_TAIL(&items, mkitem(6), entry);
    print_tailq();

    return 0;
}

正如所料,print_tailq() 的前三个调用分别打印出来:

1,
2,1,
2,1,3,

但是,最后三个调用表明列表被TAILQ_INSERT_HEAD 截断,TAILQ_INSERT_TAIL 本质上是一个空操作。

4,
5,
5,

【问题讨论】:

  • 是的,确实如此。我希望对print_tailq 的第四、第五和第六次调用的输出分别为4,2,1,3,5,4,2,1,3,5,4,2,1,3,6,。实际输出在问题中给出。
  • 好的,我也试试。

标签: c linux queue bsd


【解决方案1】:

here实现TAILQ_INSERT_HEADTAILQ_INSERT_HEAD(head, elm, field)

#define TAILQ_INSERT_HEAD(head, elm, field) do {                \
    if (((elm)->field.tqe_next = (head)->tqh_first) != NULL)    \
        (head)->tqh_first->field.tqe_prev =                     \
            &(elm)->field.tqe_next;                             \
    else                                                        \
        (head)->tqh_last = &(elm)->field.tqe_next;              \
    (head)->tqh_first = (elm);                                  \
    (elm)->field.tqe_prev = &(head)->tqh_first;                 \
} while (0)

它是正在扩展的宏 - 所以它基本上是用多次调用 mkitem() 来替换 elm。直接传递mkitem() 的结果会在您的代码中调用错误行为。使用mkitem 直接覆盖之前的列表(存在内存泄漏)并创建具有单个元素的新列表 - 打印出来。您必须像之前一样在此处使用变量 - 否则它将不起作用。实际上,您认为这是一个功能,但事实并非如此。 (您会看到man 页面示例也反映了这种使用变量的想法)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-22
    相关资源
    最近更新 更多