在C语言中,何时应该使用free()函数?

10

这段代码按照预期工作,但它从来没有释放malloc()分配的内存。

我尝试在任何可以的地方释放内存,但无论我在哪里这样做,程序都会崩溃。具体地说,我会得到一个“double free or corruption”错误。这更多地是关于free()malloc()实际上是做什么的问题?所有与释放有关的问题都在主要部分中:

int main(int argc,  char *argv[]){
if(argc!=2){
    exit(1);
}

printf("CSA WC version 1.0\n\n");

int length = strlen(argv[argc-1]);
char file_to_open[length];
strcpy(file_to_open, argv[argc-1]);

//printf("filename:%s\n",file_to_open);

//create counters for output
int count_number_of_lines = 0;
int count_number_of_words = 0;
int count_number_of_characters = 0;

//create int size of default array size
int current_array_size = pre_read(file_to_open);
//printf("number of lines: %i\n",current_array_size);

//create string array of default size
char *strings_array[current_array_size];

//create a pointer to catch incoming strings
char *incoming_string=NULL;

int done=0;
while(done==0){
    incoming_string=get_line_from_file(file_to_open, count_number_of_lines);
    if(incoming_string!=NULL){
        incoming_string=csestrcpy2(incoming_string);
        //printf("incoming line: %s\n",incoming_string);
        strings_array[count_number_of_lines]=(char*)malloc(strlen(incoming_string+1));
        strings_array[count_number_of_lines]=csestrcpy2(incoming_string);
        //printf("added to array:%s\n",strings_array[count_number_of_lines]);
        count_number_of_lines++;
        count_number_of_characters=(count_number_of_characters+(strlen(incoming_string)-1));
    }
    else{
        done=1;
    }

}
//all data is stored in a properly sized array


//count all words in array
int count=0;
int word_count=0;
char *readline;

while(count<current_array_size){
    readline = csestrcpy2(strings_array[count]);
    printf("line being checked: %s", readline);

    int i=0;
    int j=1;

    while( j< strlen(readline)+1 ){
        if(strcmp(readline,"\n")!=0){
            if( (readline[i] == ' ') && (readline[j] != ' ') ){
                word_count++;
            }
            if( (readline[i] != ' ') && (readline[j] == '\n') ){
                word_count++;
            }
        }
        i++;
        j++;
    }
    count++;
}
printf("current word count: %i", word_count);
return 0;
}



char* csestrcpy2(char* src){

int i = 0;
char *dest;
char t;
dest = (char*) malloc(MAX_LINE);

while( src[i] != '\0'){

    dest[i] = src[i];
    i++;

}

dest[i] = '\0';
//printf("length:%i\n",i);
free(dest);

return dest;
}

1
没有必要将file_to_openargv中复制出来,你可以直接使用argv - user229044
对于 incoming_string=csestrcpy2(incoming_string); 您是否感到好奇?这只是一个字符串复制函数吗? - Coeffect
2
另外,malloc(strlen(str+1)); 几乎肯定是错误的。你可能想要写成 malloc(strlen(str)+1);。(包括我在内的许多人建议省略强制类型转换 - void * 指针将被隐式转换,如果你明确地将其转换为错误的类型,可能会导致潜在问题。) - Chris Lutz
请发布您的csestrcopy2函数的完整代码,因为根据您在评论中发布的内容,这是您问题的一部分。 - Coeffect
我最近编辑了代码以反映我对其进行的更改!代码中malloc()的次数已减少到一次... 我想感谢所有帮助过我的人! - Koffeeaddict4eva
显示剩余4条评论
7个回答

19

通常情况下,您只需要释放为您动态保留的内存。这意味着如果您有以下语句:

int *my_int_pointer;
my_int_pointer = malloc(sizeof(int));

如果您不确定在哪里释放已由malloc分配(保留)的内存,则需要释放它。最简单的方法是在程序结束时使用free函数进行释放。

free(my_int_pointer);

