为什么 TAILQ_INSERT_* 宏需要一个条目绑定到一个变量?
Why do the TAILQ_INSERT_* macros require an entry to be bound to a variable?
当使用来自 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,
从here执行TAILQ_INSERT_HEAD
宏TAILQ_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
页面示例也反映了这种使用变量的想法)
当使用来自 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,
从here执行TAILQ_INSERT_HEAD
宏TAILQ_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
页面示例也反映了这种使用变量的想法)