【问题标题】:How to check if free(node) works如何检查免费(节点)是否有效
【发布时间】:2025-11-27 15:00:01
【问题描述】:

这里是释放整个链表的代码

void free_list(RecordType *list)
{
   RecordType *tempNode;        /* temporary Node to hold on the value of previous node */

   while(list != NULL)          /* as long as the listnode doesn't point to null */
   {
       tempNode = list;         /* let tempNode be listNode in order to free the node */
       list = list->next;       /* let list be the next list (iteration) */
       free(tempNode);          /* free the node! */
   }
}

我认为这段代码本身工作正常(?),但我不知道如何检查。 我只应用了这个理论(例如,# of frees must = to the # of mallocs)

所以这里有一些我想知道的问题......

  1. 这种方法有效吗?
  2. 我需要 malloc tempNode 吗?
  3. 我在 while 循环之前初始化了 tempNode……但是在我释放之后,tempNode 仍然可以工作……我真的不明白那部分

我使用的理论:

  1. # of free() == # of malloc()
  2. 您需要一个临时节点来保存当前节点
  3. 让当前节点等于下一个节点
  4. 使用临时节点释放当前节点

如果我的任何理论听起来不对,请解释!

谢谢!

【问题讨论】:

  • 它的工作,假设你做了,事实上,正确地建立你的列表(看起来你可能做到了)。你的理论清单是正确的。

标签: c linked-list free


【解决方案1】:

这个方法有用吗?

是的,假设列表节点都是动态分配的并且之前没有被释放

我需要 malloc tempNode 吗?

您不需要在free_list 内分配任何内存,但所有列表元素必须事先已动态分配。您只能在使用malloc(或calloc)分配的内存上调用free

我在 while 循环之前初始化了 tempNode……但在我释放之后,tempNode 仍然有效......我真的不明白那部分

调用free 将内存所有权返回给系统。它可能会选择立即重用此内存,也可能会在一段时间内保持不变。没有什么可以阻止你再次访问内存,但是读取或写入它的结果是不确定的。

如果您想让客户端代码更难意外访问已释放的内存,您可以将 free_list 更改为 NULL 他们的指针

void free_list(RecordType **list)
{
    RecordType *tempNode;
    while(*list != NULL) {
        tempNode = *list;
        list = tempNode->next;
        free(tempNode);
    }
    *list = NULL;
}

如果您还想检查是否确实释放了所有内存,请考虑使用 valgrind。这将报告任何内存泄漏并标记某些类型的无效内存访问。

【讨论】:

  • +1,尽管我认为他关于tempNode 的问题是针对他的循环性质的。 tempNode points 显然已经正确分配(或 NULL)或者这是 UB,但在 free_list() 没有 malloc() calls 的上下文中应该是需要。只是因为我有时喜欢让我的同事scratch their heads(注意free() 电话=P)
  • 谢谢,我认为你是对的,我误解了问题的那一部分。我已经更新了我的答案。
【解决方案2】:

该方法确实有效——但它应该是mallocd 在freeing 之前。否则它是未定义的行为。

仅当 list 以前是 malloc()d 时,您才需要 malloc() tempNode

第三部分是未定义的行为。在free() 之后,数据可能仍然存在,但被标记为被覆盖。一旦它是free()d,您就不能依赖该节点

【讨论】:

  • 所以如果我正确地使用了插入优先方法来使用 malloc 创建列表,我需要 malloc tempNode?​​span>
  • @LarsChung 不,你不知道。事实上,如果你这样做然后说tempNode = list; ,那么你将在这里泄漏内存。当您输入tempNode = list; 行时,您实际上是在将tempNode 指向list 指向的任何位置(希望是malloc()d)
【解决方案3】:

检查代码的最佳方法是通过调试器进行交互式跟踪。 Linux 上的 KDevelop 中的 Gdb 或 MS Windows 上的 MS Visual Studio 调试器是完美的。我将在此演示中使用后者。

这段代码定义了一个具有三个函数的单向整数列表:ListPush() 将整数添加到列表中,ListPrint() 显示列表内容,ListDestroy() 销毁列表。在 main() 中,我将 3 个整数插入到列表中,打印它们并销毁列表。

#include <malloc.h>
#include <stdlib.h>
#include <stdio.h>
typedef struct Node NODE, *PNODE;
typedef struct Node {
    int item;
    PNODE next;
};
PNODE ListPush(PNODE head, int item) {
    PNODE p;
    PNODE n = (PNODE) malloc(sizeof(NODE));
    if ( !n ) exit(1);
    n->next = 0;
    n->item = item;

    if (!head) {
        head = n;
    }
    else {
        for ( p=head; p->next != 0; p=p->next );
        p->next = n;    
    }
    return head;
}

void ListPrint(PNODE head) {
    PNODE p;
    printf("List contents:\n\n");
    for (p=head; p!=0; p=p->next) {
        printf("%d ", p->item ); 
    }
}

void ListDestroy( PNODE head ) {
    PNODE n, c = head;
    if ( !head ) return; 
    do {
        n = c->next;
        free(c);
        c = n;
    } while (c );

}

int main() {
    int i;
    int a[3] = {1,2,3};
    PNODE head = 0;
    for ( i = 0; i<3; ++i ) {
        head = ListPush(head, a[i]);
    }
    ListPrint(head);
    ListDestroy(head);
    return 0;
}

三个附加图片说明了程序的 2 个阶段(MSVS2012 调试器)。

第一个显示 for() 循环结束后相关本地变量的状态。查看头变量并在树上继续。您可以看到三个节点及其内容:分别为整数 1,2 和 3。

第二张图片显示了在第一次调用 free() 后 ListDestroy() 中的变量。您可以看到 head 指向已释放的内存(红色圆圈),变量 c 中的指针指向在下一个循环中被销毁的下一个节点。

【讨论】: