释放记忆
可以通过调用 free()
来释放动态分配的内存。
int *p = malloc(10 * sizeof *p); /* allocation of memory */
if (p == NULL)
{
perror("malloc failed");
return -1;
}
free(p); /* release of memory */
/* note that after free(p), even using the *value* of the pointer p
has undefined behavior, until a new value is stored into it. */
/* reusing/re-purposing the pointer itself */
int i = 42;
p = &i; /* This is valid, has defined behaviour */
p
指向的内存在调用 free()
之后被回收(通过 libc 实现或底层操作系统),因此通过 p
访问该释放的内存块将导致未定义的行为 。引用已释放的存储器元素的指针通常称为悬空指针 ,并存在安全风险。此外,C 标准规定即使访问悬空指针的值也有未定义的行为。请注意,指针 p
本身可以重新使用,如上所示。
请注意,你只能在 malloc()
,calloc()
,realloc()
和 aligned_alloc()
函数中直接返回的指针上调用 free()
,或者文档告诉你内存已经分配的方式(像 strdup ()
这样的函数是值得注意的例子)。释放一个指针,
- 通过在变量上使用
&
运算符获得的,或者 - 在分配的块的中间,
禁止。这样的错误通常不会被编译器诊断出来,但会导致程序在未定义的状态下执行。
有两种常见的策略可以防止这种未定义行为的实例。
第一个也是最好的很简单 - 当不再需要时,p
本身不再存在,例如:
if (something_is_needed())
{
int *p = malloc(10 * sizeof *p);
if (p == NULL)
{
perror("malloc failed");
return -1;
}
/* do whatever is needed with p */
free(p);
}
通过在包含块(即 }
)结束之前直接调用 free()
,p
本身不再存在。编译器将在任何尝试使用 p
之后给出编译错误。
第二种方法是在释放指向它的内存后使指针本身无效:
free(p);
p = NULL; // you may also use 0 instead of NULL
这种方法的论据:
-
在许多平台上,尝试取消引用空指针将导致即时崩溃:分段错误。在这里,我们至少得到一个指向被释放后使用的变量的堆栈跟踪。
如果没有设置指向
NULL
的指针,我们就有悬空指针。该程序很可能仍然会崩溃,但后来,因为指针指向的内存将无声地被破坏。这些错误很难追踪,因为它们可能导致与初始问题完全无关的调用堆栈。因此,这种方法遵循快速失败的概念 。
-
释放空指针是安全的。在 C 标准规定的是
free(NULL)
没有影响:free 函数导致 ptr 指向的空间被释放,即可用于进一步分配。如果 ptr 是空指针,则不执行任何操作。否则,如果参数与
calloc
,malloc
或realloc
函数先前返回的指针不匹配,或者如果通过调用free
或realloc
释放了空间,则行为未定义。 -
有时第一种方法不能使用(例如,内存在一个函数中分配,并且在一个完全不同的函数中稍后释放)