在您的文件中,每当读取文件中出现新行时(在while(done==0)循环中),似乎会分配内存。因此,在此循环中的每个if之后,您都需要释放变量使用的内存。

此外,还需要释放为readline变量分配的内存。但是正如之前指出的那样,您可能存在内存泄漏。

希望这可以帮助您。

编辑:好吧-我已经对csestrcpy函数产生了疑问。让我们看看这个函数:

char* csestrcpy2(char* src){
    int i = 0;
    char *dest;
    char t;
    dest = (char*) malloc(MAX_LINE); /*<<- This allocates memory that has to be freed*/
    while( src[i] != '\0'){
        dest[i] = src[i];
        i++;
    }
    dest[i] = '\0';
    //printf("length:%i\n",i);
    free(dest);                  /* This frees the memory, but you return a pointer */
    return dest;                 /* to this memory. this is invalid.                */
}

然而,你可以在该函数中释放src指针。但是请记住:在底层内存被释放后,指针不能再保留信息!它只是指向一个不应再写入或读取的内存位置。

此外,该函数会复制字符串,只要没有'\0'。如果没有终止符会发生什么?该函数将继续从一些内存地址复制,这些地址本不应该被访问!

你不应该使用那个函数 ;)


尝试释放你所说的内存,但出现了以下错误:free(): invalid pointer: 0xb76f5000,也许这是无法修复的。 - Koffeeaddict4eva
你说的读取文件的部分是正确的...我需要读取一个文件,然后将每一行读入并存储为一个适当大小的以空字符结尾的字符串,存储在一个字符串数组中...这个方法实际上很好用...如果我想要稍后再次读取该数组,我可以释放为该数组分配的内存吗?如果我释放了它,我还能读取该数组吗?在我的程序死亡之前,我可以使用多少内存来进行malloc()操作? - Koffeeaddict4eva
那不是 free 的语法。 - Oliver Charlesworth
哎呀,最近我刚刚在使用C++时混淆了“free(ptr);”和“delete ptr;”。 - Florian
如果我对未使用malloc()创建的变量调用free()会发生什么,例如,如果我收到一个指向结构体的指针并在其上调用free(),那么我的程序会崩溃还是正确的? - Redauser

8
每次成功调用malloc()都需要调用free()。
这并不一定意味着您需要在代码中有相等数量的malloc()和free()调用;它意味着每当程序运行时执行malloc()调用时,您应该调用free(),并将从malloc()获取的指针值传递给它。malloc()分配内存,free()告诉系统你已经完成了分配的内存。
(当程序终止时,您几乎肯定可以不释放分配的内存,因为它将被操作系统恢复,但出于风格和良好的实践考虑,您仍然应该将malloc()与free()匹配。)
我忽略了calloc()和realloc()调用。

您能否描述一下使用free()的绝对必要情况?既然操作系统会处理它,为什么有人会使用它(除了具有“良好的编程实践”之外)? - Coder
3
考虑一个无限循环的程序,每次迭代都会分配内存。如果它不释放已分配的内存,它很快就会耗尽内存。 - Keith Thompson

3
动态内存分配(malloc)分配请求大小的内存块,并返回指向该块开头的指针。由于我们从内存中获取了此块,因此在完成任务后将其返回到内存是一个好习惯。
现在回答你的问题,为了始终保持安全,您可以在返回之前调用free函数。
main{
    int *i;
    ..
    ..
    i=(int *)malloc(sizeof(int));
    ...//so something with i
    ..
    free(i);
    return 0;
}

1

mallocfree看作是“开始”和“结束”。每次调用malloc时,做你需要做的事情,完成后始终调用free。确保只释放一次,双重释放会导致运行时错误。

如果你不小心丢失了malloc返回的值(是的,这就是你的代码出现的问题),那么你就会有一个内存泄漏(地狱之门打开了,等等)。

再次强调:释放malloc返回的任何内容(除了null)。


