释放记忆

可以通过调用 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 是空指针,则不执行任何操作。否则,如果参数与 callocmallocrealloc 函数先前返回的指针不匹配,或者如果通过调用 freerealloc 释放了空间,则行为未定义。

  • 有时第一种方法不能使用(例如,内存在一个函数中分配,并且在一个完全不同的函数中稍后释放)