C free()如何工作?

sle*_*lee 9 c free memory-management

可能重复:
malloc()和free()如何工作

#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]);
}
Run Code Online (Sandbox Code Playgroud)

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

Mat*_*hen 14

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

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

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


Emp*_*ian 13

免费并没有做任何事.它将内存释放回分配器,因此可以再次使用内存.

你在做什么调用未定义的行为.您的程序似乎可以正常运行,但可能会在以后崩溃.


Dig*_*oss 9

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

内存只能以页面为单位从程序中删除,甚至不太可能被观察到.

如果需要,calloc(3)和malloc(3)确实与内核交互以获取内存.但是大多数free(3)的实现都没有将内存返回给内核1,它们只是将它添加到一个空闲列表中,calloc()和malloc()稍后会参考,以便重用已发布的块.

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

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

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

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


Pra*_*rav 4

从技术上来说

 p[0] = 1;
 p[1] = 2;
Run Code Online (Sandbox Code Playgroud)

当您尝试使用悬空指针时,调用未定义的行为(这意味着任何事情都可能发生) 。 p

此外,迂腐甚至printf("%d\n",p);调用 UB (格式说明符和参数类型不匹配printf()

  • “main”中的两行“printf”也是如此:第一行用于不匹配的格式字符串,第二行用于取消引用参数中的无效指针。 (2认同)