繁体   English   中英

在 C 中释放 malloc()、realloc() 分配的所有内存

[英]Free all the memory allocated by malloc(), realloc() in C

我正在尝试通过 malloc()、realloc() 释放所有分配的内存,但 valgrind 说这是内存泄漏。

编码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int main(int argc, char *argv[]) {

    int lines_allocated = 128;
    int max_line_len = 50;

    FILE *fp;

    /* File allocate lines of text */
    char **array = (char **)malloc(sizeof(char*)*lines_allocated);
    if (array==NULL) {
        fprintf(stderr,"Out of memory (1).\n");
        exit(1);
    }

    FILE *file = fopen("file.txt", "r");
    if (file == NULL) {
        fprintf(stderr,"Error opening file.\n");
        exit(2);
    }

    int il;
    for (il = 0; 1; il++) {
        int j;

        /* Have we gone over our line allocation? */
        if (il >= lines_allocated) {
            int new_size;

            /* Double our allocation and re-allocate */
            new_size = lines_allocated*2;
            array = (char **)realloc(array,sizeof(char*)*new_size);

            if (array==NULL) {
                fprintf(stderr,"Out of memory.\n");
                exit(3);
            }

            lines_allocated = new_size;
        }

        /* Allocate space for the next line */
        array[il] = malloc(max_line_len);
        if (array[il]==NULL) {
                fprintf(stderr,"Out of memory (3).\n");
                exit(4);
            }
        if (fgets(array[il], max_line_len-1, file)==NULL)
            break;

        /* Get rid of CR or LF at end of line */
        for (j=strlen(array[il])-1;j>=0 && (array[il][j]=='\n' || array[il][j]=='\r');j--)
            ;

        array[il][j+1]='\0';
    }

    /* Close file */
    fclose(file);

    /* Print and free the every element of the array */
    int cc;
    for (cc = 0; cc < il; cc++) {
        printf("%s\n", array[cc]);
        
        /* Free the every element of the array */
        free(array[cc]);
    }

    /* Free hole array */
    free(array);

    return 0;
}

valgrind ./main

valgrind --leak-check=full --show-reachable=yes ./main
==4806== Memcheck, a memory error detector
==4806== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==4806== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==4806== Command: ./main
==4806== 
1
2
3
4
5
6
7
8
9
10
11
==4806== 
==4806== HEAP SUMMARY:
==4806==     in use at exit: 50 bytes in 1 blocks
==4806==   total heap usage: 14 allocs, 13 frees, 2,192 bytes allocated
==4806== 
==4806== 50 bytes in 1 blocks are definitely lost in loss record 1 of 1
==4806==    at 0x4C2AC3D: malloc (vg_replace_malloc.c:299)
==4806==    by 0x40092E: main (in /var/www/mem/main)
==4806== 
==4806== LEAK SUMMARY:
==4806==    definitely lost: 50 bytes in 1 blocks
==4806==    indirectly lost: 0 bytes in 0 blocks
==4806==      possibly lost: 0 bytes in 0 blocks
==4806==    still reachable: 0 bytes in 0 blocks
==4806==         suppressed: 0 bytes in 0 blocks
==4806== 
==4806== For counts of detected and suppressed errors, rerun with: -v
==4806== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

如何正确释放内存? 它说应该再释放一块内存,但它在哪里?

for (cc = 0; cc < il; cc++) {

如果ilarray的有效索引(并且确实如此),则循环中的比较应该是:

for (cc = 0; cc <= il; cc++) {

为了触发array的最后一个元素(并释放它的内存)。

只需更换

for (cc = 0; cc < il; cc++)

for (cc = 0; cc <= il; cc++)

为了弄清楚这一点,想象一下如果分配循环for (il = 0; 1; il++)只迭代一次会发生什么。 在这种情况下,控制不会达到il++ ,因此il保持为零并且for (cc = 0; cc < il; cc++)迭代零次。 在一般情况下,释放循环比分配循环少一次迭代。

您的代码泄漏了最后一次分配,因为当fgets(array[il], max_line_len-1, file)返回NULLil永远不会增加。

移动array[il] = malloc(max_line_len); 以及对fgets之后的NULL检查将解决此问题。 这种方法的另一个好处是您可以进行精确大小的分配,而不是在max_line_len进行分配。

// Check that we will need the allocation
char temp[max_line_len];
if (fgets(temp, max_line_len-1, file)==NULL) {
    break;
}
// Allocate only when we are sure that we are going to need it
temp[max_line_len-1] = '\0';
size_t len = strlen(temp);
array[il] = malloc(len+1);
if (array[il]==NULL) {
    fprintf(stderr,"Out of memory (3).\n");
    exit(4);
}

注意:realloc分配回正在重新分配的变量可能会导致泄漏先前分配给该变量的内存。 这在您的代码中不是问题,因为您立即调用了exit(4) ,但您应该注意此分配的一般问题。

如果您遇到其他问题,并且混合调用各种分配器,并且对事情非常挑剔,那么为各种内存分配器实现一个包装器(这可以使用宏巧妙地完成),将新分配的缓冲区的地址缓存在某处(例如,在堆栈顶部)然后 - 在某个点 - 导航堆栈并释放每个人。 不要在这里和那里混合对free()或其类似物的随机调用。 当某物被释放时,用零覆盖某物,这样你就不会意外地再次尝试free()它,这会扰乱free()

如何巧妙地使用宏(我说过了,所以我现在最好让它工作)并避免递归问题:

让我们使用malloc()作为我们的第一个受害者。

在另一个源文件中,创建一个调用malloc()的函数_malloc() )。

在包含所有内存分配和释放的源文件中,定义malloc()如下:

#define malloc( n )  ( *sp++ = _malloc( n ) )

必须有代码作为序言调用,以设置堆栈并在其底部指向sp 让它变得又大又好:你会惊讶地发现malloc()和它的兄弟最终会被调用多少次。 在不同的时候——实际上是在合适的时候——调用你自己的free_all()来做这个:

void free_all() {  
  while( --sp >= base_of_malloc_buf_stack ) {  
    free( *sp );   
    *sp = 0; /* avoid future re-free() */    
  }  
}

我会在没有宏的情况下选择布鲁斯的解决方案。 我只是直接使用 _malloc 包装器。 使用宏可能不利于理解代码。

Bruce 的问题:重叠宏名称和实际功能不会与使用 malloc 的其他组件混淆吗?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM