我在讲座中接受过教学,在指针上调用free()
两次真的非常非常糟糕。我知道在释放它之后设置指向NULL
的指针是一种很好的做法。
然而,我仍然没有听到任何解释为什么会这样。根据我的理解,malloc()
的工作方式,它应该在技术上跟踪它已分配并指定您使用的指针。那么为什么它不知道它通过free()
收到的指针是否已被释放?
当您在之前已被释放的位置上致电free()
时,我很想了解内部会发生什么。
答案 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()
我们的块时,我们实际上只释放了应用程序当前使用的部分内存块。这将导致更多意想不到的问题。