将复合文字传递给函数是否有效?

Is it valid to pass a compound literal into a function?

我有一个结构类型作为参数,需要将它传递给函数。

完整代码如下:

void insert(struct node *newt) {
    struct node *node = head, *prev = NULL;
    while (node != NULL && node->data < newt->data) {
        prev = node;
        node = node->next;
    }
    newt->next = node;
    if (prev == NULL)
        head = newt;
    else
        prev->next = newt;
}
void print(){
    struct  node *tmp = head;
    while(tmp){
        printf("%d->",tmp->data);
        tmp = tmp->next;
    }
    printf("\n");
}
int main(){
    /*
    * Experiment for naive-insert
    */
    for(int i = 1; i < 4; i++){
        insert(&(struct node){.data = i, .next = NULL});
    }
    //insert(&(struct node){.data = 1, .next = NULL});
    //insert(&(struct node){.data = 2, .next = NULL});
    //insert(&(struct node){.data = 3, .next = NULL});
    print();

}

如果我在for循环中调用insert,它会打印.....->3->3->3....(停不下来)

但是如果我只是用

替换 for 循环
insert(&(struct node){.data = 1, .next = NULL});
insert(&(struct node){.data = 2, .next = NULL});
insert(&(struct node){.data = 3, .next = NULL});

它将正常运行。我想知道我的 for-loop 版本代码是怎么回事。

您正在尝试使用临时值初始化链表中的新节点 - 这不会顺利结束!!

for(int i = 0; i < 3; i++){
        insert(&(struct node){.data = i, .next = NULL});
    }

这里明显发生的是编译器回收了堆栈上的相同内存以生成临时变量。实际上,前一个节点与下一个节点相同,因此您最终会得到指向自身的同一节点(其最后一个值为 2)。

您可以通过在插入的开头添加以下行来查看:

    printf("%p\n", newt);

你真的应该使用 malloc 而不是这样做。

[编辑:在初稿中,我对使用复合文字创建的存储的性质有错误的理解。 Jonathan Leffler 的评论和 Eric Postpischil 的回答是正确的,并证明我最初的观点是错误的。我建议的创建链接列表的替代方法是有效的,不再需要。我修改了我的答案。]]

循环中的代码等同于:

for (int i = 0; i < 3; i++) {
    struct node tmp = {.data = i, .next = NULL};

    insert(&tmp);
}

编译器创建一个临时对象并将其地址传递给您的列表函数。该临时对象仅在循环体内有效,插入后直接超出范围。在打印列表时通过列表中的指针访问循环后的无效对象是未定义的行为。)

没有循环的代码相当于:

struct node tmp1 = {.data = 1, .next = NULL};
struct node tmp2 = {.data = 2, .next = NULL};
struct node tmp3 = {.data = 3, .next = NULL};

insert(&tmp1);
insert(&tmp2);
insert(&tmp3);

只要tmpX变量不超出范围,你的链表就很好。

C 2018 6.5.2.5 5 表示函数内的复合文字具有自动存储期限:

… If the compound literal occurs outside the body of a function, the object has static storage duration; otherwise, it has automatic storage duration associated with the enclosing block.

这意味着从程序执行到达它的声明时到封闭块的执行结束,存储是为复合文字保留的。

代码编写时:

int main(void)
{
   …
    insert(&(struct node){.data = 1, .next = NULL});
    insert(&(struct node){.data = 2, .next = NULL});
    insert(&(struct node){.data = 3, .next = NULL});
    print();
}

则封闭块是构成main主体的{ … },复合文字一直存在到main执行结束。所以当 print 正在执行时,它们仍然存在(意味着为它们保留存储空间)。

在此代码中:

for(int i = 1; i < 4; i++){
        insert(&(struct node){.data = i, .next = NULL});
    }

封闭块是 { … },它是 for 循环的主体。每次 for 循环迭代时,该块的执行都会结束。因此,当稍后调用 print 例程时,这些复合文字中的 none 不再存在(存储不为它们中的任何一个保留,并且它可能已被连续地重新用于它们中的每一个)。