我该如何消除内存泄漏?或者至少找出它在哪里? - Koffeeaddict4eva
你无法“杀死”一个内存泄漏,它会在程序终止时发生,而没有释放其分配的内存。大多数操作系统会自行跟踪已分配和释放的内存,但并不保证其正常运行。您需要重新启动操作系统才能恢复内存。您可以使用启用了调试支持的编译器(如gcc中的-g),然后使用gdb或valgrind等调试工具进行编译。valgrind甚至有一个名为memcheck的工具,它显示了已分配内存的列表(以及位置)。 - Nalin Kanwar
1
@Nalin:抱歉,但那是错误的。操作系统确实会跟踪为特定程序分配的内存。但是当程序退出时,操作系统可以并且确实会收回所有这些内存。我记得没有最后一个不这样做的操作系统是Windows 98(这就是为什么它需要经常重新启动)。 - Chris Eberle
我以为那只是“删除”。有趣。 - Chris Eberle
@Chris:谢谢你提供的信息。这就是我所说的现代操作系统。但是大多数嵌入式系统并不保证这一点。这就是为什么我提到它的原因。无论如何,在程序中留下内存泄漏都是不好的编程实践。 :) - Nalin Kanwar
显示剩余2条评论

0
        i++;
        j++;
        /// free here
        free(readline);
    }
    count++;

}
printf("current word count: %i", word_count);

//free here as well
for(int k = 0; k < count_number_of_lines; k++)
{
    free(strings_array[count_number_of_lines]);
}

return 0;
}

这应该可以工作。

一般来说,任何使用calloc/malloc/realloc动态分配的内存,在指针超出范围之前都需要使用free()释放。

如果您使用'new'分配内存,则需要使用'delete'释放它。


我尝试了两种方法,它们都给了我“double free or corruption (top): 0x088113c8”的错误。 - Koffeeaddict4eva
已编辑帖子。很抱歉,free应该在迭代器后面、}之前,而不是之后。 另外, strings_array[count_number_of_lines]=(char*)malloc(strlen(incoming_string+1)); 应该是 strings_array[count_number_of_lines]=(char*)malloc(strlen(incoming_string)+1)); 然后尝试像上面展示的那样释放它 - 应该可以工作。 - Jan S
你已经写了 free(dest),然后又返回了它!这不会返回一个字符串。 你不需要在那里释放内存。如果你在最后移除它并释放,那么它应该能正常工作。 - Jan S

0

这一行:

strings_array[count_number_of_lines]=(char*)malloc(strlen(incoming_string+1));

正在被其旁边的行覆盖,因此可以将其删除。

在最后一个循环中的count++之后,您还应该添加free(readline)以释放malloc创建的内存。


我无法删除那一行,否则字符串数组将永远不会填满。因此,当我尝试稍后访问字符串数组时,即使只是打印它,它也会给我一个没有任何内容的数组? - Koffeeaddict4eva
我也尝试添加了那个免费的东西,结果出现了以下错误: 双重释放或损坏(顶部):0x088113c8,程序崩溃了... - Koffeeaddict4eva

0
通常情况下,每个 malloc 都应该有一个相应的 free。但是你不能对同一块内存进行两次 free(你现在已经注意到了这一点)。我在你的代码中没有看到任何对 free 的调用,因此无法确定你的问题出在哪里。但我立刻注意到,在循环内部你使用 malloc 分配了一些内存并将其赋值给了 readline,然而在循环结束时你没有调用 free 来释放 readline,所以你在那里存在内存泄漏的问题。

如果我在循环结束时释放它,程序就会崩溃... 如果我在循环后释放它,程序也会崩溃... 如果我在程序结束时释放它,程序还是会崩溃... 似乎无论在程序的任何位置进行释放,都会出现这种情况... - Koffeeaddict4eva
@Koffee: 当然不是这样的。我指的是在每个迭代结束时。迭代次数是未知的,所以你应该自己清理。如果你正在读一个巨大的文件呢?不要设计你的代码只适用于最琐碎的情况,你应该养成编写正确代码的习惯。 - Ed S.

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