【问题标题】:How can I use this unallocated memory?我怎样才能使用这个未分配的内存?
【发布时间】:2015-01-31 05:01:03
【问题描述】:

为什么我使用以下程序没有任何错误?

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[]){
  char *pStr = (char*) malloc(25); 
  free(pStr); 
  strcpy(pStr, "foo");
  printf("%s\n", pStr);
  return 0;
}

free(pStr) 不应该阻止我写信到那个地址吗?难道我不用重新分配空间才能使用吗?

【问题讨论】:

  • 在任何合同、文档、示例代码等中,它是否说 C 语言会阻止您写入内存?如果它没有在任何地方这么说,你是从哪里得到这个想法的?
  • “未定义”行为的确切含义是:它可能会出现段错误,它可能会崩溃,它可能会让恶魔飞出你的鼻子,......或者它可能会完美运行,但永远不会给你任何线索坏了。
  • C 编程语言比汇编语言高出一步。它是高级编程语言中最低的。因此,它允许您做任何您想做的事情,并假设您知道自己在做什么。新手没有安全网。
  • 我认为它是低级语言中最高的,但是是的,这就是基本思想。
  • Dennis Ritchie 有句名言:“C 具有汇编的所有功能,具有汇编的所有便利”

标签: c memory-management heap-memory


【解决方案1】:

free 不会阻止你做任何事情,只要它在语法上是正确的。所以仍然非常欢迎您复制到char*,就像您从一开始就没有分配内存一样。但这是未定义的行为,并且很可能(阅读:可能)导致您的程序崩溃或在没有警告的情况下做错事。

例如,如果您的编译器进行了一些优化,它可能会重新排序一些指令以节省时间。由于您已释放内存,编译器可能认为在该位置为稍后将创建的其他数据分配内存是安全的。如果优化器移动该分配并写入您的strcpy 之前,您可以覆盖存储在那里的对象。

考虑这段代码:

int main(int arc, char *argv[]){
    char* pStr = (char*) malloc(25);
    free(pStr);
    strcpy(pStr, "foo");
    printf("%s\n", pStr);
    int* a = (int*) malloc(sizeof(int));
    *a = 36;
    printf("%d\n", *a);
}

由于您写入了未分配的内存,您无法确定printfs 中的任一会显示什么。 36 可能已经覆盖了一些“foo”字符串。 “foo”字符串可能已经覆盖了 a 指向的值 36。或者也许它们都不会影响另一个,并且您的程序似乎运行得很好,直到您更改某个变量的名称并重新编译,并且由于某种原因,即使您没有更改任何内容,一切都搞砸了。

故事的寓意:你是正确的,你不应该写信给freed memory;但是,您无法写入freed 内存是不正确的。 C 不检查这种情况,并假设您正在尝试做一些花哨的事情。如果您确切地知道编译器如何优化以及在调用 malloc 时分配内存的位置,您可能知道在特定情况下写入未分配的内存是安全的,C 不会阻止您这样做。但在 99.999% 的情况下,您所有事情都不了解,请不要这样做。

【讨论】:

    【解决方案2】:

    这是一个undefined behavior。真正发生的事情是特定于实现的。

    在实践中,free 经常将释放的内存块标记为可重用以供将来使用 malloc-s。

    另见this ...

    【讨论】:

      【解决方案3】:

      正如其他答案指出的那样,它是未定义的行为,因此编译器没有义务进行任何诊断。但是,如果您有现代版本的gcc (>= 4.8) 或clang,那么AddressSanitizer 可能会在出现这种“use-after-free”错误时有所帮助:

      $ gcc -ansi -pedantic -fsanitize=address check.c
      $ ./a.out 
      =================================================================
      ==2193== ERROR: AddressSanitizer: heap-use-after-free on address 0x60060000efe0 at pc 0x4009a8 bp 0x7fff62e22bc0 sp 0x7fff62e22bb8
      ...
      

      常见的防御性编程“技巧”是在free() 调用之后立即分配NULL

      free(pStr), pStr = NULL;

      有了它,在 GNU/Linux 上,"Segmentation fault"pStr 可能会取消引用,但不能保证这一点。

      【讨论】:

        【解决方案4】:

        了解你的问题的答案你需要了解内存分配的过程。在一般意义上,malloc/free 是库函数。它们管理从操作系统服务分配的内存池。

        [冒着过于简单化的风险]

        您的第一个 malloc 找到了一个空池。然后它调用操作系统系统服务将页面添加到添加到池中的进程。然后 Malloc 从该池中返回一块内存。

        调用 free 将块返回到池中。它仍然是池中的有效内存块。

        如果您访问已释放的地址,内存仍然存在。但是,您正在 #$@$ 占用 malloc 的内存池。这种访问最终会在 #$#$ 中对您造成影响。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多