【问题标题】:C memory leak despite free尽管免费,但 C 内存泄漏
【发布时间】:2011-06-23 11:37:37
【问题描述】:

在使用 Valgrind 调试我的程序时,我发现了内存泄漏,尽管我认为是对 free 的有效调用。首先,分配内存并存储它的代码:

    row = malloc(sizeof(Row));
    row->columns = malloc(sizeof(char*) * headcnt);
    row->numcol  = 0;

    ...

    row->numcol    = colcnt;
    rows           = realloc(rows, (rowcnt+1) * sizeof(Row));
    rows[rowcnt++] = *row;

负责尝试释放内存的代码:

void cleanUp(){
    int i = 0;
    int j = 0;

    for (i = 0; i < rowcnt; i++){
        for (j = 0; j < rows[i].numcols; j++){
            free(rows[i].columns[j]);
        }
        free(&rows[i]);
    }
    free(rows); 
    exit(0);
}

Row 的声明:

typedef struct {
    char** columns;
    unsigned short int numcol;
} Row;

Row* rows = NULL;

更糟糕的是,这个程序有时会在free(&amp;rows[i]) 处导致一个 glibc 错误,抱怨双重释放。我是 C 新手,希望有人能提供任何指点(咳咳)。

【问题讨论】:

  • 需要包含Row类型的声明。
  • 可以在这里使用更多代码。目前我有点困惑,为什么你 malloc 为一行中的每一列留出空格,然后将 row->numcol 设置为零。
  • free() 知道malloc() 分配的内存块大小,并将释放整个块。看起来您正在遍历该块并尝试释放部分内存块。
  • @ustun:我也喜欢 clang,但这对这里有什么帮助?
  • @Lucas 我不确定,但 clang 静态分析器(不是编译器)有时有助于解决内存泄漏问题。

标签: c memory pointers valgrind


【解决方案1】:

执行rows[rowcnt++] = *row; 会有效地复制您分配的内存。您的数组行应该是一个指针数组。同样就像 Oli Chalesworth 指出的那样,您对列的免费应该是对所有列的一次免费。

rows = malloc(count * sizeof(Row*)); // This is probably done somewhere

row->columns = malloc(sizeof(char*) * headcnt);
row->numcol  = 0;

...

row->numcol    = colcnt;
rows           = realloc(rows, (rowcnt+1) * sizeof(Row*));
rows[rowcnt++] = row;

现在如果你的清理工作

void cleanUp(){
    int i = 0;
    int j = 0;

    for (i = 0; i < rowcnt; i++){
        free(rows[i]->columns);
    }
    free(rows); 
    exit(0);
}

【讨论】:

    【解决方案2】:

    malloc(或realloc)的每次调用都必须与对free 的对应调用相匹配。如果你这样动态分配一个数组:

    int *p = malloc(sizeof(int) * NUM);
    

    你可以这样释放它:

    free(p);
    

    不是这样的:

    for (int i = 0; i < NUM; i++)
    {
        free(p[i]);
    }
    

    您的操作似乎不正确。我怀疑你的清理代码应该是:

    void cleanUp(){
        int i = 0;
        int j = 0;
    
        for (i = 0; i < rowcnt; i++){
            for (j = 0; j < rows[i].numcols; j++){
                free(rows[i].columns[j]); // Free whatever rows[i].columns[j] points to
            }
            free(rows[i].columns); // Matches row->columns = malloc(sizeof(char*) * headcnt);
        }
        free(rows);  // Matches rows = realloc(rows, (rowcnt+1) * sizeof(Row));
        exit(0);
    }
    

    另外,没有办法匹配row = malloc(sizeof(Row));。我怀疑您的分配代码应该是:

    row->numcol    = colcnt;
    rows           = realloc(rows, (rowcnt+1) * sizeof(Row));
    rows[rowcnt].columns = malloc(sizeof(char*) * headcnt);
    rows[rowcnt].numcol = 0;
    rowcnt++;
    

    【讨论】:

    • 谢谢,您的回答消除了我对释放动态分配的数组的误解。
    【解决方案3】:

    也许我很密集,但这不是完全没有必要吗?无论如何,一旦程序退出,您的所有内存都会被释放。

    【讨论】:

    • 这是真的,但我之前提出过这一点,已经被资深的 C 程序员给瞪大了眼睛。
    • 这是公平的,因为如果你不整理这些内存,那么当它们发生时你应该如何发现真正的内存泄漏?这就像您忽略了来自编译器的 50 个警告,因为您知道它们“没问题”。当警告 51 出现时,它就会消失在噪音中。
    • 为了明确地结束讨论,在这个问题的上下文中,释放与否的问题根本不重要。 OP专门询问如何正确释放已用空间,而不是他是否应该释放它。 :-)
    • “如果它是更大的应用程序的一部分并不重要 - exit() 语义不会因此而改变......” - 没有抓住重点;在实际应用中,exit 可能会在很久以后调用。 “手动调用 free() 会不必要地占用资源”——你到底在说什么?
    • “也许发布的操作只是更大应用程序的一个子集?” ……“也许我是个外星人。” -- OP 发布的内容实际上可能只是更大应用程序的一个子集,而你不可能是外星人……但如果你是外星人,那你就是一个粗鲁的人。
    猜你喜欢
    • 1970-01-01
    • 2017-07-17
    • 1970-01-01
    • 1970-01-01
    • 2016-02-13
    • 1970-01-01
    • 2021-07-20
    • 2012-01-25
    • 2012-10-15
    相关资源
    最近更新 更多