两次指针自由调用

时间:2015-12-15 09:00:17

标签: c pointers malloc free dynamic-memory-allocation

我在讲座中接受过教学,在指针上调用free()两次真的非常非常糟糕。我知道在释放它之后设置指向NULL的指针是一种很好的做法。

然而,我仍然没有听到任何解释为什么会这样。根据我的理解,malloc()的工作方式,它应该在技术上跟踪它已分配并指定您使用的指针。那么为什么它不知道它通过free()收到的指针是否已被释放?

当您在之前已被释放的位置上致电free()时,我很想了解内部会发生什么。

4 个答案:

答案 0 :(得分:13)

当您使用malloc时,您告诉PC您要为堆保留一些内存位置。计算机返回指向寻址空间的第一个字节的指针。

当您使用free时,您实际上已经告诉计算机您不再需要该空间,因此它会将该空间标记为可用于其他数据。

指针仍然指向该内存地址。此时,另一个malloc调用可以返回堆中的相同空间。当您第二次调用free时,您不会释放以前的数据,而是释放新数据,这可能不适合您的程序;)

答案 1 :(得分:3)

回答你的第一个问题,

  

那么为什么它不知道它通过free()收到的指针是否已被释放?

因为,C标准中malloc()的规范并未强制要求这样做。当您调用malloc()或函数族时,它的作用是返回一个指针,并在内部存储指针中分配的内存位置的大小。这就是free()不需要大小来清理内存的原因。

此外,一旦free() - d,实际分配的内存所发生的情况仍然依赖于实现。调用free()只是一个标记,指出进程已经不再使用已分配的内存,并且可以根据需要进行回收和重新分配。因此,在那一点上跟踪分配的指针是非常不必要的。 所有的回溯对操作系统来说是不必要的负担。

但是,出于调试目的,一些库实现可以为您完成这项工作,例如DUMA或dmalloc以及Valgrind的最后但并非最不重要的memcheck工具。

现在,技术上,如果您在已经自由编辑的指针上调用C,则free()标准不会指定任何行为。它是undefined behavior

C11,章节§7.22.3.3,free()功能

  

[...]如果   该参数与先前由内存管理返回的指针不匹配   函数,或者如果通过调用free()realloc()释放了空格,则   行为未定义。

答案 2 :(得分:2)

当你打电话给malloc时,你会得到一个指针。运行时库需要跟踪malloc内存。通常malloc不会将内存管理结构与malloc内存分开,而是存储在一个位置。因此,对于x个字节,malloc实际上需要x + n个字节,其中一个可能的布局是前n个字节包含链接列表结构,其中指向下一个(也可能是前一个)分配的内存块。

当您free指针时,函数free可以遍历它的内部内存管理结构,并检查您传入的指针是否是{{1}的有效指针}编辑。只有这样它才能访问内存块的隐藏部分。但是进行此检查会非常耗时,特别是如果您分配了很多。所以malloc只是假设你传入一个有效的指针。这意味着它直接访问内存块的隐藏部分,并假设其中的链表指针有效。

如果你free一个块两次,那么你可能会遇到有人做了新free的问题,得到你刚刚释放的内存,覆盖它,第二个malloc读取无效指针从它。

free d指针设置为free是很好的做法,因为它有助于调试。如果您访问NULL内存,您的程序可能会崩溃,但它也可能只是读取可疑值并可能在以后崩溃。找到根本原因可能很难。如果将free指针设置为free,当您尝试访问内存时,程序将立即崩溃。这在调试过程中有很大帮助。

答案 3 :(得分:0)

C标准仅表示在free返回的指针上调用malloc两次,并且其族函数调用未定义的行为。没有进一步的解释为什么会这样 但是,解释为什么它很糟糕here

  

释放相同的块两次

     

要了解这种错误可能导致的错误,我们应该记住内存管理器的正常工作方式。通常,它将分配的块的大小存储在块本身之前的内存中。如果我们释放了内存,那么这个内存块可能已被另一个malloc()请求再次分配,因此这个 double-free 实际上会释放错误的内存块 - 导致我们有一个悬空指针在我们的应用程序的其他地方。这些错误往往比它们出现的代码中的位置要晚得多。有时候我们根本看不到它们,但是它们仍然潜伏着,等待着抓住丑陋头脑的机会。

     

可能发生的另一个问题是,在释放的块与相邻的空闲块合并形成更大的空闲块之后,将完成此 double-free ,然后更大的块重新开始-allocated。在这种情况下,当我们第二次尝试free()我们的块时,我们实际上只释放了应用程序当前使用的部分内存块。这将导致更多意想不到的问题。