当你 malloc 一个块时,它实际上分配的内存比你要求的多一点。这个额外的内存用于存储信息,例如分配块的大小,以及到块链中下一个空闲/使用块的链接,有时还有一些“保护数据”,可以帮助系统检测你是否写过去您分配的块的结尾。此外,大多数分配器会将总大小和/或内存部分的开头四舍五入为字节的倍数(例如,在 64 位系统上,它可能会将数据对齐为 64 位(8 字节)的倍数,如对于处理器/总线来说,从非对齐地址访问数据可能更加困难和低效),因此您最终可能还会得到一些“填充”(未使用的字节)。
当您释放指针时,它使用该地址来查找它添加到已分配块的开头(通常)的特殊信息。如果你传入一个不同的地址,它将访问包含垃圾的内存,因此它的行为是不确定的(但最常见的是会导致崩溃)
稍后,如果您 free() 块但没有“忘记”您的指针,您将来可能会不小心尝试通过该指针访问数据,并且行为未定义。可能会出现以下任何一种情况:
- 内存可能会放在空闲块列表中,因此当您访问它时,它仍然恰好包含您留在那里的数据,并且您的代码正常运行。
- 内存分配器可能已将(部分)内存分配给程序的另一部分,然后可能会覆盖(部分)您的旧数据,因此当您读取它时,您会得到可能导致意外行为的垃圾或从您的代码中崩溃。或者您将覆盖其他数据,导致程序的其他部分在将来的某个时间点出现奇怪的行为。
- 内存可能已返回给操作系统(您不再使用的内存“页面”可以从您的地址空间中删除,因此该地址不再有任何可用内存 - 本质上是一个未使用的“洞”在您的应用程序的内存中)。当您的应用程序尝试访问数据时,将发生硬内存故障并终止您的进程。
这就是为什么确保在释放指针指向的内存后不使用指针很重要的原因——最佳实践是在释放内存后将指针设置为 NULL,因为您可以轻松测试 NULL,并且尝试通过 NULL 指针访问内存将导致错误但一致的行为,这更容易调试。