1

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

整个代码如下:

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 版本代码发生了什么。

4

3 回答 3

3

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

循环中的代码相当于:

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变量不超出范围,您的链接列表就很好。

于 2020-03-11T06:32:23.013 回答
2

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

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

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

您可以通过在插入开头添加以下行来看到这一点:

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

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

于 2020-03-11T06:11:34.307 回答
2

C 2018 6.5.2.5 5 表示函数内的复合文字具有自动存储持续时间:

…如果复合文字出现在函数体之外,则该对象具有静态存储持续时间;否则,它具有与封闭块关联的自动存储持续时间。

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

编写代码时:

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稍后调用该例程时,这些复合文字都不存在了(没有为它们中的任何一个保留存储空间,并且很可能已连续为它们中的每一个重复使用)。

于 2020-03-11T11:49:17.307 回答