【问题标题】:Why freed struct in C still has data?为什么 C 中释放的结构仍然有数据?
【发布时间】:2011-02-26 22:52:14
【问题描述】:

当我运行这段代码时:

#include <stdio.h>

typedef struct _Food
{
    char          name [128];
} Food;

int
main (int argc, char **argv)
{
    Food  *food;

food = (Food*) malloc (sizeof (Food));
snprintf (food->name, 128, "%s", "Corn");

free (food);

printf ("%d\n", sizeof *food);
printf ("%s\n", food->name);
}

我还是得到了

128
Corn

虽然我已经释放了食物。为什么是这样?内存真的释放了吗?

【问题讨论】:

  • 我个人不同意人们建议在释放后将指针归零是 C 中的一个好习惯。它可能导致粗略的“if(ptr == NULL)”检查哪些掩码错误如果您没有进行归零,则 valgrind 可能 已捕获。但要进行更深入的讨论,请参阅以下问题:stackoverflow.com/questions/1931126/…
  • 我现在尝试了valgrind,它没有检测到内存泄漏,但它可以检测到访问释放内存的错误。谢谢你的提示。经过测试,我可以说在这个例子中: - 内存被释放 - 当我尝试访问释放的内存时没有错误或警告
  • 您的示例中没有内存泄漏。你是说如果你删除 free 语句就不会收到泄漏通知吗?如果是这样,请查看 --leak-check 的各种设置,因为有许多不同的情况,并且有些程序故意不通过并在程序退出时释放所有内容(作为增加关机速度,因为无论如何操作系统都会扔掉进程内存):valgrind.org/docs/manual/mc-manual.html#mc-manual.leaks
  • 我想知道是否释放了内存,因为一切就像我从未释放过它一样。现在使用 valgrind,我使用选项“--tool=memcheck --leak-check=yes”确认没有泄漏。唯一让我困扰的是,在 C 中访问释放的数据没有显示错误或警告。

标签: c memory struct free


【解决方案1】:

当你释放“食物”时,你是在说你已经吃完了。但是,指针 food 仍然指向同一个地址,并且该数据仍然存在(在不需要时必须将释放的每一位内存都归零会产生太多开销)

基本上这是因为它是一个如此小的例子,它有效。如果在 free 和 print 语句之间有任何其他 malloc 调用,那么您可能不会看到这一点,并且很可能会以某种可怕的方式崩溃。你不应该依赖这种行为。

【讨论】:

  • 捕获这种错误的成本更高。但是,如果在调试阶段您愿意支付这笔费用(至少在偶尔运行时),请考虑使用 Valgrind 的 memcheck 之类的东西。你也会发现其他东西:valgrind.org/docs/manual/mc-manual.html
【解决方案2】:

没有什么能比得上免费的食物 :) 当您“释放”某些东西时,这意味着同一空间再次准备好被其他东西使用。这并不意味着用垃圾填满它。 其次,指针值没有改变——如果你正在认真编码,你应该在释放指针后将指针设置为 NULL,这样就不会发生像这样的潜在垃圾访问。

【讨论】:

    【解决方案3】:

    释放内存并不一定会覆盖它的内容。

    【讨论】:

      【解决方案4】:

      sizeof 是编译时操作,因此内存分配不会改变它的工作方式。

      free 不会擦除内存,它只是将块标记为未使用。即使您分配了几百兆字节的内存,您的指针可能仍然不会被覆盖(现代计算机有 很多 的 RAM)。但是,释放内存后,您就不能再依赖它的值了。

      看看你的开发环境是否有内存分配调试设置——当你free它们时,有些设置会用0xDEADBEEF之类的东西覆盖块。

      另外,您可能希望养成在调用free 后立即将指针设置为NULL 的习惯(以帮助鼓励您的程序尽早且大声地崩溃)。

      【讨论】:

        【解决方案5】:

        free 告诉内存分配器它可以重用该内存块,仅此而已。它不会用零或任何东西覆盖块 - 幸运的是,因为这可能是一个非常昂贵的操作!它所做 所做的是使指针的任何进一步取消引用未定义,但“未定义”行为很可能意味着“做与以前相同的事情” - 你不能依赖它。在另一个编译器、另一个 runime 或其他条件下,它可能会抛出异常、终止程序或损坏其他数据,所以......不要这样做。

        【讨论】:

          【解决方案6】:

          C 中没有“结构有数据”或“结构没有数据”之类的东西。在您的程序中,您有一个指向内存某处的指针。只要此内存属于您的应用程序(即未返回到系统),它将始终包含某些内容。那个“东西”可能完全是垃圾,或者看起来或多或少有意义。此外,内存可能包含看似有意义的垃圾(之前存储在那里的数据的剩余部分)。

          这正是您在实验中观察到的。一旦你释放了这个结构,它之前占用的内存就正式包含了垃圾。但是,这些垃圾可能仍然类似于存储在该结构对象中的原始数据的点点滴滴,在它被释放的那一刻。在您的情况下,您很幸运,因此数据看起来完好无损。不过不要指望它——下一次它可能会被彻底摧毁。

          就 C 语言而言,您所做的事情构成了未定义的行为。您不能检查已释放的结构是否“有数据”。你问的“为什么”问题在 C 语言领域并不存在。

          【讨论】:

            【解决方案7】:

            在某些系统中,释放内存会将其从地址空间中取消映射,如果您在取消分配后尝试访问它,您将获得核心转储或等效内容。

            在 win32 系统中(至少在 XP 之前),具体情况不是。微软特意在 32 位 Windows 上制作了内存子系统,以保持与众所周知的 MS-DOS 应用程序在释放内存后使用内存的兼容性。

            在 MS-DOS 编程模型中没有映射或进程空间的概念,因此这些类型的错误直到在 Windows95 下作为 DOS 模式程序执行时才会显示为程序故障。

            这种行为在 32 位 Windows 中持续了十多年。现在它可能会改变,因为 Vista 和 7 等系统中的旧版兼容性正在被取消。

            【讨论】:

            • -1:目前还没有通过取消映射内存来实现 free 的系统(可能非常大的分配除外)。
            • +1 因为这一点是正确的,即使 Randall 确信没有一个现有的操作系​​统取消映射内存 -我不知道每个操作系统,我不确定 all 不会取消映射...我可以相信延迟取消映射...因此,内存 可能未映射或尚未映射,具体取决于系统活动。
            猜你喜欢
            • 2011-06-16
            • 1970-01-01
            • 2013-05-04
            • 1970-01-01
            • 1970-01-01
            • 2010-11-06
            • 2021-02-01
            • 1970-01-01
            • 2012-11-15
            相关资源
            最近更新 更多