int a = 0;
int *b = malloc (sizeof(int));
b = malloc (sizeof(int));
Run Code Online (Sandbox Code Playgroud)
上面的代码很糟糕,因为它在堆上分配内存然后不释放它,这意味着您将无法访问它.但是你也创建了'a'并且从未使用它,所以你也在堆栈上分配了内存,在范围结束之前不会释放内存.
那么为什么不在堆上释放内存,但是没有释放堆栈上的内存(直到作用域结束)是不好的做法?
注意:我知道堆栈上的内存无法释放,我想知道为什么它不被认为是坏的.
当范围结束时,堆栈内存将自动释放.除非您明确释放,否则堆上分配的内存将保持占用状态.举个例子:
void foo(void) {
int a = 0;
void *b = malloc(1000);
}
for (int i=0; i<1000; i++) {
foo();
}
Run Code Online (Sandbox Code Playgroud)
运行此代码会将可用内存减少1000*1000字节b,而所需的内存a将始终在您从foo调用返回时自动释放.
简单:因为你会泄漏内存.内存泄漏很糟糕.泄漏:糟糕,自由:好.
在调用malloc或者calloc实际上任何*alloc函数时,你声称有一块内存(其大小由传递给分配函数的参数定义).
与堆栈变量不同,堆栈变量驻留在内存的一部分中,程序具有一定程度的自由统治,相同的规则不适用于堆内存.你可能需要分配堆内存有多种原因:堆栈不够大,你需要一个指针数组,但无法知道这个数组在编译时需要多大,你需要共享一些内存(线程恶梦),一个需要在程序中的各个位置(函数)设置成员的结构...
其中一些原因,就其性质而言,暗示只要指向该内存的指针超出范围,就无法释放内存.另一个指针可能仍然在另一个范围内,指向同一块内存.
但是,正如其中一条评论中所提到的,这有一点点缺点:堆内存不仅需要对程序员部分有更多的认识,而且它也比在堆栈上工作更昂贵,更慢.
所以一些经验法则是:
无论如何,一些例子:
堆栈溢出:
#include <stdio.h>
int main()
{
int foo[2000000000];//stack overflow, array is too large!
return 0;
}
Run Code Online (Sandbox Code Playgroud)
所以,这里我们耗尽了堆栈,我们需要在堆上分配内存:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *foo= malloc(2000000000*sizeof(int));//heap is bigger
if (foo == NULL)
{
fprintf(stderr, "But not big enough\n");
}
free(foo);//free claimed memory
return 0;
}
Run Code Online (Sandbox Code Playgroud)
或者,一个数组的示例,其长度取决于用户输入:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *arr = NULL;//null pointer
int arrLen;
scanf("%d", &arrLen);
arr = malloc(arrLen * sizeof(int));
if (arr == NULL)
{
fprintf(stderr, "Not enough heap-mem for %d ints\n", arrLen);
exit ( EXIT_FAILURE);
}
//do stuff
free(arr);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
所以这样的例子不胜枚举......哪里还有一种情况malloc或者calloc是有用的:字符串数组,所有可能大小不等.相比:
char str_array[20][100];
Run Code Online (Sandbox Code Playgroud)
在这种情况下,str_array是一个包含20个char数组(或字符串)的数组,每个数组长100个字符.但是如果100个字符是你需要的最大值,平均而言,你只会使用25个字符或更少?
你是用C语言写的,因为它很快,你的程序不会使用比实际需要更多的资源?那不是你真正想做的事情.更有可能的是,你想:
char *str_array[20];
for (int i=0;i<20;++i) str_array[i] = malloc((someInt+i)*sizeof(int));
Run Code Online (Sandbox Code Playgroud)
现在,每个元素都str_array具有我需要分配的内存量.那更干净.但是,在这种情况下,呼叫free(str_array)不会削减它.另一个经验法则是:每个alloc调用都必须有一个free匹配它的调用,所以解除分配这个内存如下所示:
for (i=0;i<20;++i) free(str_array[i]);
Run Code Online (Sandbox Code Playgroud)
注意:
动态分配的内存不是内存泄漏的唯一原因.必须要说的是.如果您读取文件,使用打开文件指针fopen,但未能关闭该文件(fclose)也会导致泄漏:
int main()
{//LEAK!!
FILE *fp = fopen("some_file.txt", "w");
if (fp == NULL) exit(EXIT_FAILURE);
fwritef(fp, "%s\n", "I was written in a buggy program");
return 0;
}
Run Code Online (Sandbox Code Playgroud)
编译和运行会很好,但它会包含一个泄漏,只需添加一行就可以轻松插入(并且应该插入):
int main()
{//OK
FILE *fp = fopen("some_file.txt", "w");
if (fp == NULL) exit(EXIT_FAILURE);
fwritef(fp, "%s\n", "I was written in a bug-free(?) program");
fclose(fp);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
作为一个助手:如果范围很长,你可能会试图将太多的东西塞进一个功能中.即便如此,如果你不是:你可以在任何时候释放声称的内存,它不必是当前范围的结束:
_Bool some_long_f()
{
int *foo = malloc(2000000000*sizeof(int));
if (foo == NULL) exit(EXIT_FAILURE);
//do stuff with foo
free(foo);
//do more stuff
//and some more
//...
//and more
return true;
}
Run Code Online (Sandbox Code Playgroud)