You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

关于sys/queue.h中TAILQ_INSERT_*宏及队列插入代码的疑问

Great questions about the sys/queue.h tail queue macros—let's break them down one by one!

1. Why do TAILQ_INSERT_* macros require entries to be bound to variables?

Let's start with what these macros actually do under the hood. The TAILQ_INSERT_* family (like TAILQ_INSERT_HEAD or TAILQ_INSERT_TAIL) directly tweaks the internal link pointers stored in each queue entry. These links live in the TAILQ_ENTRY struct you embed in your custom entry type.

For these modifications to work reliably, the entry needs a stable, valid memory address. Binding the entry to a variable—whether that's a stack variable, global variable, or a heap-allocated object assigned to a pointer—guarantees the memory for the entry (and its embedded link struct) sticks around as long as the entry is part of the queue.

If you tried using an entry that isn't bound to a variable (say, an anonymous temporary struct created inline in the macro call), that entry only exists for the current expression's lifespan. Once the expression finishes, that temporary stack memory gets reclaimed (overwritten by whatever comes next on the stack), leaving your queue with pointers to garbage. This causes undefined behavior: corrupted queues, crashes, or weird glitches like truncated lists.

Put simply: the macros need a reliable spot in memory to modify those link pointers, and binding to a variable ensures that spot stays valid while the entry is in the queue.

2. Why does one code snippet work but an "equivalent" one break the queue?

From your description—where TAILQ_INSERT_HEAD truncates the list and TAILQ_INSERT_TAIL acts like it does nothing—the most common culprit is using temporary anonymous entries in the broken code. Let's walk through examples to see the difference.

Working code (stable, variable-bound entries):

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

struct my_entry {
    int val;
    TAILQ_ENTRY(my_entry) links;
};

TAILQ_HEAD(my_queue, my_entry) queue = TAILQ_HEAD_INITIALIZER(queue);

void print_tailq() {
    struct my_entry *e;
    TAILQ_FOREACH(e, &queue, links) {
        printf("%d ", e->val);
    }
    printf("\n");
}

int main() {
    // Entries are bound to stack variables—their memory stays valid
    struct my_entry e1 = {.val = 1};
    struct my_entry e2 = {.val = 2};
    struct my_entry e3 = {.val = 3};

    TAILQ_INSERT_TAIL(&queue, &e1, links);
    print_tailq(); // Output: 1
    TAILQ_INSERT_TAIL(&queue, &e2, links);
    print_tailq(); // Output: 1 2
    TAILQ_INSERT_HEAD(&queue, &e3, links);
    print_tailq(); // Output: 3 1 2

    return 0;
}

Broken code (temporary anonymous entries):

// Same struct and print_tailq() as above

int main() {
    // Using inline anonymous structs—these are temporary stack objects!
    TAILQ_INSERT_TAIL(&queue, &(struct my_entry){.val = 1}, links);
    print_tailq(); // Might show 1 (temp is still on stack for now)
    TAILQ_INSERT_TAIL(&queue, &(struct my_entry){.val = 2}, links);
    print_tailq(); // Now the first temp is overwritten—output could be 2
    TAILQ_INSERT_HEAD(&queue, &(struct my_entry){.val = 3}, links);
    print_tailq(); // Truncated output, like 3, because old entries are gone

    return 0;
}

Why this breaks:

When you create an anonymous struct my_entry inline (like &(struct my_entry){.val = 1}), it's a temporary stack object. Its memory only exists for the duration of the macro call. Once TAILQ_INSERT_TAIL finishes, that memory is no longer reserved—it gets overwritten by the next function call, variable declaration, or stack operation.

By the time you call print_tailq() again, the previous entries' memory is already corrupted. Inserting a new temporary entry might overwrite the remains of the old ones, making it look like the list was truncated. TAILQ_INSERT_TAIL seems useless because the queue's "last" entry pointer is already pointing to invalid memory, so the macro can't correctly update the links.

The fix is straightforward: always bind your queue entries to persistent variables (stack, global, or heap-allocated with malloc/calloc) so their memory stays valid for the entire time they're in the queue.


内容的提问来源于stack exchange,提问作者plonk

火山引擎 最新活动