C free()如何工作?

时间:2011-01-16 05:53:01

标签: c memory-management free

  

可能重复:
  How malloc() and free() work

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

int * alloc()
{
    int *p = (int *)calloc(5,4);
    printf("%d\n",p);
    return p;
}

int main()
{
 int *p = alloc();

 free(p);
 printf("%d\n",p);
 p[0] = 1;
 p[1] = 2;
 printf("%d %d\n",p[0],p[1]);
}

至于代码段,我先分配5个整数。然后我释放了记忆。 当我打印p时,为什么p sill的值与首先分配的内存地址相同? 我也可以为p [0]和p [1]赋值。这是否意味着free()什么都不做?有一次,我 分配内存,我可以稍后使用虽然我已经释放它。

7 个答案:

答案 0 :(得分:13)

免费什么也不做。它将内存释放回分配器,因此可以再次使用内存。

你在做什么调用未定义的行为。您的程序可能出现,但稍后可能会崩溃。

答案 1 :(得分:13)

free在该地址释放内存。它不会更改p变量本身。但是,在此之后使用p执行任何操作都是未定义的行为。如果你在释放后立即使用它似乎可行,但它仍然完全错误,可能导致崩溃或更糟。

free是特定于实现的。但是,在大多数实现中,它将写入堆中的簿记数据,以指示内存现在可用。例如,它可能将特定块标记为未使用,或者将块与相邻的空块组合。

请注意,使用%d作为指针也是未定义的。

答案 2 :(得分:9)

内存保护具有页面粒度,需要内核交互

只能以页面为单位从程序中删除内存,即使这样也不太可能被观察到。

如果需要,

calloc(3)和malloc(3)与内核进行交互以获取内存。但是大多数free(3)的实现都没有将内存返回到内核 1 ,它们只是将它添加到一个空闲列表中,calloc()和malloc()稍后会参考,以便重用已释放的块

即使free()想要将内存返回给系统,它也需要至少一个连续的内存页才能让内核真正保护该区域,因此释放一个小块只会导致保护更改如果它是页面中的 last 小块。

所以你的街区就在那里,坐在免费清单上。您可以像访问它一样访问它。 C直接编译为机器代码,没有特殊的调试安排,对负载和存储没有健全性检查。现在,如果您尝试访问一个空闲块,标准就不会定义该行为,以免对库实现者提出不合理的要求。有各种各样的事情可能会出错:

  • 有时分配器会维护单独的内存块,有时候它们会使用它们在块之前或之后分配的标头(我猜是“块脚”),但它们可能只是想在块内使用内存以保持目的自由列表链接在一起。如果是这样,你读取块是正常的,但其内容可能会改变,写入块可能会导致分配器行为不端或崩溃。
  • 当然,您的块可能会在将来分配,然后很可能被您的代码或库例程覆盖,或者被calloc()覆盖。
  • 如果重新分配块,它的大小也可能会改变,在这种情况下,会在不同的地方写入更多的链接或初始化。

1。很少有free()实现尝试将内存返回系统的事实不一定是由于实现者的松弛。与内核交互比简单地执行库代码慢得多,而且好处很小。大多数程序具有稳态或增加的内存占用,因此分析堆寻找可返回内存所花费的时间将被完全浪费。其他原因包括内部碎片使页面对齐的块不太可能存在,并且返回块可能会将块分块到任何一方。最后,少数几个返回大量内存的程序可能会绕过malloc()并简单地分配和释放页面。

答案 3 :(得分:4)

从技术上讲

 p[0] = 1;
 p[1] = 2;
当您尝试使用 dangling pointer p时,

调用未定义的行为(这意味着任何)。

此外,即使printf("%d\n",p);调用UB(格式说明符的不匹配和printf()中的参数类型),也是迂腐的

答案 4 :(得分:3)

逻辑思考。

调用free(ptr),告诉系统,ptr引用的先前内存现在是免费的。

这意味着,系统可以根据需要使用内存。并且相信我,系统会很快或者稍后将自己的数据写入同一个地址,覆盖您的数据,或者同样的事情将在您的多任务操作系统中执行另一个程序。

您可能会问为什么ptr具有相同的值? 嗯,答案很简单:速度。 系统不知道您是否打算在免费通话后立即为ptr分配一个新的有效地址,或者您只是将其弃用。

在任何情况下,最好在免费调用之后立即为ptr指定一个NULL指针:

free(ptr);
ptr = NULL;

因为在您的功能/模块/程序的另一部分,您将能够检查:

if(NULL == ptr){
/* ... */
}

顺便说一句,如果你以某种方式在相同的地址上免费拨打两次,你的程序将崩溃 - 这是在免费通话后进行NULL分配的另一个好理由,因为免费(NULL) - 是安全的操作:

free(ptr);
ptr = NULL; /* try to comment out/put back this line and see what happens */
free(ptr);

在复杂的程序中,它可能会发生。

答案 5 :(得分:2)

free()实际上是释放内存。但是,它对指针没有任何作用。实际上,在C语言中,您可以尝试写入任何内存位置。没有安全检查(超出段错误,如果您尝试访问程序区域外的内存,则会导致程序崩溃)。但是,这并不意味着尝试使用已发布和/或未初始化的内存是个好主意。这是一个内存错误。你会变得讨厌他们。 :)

答案 6 :(得分:2)

free被定义为将malloc和朋友分配的内存返回给系统。实际发生的情况在不同系统上有所不同。以下事情可能发生:

  1. 内存在内存分配器数据结构中标记为“空闲”(您永远不会直接看到这些内容)
  2. 内存被内存分配器数据部分覆盖(其中一些内存数据存储在空闲块内)
  3. 将内存重新分配给程序的其他部分 - 例如,printf可能会将某些内存用于某些内部目的,或者可能不会 - 取决于实现。
  4. 内存将返回到操作系统,因此无法访问该进程。
  5. 实际上发生了哪些事情,取决于您在调用free之后及之后的确切时刻C库的实现和系统状态。但有一点应该是明确的 - 你应该永远在以任何方式调用free之后使用内存。它可能会崩溃,它可能不会崩溃,但它永远不会好。

    要抓住这种情况 - 在free之后使用内存 - 有许多程序。在Linux中,最受欢迎的是valgrind