C语言 - 正确释放结构体内存

3
我的结构体名为Table,它包含了一个名为Object的结构体数组。Object结构体包含指向另一个Object的指针。我想创建两个方法——一个用于释放Object,另一个用于释放Table,当给定这些结构体的指针(分别为ObjectPTableP)时:

这是我当前天真无知的实现,完全不起作用,因为valgrid在各个地方都在报警告(我从Java转过来,对C语言非常陌生):

/*
 * Represents a linked list containing a key value
 */
typedef struct Object {
    void *key;
    struct Object *top;
    struct Object *next;
    Boolean originalCell;
} Object;

/*
 * Represents a table that stores keys based on a given object's hash
 */
typedef struct Table{
    Object *linkedObjects;
    size_t size, originalSize;
    HashFcn hfun;
    PrintFcn pfun;
    ComparisonFcn fcomp;
    Boolean wasDuplicated;
} Table;

void FreeObject(ObjectP object)
{
    free(object);
}

void FreeTable(TableP table)
{
    free(table);
}

如何正确释放这些结构体?

编辑:

这是我分配变量的方式:

ObjectP CreateObject(void *key)
{
struct Object *object = (struct Object*) malloc(sizeof(struct Object));
...
}

TableP CreateTable(size_t tableSize, HashFcn hfun, PrintFcn pfun, ComparisonFcn fcomp)
{
    struct Table *table = malloc(sizeof(Table));

    if (table==NULL)
    {
        ReportError(MEM_OUT);
        return NULL;
    }

    table->linkedObjects = NULL;
    table->linkedObjects  = malloc(tableSize * sizeof(Object));
...
}

你看到的警告有什么例子? - prprcupofcoffee
你能展示一下如何分配它们吗?通常删除对象应该涉及到与构造时调用malloc相同数量的free调用。 - simonc
什么是 top?它是第一个元素吗? - Adam Sznajder
2
您是否想从整个表中单独删除对象?如果是这样,您将面临一个重大问题,因为对于列表中间的每个被删除的对象,都有一个指向被删除对象的指针的前置对象成为悬浮引用。您真的需要找到该对象并修复其下一个指针,使其指向正在被删除对象的下一个指针所指向的对象。如果您只是删除整个表,则生活会变得容易得多!(抱歉,双关语。) - Jonathan Leffler
你是想实现一个链表还是向量?对于链表,你不需要这个:table->linkedObjects = malloc(tableSize * sizeof(Object)); 而对于表格,你的对象中也不需要 next 指针。 - Roddy
显示剩余4条评论
4个回答

2
我不喜欢你的ObjectP和TableP符号表示法;我将使用Object*和Table*显式指针。
从评论中可以看出,您只关心删除整个表格;这很好,因为它比删除任意对象要简单得多。
我假设CreateTable()函数中的tableSize参数存储在table->size中。
static Object *FreeObject(Object *obj)
{
    Object *next = 0;
    if (obj != 0)
    {
        free(obj->key);   // If you allocated this
        next = obj->next;
        free(obj);
    }
    return(next);
}

void FreeTable(Table *tab)
{
    if (tab != 0)
    {
        for (size_t i = 0; i < tab->size; i++)
        {
            Object *next = tab->linkedObjects[i].next;
            while ((next = FreeObject(next)) != 0)
                ;
            free(tab->linkedObjects[i].key);  // If you allocated this
        }
        free(tab->linkedObjects);
        free(tab);
    }
}
FreeObject()函数是静态的,因为它只用于FreeTable()函数。它删除键,并假定在分配对象时已分配了该键;它捕获列表中下一个项目的指针(可能是空指针),释放对象本身,并返回指向下一个项目的指针。 FreeTable()函数使用FreeObject()函数释放由数组linkedObjects分配的对象中悬挂的所有链接对象列表中的所有对象,然后删除对象数组,最后删除表格。
释放内存的关键是确保每个分配都有一个空闲,并且每个分配只有一个空闲。传递给free()的值必须是从malloc()calloc()realloc()中获得的值。请记住,realloc()可以更改以前分配的地址,但必须释放realloc()的值而不是malloc()calloc()中的原始分配。

2
为了删除整个表格(包括对象),您需要遍历对象图并针对每个通过malloc分配的块调用free(),即一个Object或一个这些对象的数组。

我理解您的对象图看起来像这样:

Graph

然后删除的方法是:

void FreeObjectChain(Object* obj) {
    Object* curr = obj->next;
    while (curr) {
        Object* tmp = curr;
        curr = tmp->next;
        free(tmp);
    }
}

并且

void FreeTable(Table* table) {
    for (int i=0; i<table->size; ++i) {
        FreeObjectChain( &(table->linkedObjects[i]) );
        // or simply FreeObjectChain( table->linkedObjects + i );
    }
    free(table->linkedObjects);
    free(table);
}

你很接近了,但我认为你在使用FreeObjectChain()的方式上存在问题。问题在于你会尝试单独释放table->linkedObjects数组中的每个元素(这不是一个好主意)。我认为你需要将table->linkedObjects[i].next传递给FreeObjectChain()以避免出现问题。 - Jonathan Leffler
我不确定我是否理解正确,但请注意我的“FreeObjectChain”从“obj->next”开始,这也应该避免您提到的问题。(虽然您建议的方式可能看起来更自然) - Kos
好的 - 是的,你是对的。那解决了问题。你没问题;我的评论不相关/不准确。 - Jonathan Leffler

1
我会尝试做类似这样的事情:
void freeObject(Object* obj) {
    if (obj->next != NULL) {
        freeObject(obj->next);
    }
    if (obj->key != NULL) {
        free(obj->key);
    }   
    free(obj);
}

void freeTable(Table* table) {
     for (int i=0; i<table->size; ++i) {
         freeObject(&(table->linkedObjects[i]));
     }
     freeObject(table->linkedObjects);
     free(table);
}

我通常不喜欢递归函数,但在这里它非常合理...假设没有循环...但如果循环是可能的,你也必须将obj->next清空。 - Grady Player

0

FreeTable() 必须遍历数组并调用 free() 函数释放所有对象。

在尝试处理内存管理的任何代码之前,您应该首先了解/学习计算机在低级范围内的工作原理。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接