在指针上调用 free 两次
Posted
技术标签:
【中文标题】在指针上调用 free 两次【英文标题】:Calling free on a pointer twice 【发布时间】:2016-03-20 23:38:42 【问题描述】:我在讲座中被教导,在指针上调用free()
两次真的非常糟糕。我知道在释放它之后立即设置指向NULL
的指针是一种很好的做法。
但是,我仍然没有听到任何关于为什么会这样的解释。据我了解,malloc()
的工作方式在技术上应该跟踪它已分配并让您使用的指针。那么它为什么不知道它通过free()
接收到的指针是否已经被释放呢?
我很想了解,当您在之前已被释放的位置上调用 free()
时,内部会发生什么。
【问题讨论】:
指针只是你注释数据方向的地方,但你真正释放的是数据本身 @DanielSanchez 是的,但是由于 malloc 向您抛出了那个指针,内存释放/分配构造是否应该仍然注意到它已经释放了指针指向的位置? 如果在free
之后不使用指针,则将其设置为NULL
是没有用的。 如果你在free
之后使用指针,无论你是否将它设置为NULL
,你都有未定义的行为。当然,如果您检查 NULL
,那么它会有所帮助,但是设置指向NULL
的指针并不是您绝对必须要做的事情,请根据具体情况进行取决于你如何使用指针。
它可以搜索释放的块是否已经在空闲块列表中,但这会减慢不需要该功能的程序
可以把malloc
和free
管理的堆内存看成一滩水。当您使用malloc
分配内存时,您会得到一勺水,您可以随心所欲地使用它。当您free
内存时,如果将一勺水倒回池中,您就会忘记哪个内存是您的,内存管理器也是如此。释放内存只会清空你的勺子,但你仍然保留实际的勺子(指针)。
【参考方案1】:
当您使用malloc
时,您是在告诉PC 您想在堆上为您保留一些内存位置。计算机返回一个指向寻址空间的第一个字节的指针。
当您使用free
时,您实际上是在告诉计算机您不再需要该空间,因此它将该空间标记为可用于其他数据。
指针仍然指向那个内存地址。此时,另一个malloc
调用可以返回堆中的相同空间。当您第二次调用free
时,您不会释放以前的数据,而是释放新数据,这可能对您的程序不利;)
【讨论】:
感谢您的解释!【参考方案2】:回答你的第一个问题,
那么为什么它不知道通过
free()
接收到的指针是否已经被释放了呢?
因为,C 标准中malloc()
的规范并未强制执行此操作。当您调用malloc()
或函数系列时,它所做的是返回一个指针并在内部存储分配的内存位置的大小in 该指针。这就是free()
不需要大小来清理内存的原因。
此外,一旦free()
-d,实际分配的内存发生的情况仍然取决于实现。调用free()
只是一个标记,指出分配的内存不再被进程使用,如果需要,可以回收和重新分配。因此,此时跟踪分配的指针是非常不必要的。保持所有回溯对操作系统来说是不必要的负担。
然而,出于调试目的,一些库实现可以为您完成这项工作,例如 DUMA 或 dmalloc,最后但并非最不重要的是 Valgrind 的 memcheck 工具。
现在,技术上,C
标准没有指定任何行为,如果您在已经释放的指针上调用 free()
。是undefined behavior。
C11
,第 7.22.3.3 章,free()
函数
[...] 如果 该参数与内存管理先前返回的指针不匹配 函数,或者如果空间已通过调用
free()
或realloc()
释放,则 行为未定义。
【讨论】:
【参考方案3】:C 标准仅表示在 malloc
返回的指针上调用 free
两次及其系列函数会调用未定义的行为。没有进一步的解释为什么会这样。
但是,为什么不好解释here:
两次释放同一个块
要了解这种错误可能导致什么,我们应该记住内存管理器通常是如何工作的。通常,它在内存中的块本身之前存储已分配块的大小。如果我们释放了内存,这个内存块可能已经被另一个
malloc()
请求再次分配,因此这个 double-free 实际上会释放错误的内存块 - 导致我们在某处有一个悬空指针其他在我们的应用程序中。此类错误往往比它们在代码中出现的位置晚得多。有时我们根本看不到它们,但它们仍然潜伏在周围,等待机会抬起丑陋的脑袋。另外一个可能出现的问题是,这个double-free会在被释放的chunk和相邻的free chunk合并在一起形成一个更大的free chunk,然后再把更大的chunk重新-分配。在这种情况下,当我们第二次尝试
free()
我们的块时,我们实际上只会释放应用程序当前正在使用的部分内存块。这会导致更多意想不到的问题。
【讨论】:
【参考方案4】:当您调用malloc
时,您将获得一个指针。运行时库需要跟踪malloc
ed 内存。通常malloc
不存储与malloc
ed 内存分开的内存管理结构,而是在一个位置。因此,x 字节的malloc
实际上占用 x+n 字节,其中一种可能的布局是前 n 字节包含一个链表结构,其中包含指向下一个(也可能是上一个)分配的内存块的指针。
当您free
一个指针时,函数free
可以遍历它的内部内存管理结构并检查您传入的指针是否是malloc
ed 的有效指针。只有这样它才能访问内存块的隐藏部分。但是做这个检查会非常耗时,特别是如果你分配了很多。所以free
只是假设你传入了一个有效的指针。这意味着它直接访问内存块的隐藏部分,并假设那里的链表指针是有效的。
如果你free
一个块两次,那么你可能会遇到问题,有人做了一个新的malloc
,得到了你刚刚释放的内存,覆盖了它,第二个free
从中读取了无效的指针。
将free
d 指针设置为NULL
是一种很好的做法,因为它有助于调试。如果您访问free
d 内存,您的程序可能会崩溃,但它也可能只是读取可疑值并可能稍后崩溃。找到根本原因可能很难。如果您将free
d 指针设置为NULL
,您的程序将在您尝试访问内存时立即崩溃。这对调试有很大帮助。
【讨论】:
以上是关于在指针上调用 free 两次的主要内容,如果未能解决你的问题,请参考以下文章
malloc 和 free 在 C 中是如何实现的? [复制]
调用释放已存储值的 char 指针时,free() 函数失败