free和malloc如何在C中工作?

use*_*082 58 c memory malloc free

我试图找出如果我试图从中间"释放"指针会发生什么,请看下面的代码:

char *ptr = (char*)malloc(10*sizeof(char));

for (char i=0 ; i<10 ; ++i)
{
    ptr[i] = i+10;
}
++ptr;
++ptr;
++ptr;
++ptr;
free(ptr);
Run Code Online (Sandbox Code Playgroud)

我收到一个崩溃,出现未处理的异常错误消息.我想了解为什么以及如何免费工作,这样我不仅知道如何使用它,而且还能够理解奇怪的错误和异常并更好地调试我的代码ץ

非常感谢

Jas*_*ams 104

当你对一个块进行malloc时,它实际上会分配比你要求的更多的内存.这个额外的内存用于存储信息,例如分配块的大小,以及链接到块链中下一个空闲/已使用块的链接,有时还有一些"保护数据"可帮助系统检测您是否写过去已分配块的结尾.此外,大多数分配器会将内存的总大小和/或开头向上舍入为多个字节(例如,在64位系统上,它可能将数据与64位(8字节)的倍数对齐为从非对齐地址访问数据对于处理器/总线来说可能更加困难和低效,因此您最终可能会得到一些"填充"(未使用的字节).

当你释放指针时,它会使用该地址来查找它添加到已分配块的开头(通常)的特殊信息.如果传入不同的地址,它将访问包含垃圾的内存,因此其行为未定义(但最常见的是导致崩溃)

稍后,如果你释放()块但不"忘记"你的指针,你可能会意外地尝试通过该指针访问数据,并且行为是未定义的.可能发生以下任何一种情况:

  • 内存可能会放在一个空闲块列表中,因此当您访问它时,它仍然包含您留在那里的数据,并且您的代码正常运行.
  • 内存分配器可能已经将(部分)内存提供给程序的另一部分,并且可能会覆盖(部分)旧数据,因此当您读取它时,您将获得可能导致意外行为的垃圾或者从您的代码中崩溃.或者你会写下其他数据,导致你的程序的其他部分在将来的某个时刻表现得很奇怪.
  • 内存可能已经返回到操作系统(您不再使用的内存"页面"可以从您的地址空间中删除,因此该地址不再有任何可用内存 - 实质上是一个未使用的"漏洞"在你的应用程序的内存中).当您的应用程序尝试访问数据时,将发生硬内存故障并终止您的进程.

这就是为什么确保在释放它指向的内存后不使用指针是很重要的 - 最好的做法是在释放内存后将指针设置为NULL,因为你可以轻松地测试NULL,以及尝试通过NULL指针访问内存将导致错误但一致的行为,这更容易调试.


Dig*_*oss 25

你可能知道你应该准确地传回你收到的指针.

因为free()最初并不知道块的大小,所以它需要辅助信息才能从其地址中识别原始块,然后将其返回到空闲列表.它还将尝试将小的释放块与邻居合并,以产生更有价值的大型空闲块.

最终,分配器必须具有关于块的元数据,至少它需要在某处存储长度.

我将描述三种方法.

  • 一个显而易见的地方是在返回的指针之前存储它.它可以分配比请求大几个字节的块,将大小存储在第一个单词中,然后返回指向第二个单词的指针.

  • 另一种方法是使用地址作为关键字来保持至少描述已分配块长度的单独映射.

  • 实现可以从地址中获取一些信息,从地图中获取一些信息.4.3BSD内核分配器(我认为,称为"McKusick-Karel分配器")为小于页面大小的对象进行二次幂分配,并且仅保持每页大小,从给定页面的所有分配单一尺寸.

有些类型的第二种,可能是任何类型的第三种类型的分配器都可以实际检测到你已经提升了指针和DTRT,尽管我怀疑是否有任何实现会烧掉运行时这样做.


Zeo*_*rad 10

大多数(如果不是全部)实现将查找数据量,以便在您操作的实际指针之前释放几个字节.做一个狂野free会导致内存映射损坏.

如果你的例子,当你分配10个字节的内存时,系统实际保留,比方说,14.前4个包含你请求的数据量(10)然后返回值malloc是指向第一个字节的指针未分配的数据在14中分配.

当你调用free这个指针时,系统会向后查找4个字节,知道它最初分配了14个字节,因此它知道要释放多少.此系统会阻止您将要释放的数据量作为额外参数释放给free自身.

当然,其他实现malloc/ free可以选择其他方式来实现这一点.但是它们通常不支持与free指定malloc函数返回的指针不同的指针.


Pet*_*osB 8

来自http://opengroup.org/onlinepubs/007908775/xsh/free.html

free()函数导致ptr指向的空间被释放; 也就是说,可供进一步分配.如果ptr是空指针,则不执行任何操作.否则,如果参数与之前由calloc(),malloc(),realloc()或valloc()函数返回的指针不匹配,或者如果通过调用free()或realloc()来释放空间,则行为未定义.任何使用引用释放空间的指针都会导致未定义的行为.

  • 链接,@ Petros和其他人的问题可能不同意我(很有可能看到我们有12万多人),他们可能会消失(是的,甚至像维基百科这样的东西).我不介意链接本身,但答案中应该有足够的肉,这样,即使互联网的其余部分被销毁,SO仍然可能有用.我倾向于做的解释足以回答那些想要进一步发展的链接的问题. (8认同)

sha*_*oth 7

这是未定义的行为 - 不要这样做.只有free()从中获取的指针malloc()才会在此之前调整它们.

问题free()必须非常快,因此它不会尝试找到调整后的地址所属的分配,而是尝试将完全调整后的地址的块返回到堆中.这会导致未定义的行为 - 通常是堆损坏或程序崩溃.


Jas*_*n D 5

你正在释放错误的地址.通过更改ptr的值,可以更改地址.free无法知道它应该尝试释放一个块,从4字节开始.保持原始指针完整并释放它而不是被操纵的指针.正如其他人所指出的,做你正在做的事情的结果是"未定义的"......因此未处理的例外.