free和malloc如何在C中工作?
我试图找出如果我试图释放一个“从中间”的指针会发生什么,例如,看看下面的代码:
char *ptr = (char*)malloc(10*sizeof(char)); for (char i=0 ; i<10 ; ++i) { ptr[i] = i+10; } ++ptr; ++ptr; ++ptr; ++ptr; free(ptr);
我得到一个未处理的exception错误信息的崩溃。 我想知道为什么和如何自由工作,以便我不仅知道如何使用它,而且能够理解奇怪的错误和exception,并更好地debugging我的代码
非常感谢
当你malloc块,它实际上分配多一点的内存比你所要求的。 这个额外的内存被用来存储信息,比如分配块的大小,以及链接块中下一个空闲/被使用块的链接,有时还有一些“保护数据”帮助系统检测你是否过去分配的块的结束。 另外,大多数分配器会将内存部分的总大小和/或起始位置整理为多个字节(例如,在64位系统上,它可以将数据与64位(8字节)的倍数alignment为从非alignment的地址访问数据对于处理器/总线来说可能更加困难和低效),所以你也可能会得到一些“填充”(未使用的字节)。
当你释放指针时,它使用该地址来查找它添加到分配块的开头(通常)的特殊信息。 如果你传入一个不同的地址,它将访问包含垃圾的内存,因此它的行为是不确定的(但是最经常会导致崩溃)
后来,如果你释放()块但不“忘记”你的指针,你可能会意外地尝试通过这个指针访问数据,而且行为是未定义的。 可能会发生以下任何情况:
- 内存可能被放在一个空闲块的列表中,所以当你访问它时,它仍然包含你留在那里的数据,并且你的代码正常运行。
- 内存分配器可能已经把内存的一部分给了你的程序的另一部分,而这大概会覆盖掉你的旧数据的一部分,所以当你读到它的时候,你会得到垃圾,这可能会导致意外的行为或从您的代码崩溃。 或者你会写下其他的数据,导致你的程序的其他部分在未来某个时候performance得很奇怪。
- 内存可能已被返回到操作系统(您不再使用的内存“页面”可以从您的地址空间中删除,所以在该地址不再有任何内存可用 – 本质上是一个未使用的“洞”在您的应用程序的内存)。 当您的应用程序尝试访问数据时,会发生硬内存故障,并终止您的进程。
这就是为什么在释放内存之后确保不使用指针是非常重要的 – 最好的做法是在释放内存之后将指针设置为NULL,因为您可以轻松地testingNULL,试图通过NULL指针访问内存将导致一个糟糕但一致的行为,这更容易debugging。
你可能知道你应该传回你收到的指针。
因为free()一开始并不知道块有多大,所以它需要辅助信息才能从其地址中识别原始块,然后将其返回到空闲列表。 它也会尝试将小块和邻居合并,以产生一个更有价值的大块空闲块。
最终,分配器必须有关于你的块的元数据,至less它需要在某处存储长度。
我将介绍三种方法来做到这一点。
-
一个明显的地方就是将它存储在返回的指针之前。 它可以分配一个比请求大几个字节的块,将大小存储在第一个字中,然后返回一个指向第二个字的指针。
-
另一种方法是使用地址作为关键字来保存至less描述分配块的长度的单独映射。
-
一个实现可以从地址获取一些信息,而从地图获取一些信息。 4.3BSD内核分配器(我认为是“McKusick-Karel分配器” )为小于页面大小的对象分配两次幂,并且只保留每页大小,从给定的页面一个单一的大小。
第二种types的第三种types的分配器可能会实际检测到您已经高级指针和DTRT ,尽pipe我怀疑是否有任何实现会烧制运行时来执行此操作。
大多数(如果不是全部的话)实现将在你正在操作的实际指针之前查找释放几个字节的数据量。 做一个狂放的free
将导致内存映射损坏。
如果你的例子,当你分配10个字节的内存,系统实际上保留,比方说,14.前4个包含你请求的数据量(10),然后malloc
的返回值是一个指向第一个字节未分配的数据在14分配。
当你在这个指针上free
时,系统将向后查找4个字节,以知道它最初分配了14个字节,以便知道释放多less。 这个系统可以阻止你提供大量的数据作为一个额外的参数free
自己。
当然,其他的malloc
/ free
实现可以select其他的方式来实现这一点。 但是他们通常不支持free
与malloc
或等价函数返回的指针不同的指针。
这是不确定的行为 – 不要这样做。 只有从malloc()
获得的free()
指针才会在之前调整它们。
问题是free()
必须非常快,所以它不会尝试find您调整的地址所属的分配,而是尝试返回在调整后的地址块到堆。 这导致了未定义的行为 – 通常是堆损坏或程序崩溃。
从http://opengroup.org/onlinepubs/007908775/xsh/free.html
free()函数使ptr指向的空间被释放; 即可供进一步分配。 如果ptr是空指针,则不会发生任何操作。 否则,如果参数与先前由calloc(),malloc(),realloc()或valloc()函数返回的指针不匹配,或者如果通过调用free()或realloc()释放空间,则行为是不确定的。 任何使用引用释放空间的指针都会导致未定义的行为。
你正在释放错误的地址。 通过改变ptr的值,你改变地址。 免费无法知道它应该尝试释放一个开始4个字节的块。 保持原来的指针完好无损,而不是操纵的指针。 正如其他人指出,做你在做什么的结果是“未定义”…因此,未处理的exception。
永远不要这样做。
你正在释放错误的地址。 通过改变ptr的值,你改变地址。 免费无法知道它应该尝试释放一个开始4个字节的块。 保持原来的指针完好无损,而不是操纵的指针。 正如其他人指出,做你在做什么的结果是“未定义”…因此,未处理的exception
采取从书: 理解和使用C指针
当分配内存时,附加信息将作为堆pipe理器维护的数据结构的一部分进行存储。 这些信息除了别的以外,还包括块的大小,通常放在紧靠分配块的地方。