【问题标题】:c - properly free memory of a structc - 正确释放结构的内存
【发布时间】:2023-03-10 10:38:01
【问题描述】:

我的名为Table 的结构包含一个名为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));
...
}

【问题讨论】:

  • 您看到的警告示例是什么?
  • 能否请您说明如何分配它们?通常,删除对象应该涉及对free 的调用次数与构造对malloc 的调用次数一样多
  • 什么是top?它是第一个元素吗?
  • 您是否尝试从整个表中单独删除对象?如果是这样,您将遇到一个主要问题,因为对于被删除列表中间的每个对象,都有一个先前的对象具有指向被删除对象的指针,该指针成为一个悬空引用。您确实需要找到该对象并将其下一个指针修复为指向被删除对象的下一个指针指向的对象。如果您只删除整个表,那么生活会轻松很多! (对不起双关语。)
  • @JonathanLeffler 只删除整个表 :)

标签: c memory


【解决方案1】:

我不喜欢你的 ObjectPTableP 符号;我将使用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 或其中一个数组。

    我了解您的对象图如下所示:

    那么删除的方法是:

    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-&gt;linkedObjects 中数组的每个元素(这不是一个好主意)。我认为您需要将table-&gt;linkedObjects[i].next 传递给FreeObjectChain() 以避免该问题。
    • 我不确定我是否正确,但请注意我的FreeObjectChainobj-&gt;next 开始,这也应该避免您提到的问题。 (不过,您建议的方式可能看起来更自然)
    • 好的——是的;你说得对。这解决了问题。你没事;我的评论不相关/不准确。
    【解决方案3】:

    我会尝试做这样的事情:

    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);
    }
    

    【讨论】:

    • 我通常不喜欢递归函数,但在这里它很有意义......假设没有循环......但如果循环是可能的,你也必须 nil out obj->next跨度>
    【解决方案4】:

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

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

    【讨论】:

      猜你喜欢
      • 2022-01-13
      • 1970-01-01
      • 2012-07-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多