C 销毁链表,为什么会这样?

C 销毁链表,为什么会这样?,c,linked-list,valgrind,C,Linked List,Valgrind,我创建了这个函数来销毁链表,head是链表的head void destroy(node** head){ node* current = NULL; while(*head != NULL){ current = *head; free(current); *head = (*head)->next; } } 我认为这段代码不起作用,因为head和current指向内存中的同一个地址,并且由于它被释放,您无法访

我创建了这个函数来销毁链表,head是链表的head

void destroy(node** head){

    node* current = NULL;
    while(*head != NULL){
        current = *head;
        free(current);
        *head = (*head)->next;
    }

}
我认为这段代码不起作用,因为head和current指向内存中的同一个地址,并且由于它被释放,您无法访问*head->next。但当我运行程序时,它运行良好,当我检查列表为空时

我甚至在程序上运行了valgrind测试,没有内存泄漏,尽管几乎没有隐藏的错误


为什么这个代码能工作?它似乎并不直观。

它之所以有效,是因为在这种特殊情况下,释放的内存没有被修改,即使它是空闲的,并且您不再拥有它

不要这样做。在另一个编译器或平台上,可能会对其进行修改,或者可能会发生非法内存访问错误,或者可能会更改程序另一部分使用的内存。这使得这是一个糟糕的方法。只使用程序拥有的内存

我认为这段代码不起作用,因为*head和current指向内存中的同一个地址,并且由于它被释放,您无法访问*head->next

这是完全正确的:一旦您释放当前,对*head->next的访问是未定义的行为

不幸的是,未定义的行为并不等同于崩溃,因此人们可能会觉得程序正在工作;不是

我甚至在程序上运行了valgrind测试,没有内存泄漏,尽管几乎没有隐藏的错误

这不是内存泄漏,而是对已释放内存的访问。它确实会导致一条有点隐晦的错误消息——大致如下:

地址0x5a02048是大小为16 free'd的块中的4个字节

这意味着sizeofnode是16,next的偏移量是4

显然,释放列表的正确方法是交换访问next的行并调用freecurrent

请注意,电流可以在循环内声明。

当您访问刚刚释放的内存时,代码具有未定义的行为

未定义行为的真正坏处在于,即使代码具有未定义的行为,它也可能看起来有效

将代码更改为:

void destroy(node** head){   
    node* current = NULL;
    while(*head != NULL){
        current = *head;
        *head = (*head)->next;
        free(current);
    }
}

因此,您可以在拨打免费电话之前更改*head

以下是我的解决方案:

void destroy(node** head){   
                node* current = *head;
                while((*head) != NULL){
                        if((*head)->next!=NULL)
                                *head = (*head)->next;
                        else { /* if next node is NULL, free it and comes out of loop or function  **/
                                (*head) = NULL;
                                free(current);
                                current = NULL;/* to avoid Dangling pointer problem **/
                                break;//or return bcz there will not be further node to delete **/
                        }
                        /** below 2 statements will execute only if part is true **/
                        free(current);
                        current = *head;
                }
        }

释放后使用是未定义的行为,这意味着它可以做任何事情。它可能在某些实现中起作用,但在其他实现中可能会导致错误。有什么更好的替代方法?您可以循环浏览列表,获取下一项,并在获得下一项之前不释放当前项。将名称从current更改为Visum可以使逻辑非常清晰:
void destroy(node** head){   
                node* current = *head;
                while((*head) != NULL){
                        if((*head)->next!=NULL)
                                *head = (*head)->next;
                        else { /* if next node is NULL, free it and comes out of loop or function  **/
                                (*head) = NULL;
                                free(current);
                                current = NULL;/* to avoid Dangling pointer problem **/
                                break;//or return bcz there will not be further node to delete **/
                        }
                        /** below 2 statements will execute only if part is true **/
                        free(current);
                        current = *head;
                }
        }