C - 在内存空闲()编辑后访问数据?

时间:2017-03-03 21:10:34

标签: c pointers malloc free

我在标准C中阅读了很多关于malloc()和free()的内容。据我所知,你malloc()对于某些内存只有一次然后你free()相同的内存只有一次。这可能是不好的做法,但我知道在你malloc()内存后,你可以定义多个指向它的指针。一旦你free()这些指针中的任何一个,分配的内存被解除分配?

考虑这个玩具示例:

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

int main(){

    char* p = (char*)malloc(10 * sizeof(char));     // allocate memory
    int* q = (int*)p;                               // pointer to the same block of memory
    *p = 'A';                                       // Input some data
    printf("TEST::  %c %d\n", *p, *q);              // Everything's ok so far...
    free(p);                                        // free() my allocated memory?
    sleep(10);                                      // wait
    printf("%c\n", *q);                             // q now points to de-allocated memory
                                                    // shouldn't this segfault?

    free(q);                                        // *** SEGFAULTS HERE ***

    return 0;
}

输出是:

[Linux]$ ./a.out
TEST::  A 65

*** Error in `./a.out': double free or corruption (fasttop): 0x0000000001ac4010 ***
======= Backtrace: =========
...lots of backtrack info...

所以我假设当我free()第一个指针时,内存被认为是free(),但是 数据值 我在这块内存中写的仍然是“那里”,这就是我可以通过第二个指针访问它们的原因吗?

(我不是建议这是一个好主意,我试图理解系统的逻辑。)

2 个答案:

答案 0 :(得分:3)

当你使用malloc内存时,你会得到一个指向某个空间的指针,当你释放它时,你会把它交还给系统。通常,你仍然可以访问这个内存,但是在你释放它之后使用内存是非常糟糕的。

确切的行为是未定义的,但在大多数系统上,您可以继续访问内存,也可以获得段错误。

您可以尝试的一个有趣的实验是在释放该指针后尝试使用malloc更多内存。在我尝试过的大多数系统上,你得到相同的块(如果你依赖于释放块中的数据,这是一个问题)。你的程序最终会使用两个指针,但由于它们指向相同的物理数据,你将覆盖自己的数据!

原因是当你使用malloc数据时(当然取决于malloc实现),malloc首先从操作系统请求一个数据块(通常比malloc请求大得多),malloc会给你一个那段记忆。您将能够访问最初从操作系统获取的内存malloc的任何部分,因为对于操作系统,它是您的程序在内部使用的所有内存。当你自由时,你告诉malloc系统内存是免费的,并且可以稍后返回给程序。

在malloc区域外写作是非常危险的,因为

  1. 根据您的c实施情况,它可能会出现段错误
  2. 您可以覆盖malloc所依赖的元数据结构,这会在以后释放/ malloc更多数据时导致非常严重的问题
  3. 如果您有兴趣了解更多信息,我建议您通过valgrind(一个检漏仪)运行您的程序,以更好地了解已释放/未释放的内容。

    PS:在没有操作系统的系统上,你很可能根本不会得到一个段错误,而且你可以在任何地方找不到任何东西。操作系统负责触发段错误(当你写入/读取你无权访问的内存时,如内核或受保护的内存)

    如果您有兴趣了解更多信息,您应该尝试编写自己的malloc,和/或阅读/了解内存管理操作系统。

答案 1 :(得分:2)

代码崩溃是由于free加倍。 Appendix J.2 of C11表示行为未定义,例如:

  

free或realloc函数的指针参数与先前由内存管理函数返回的指针不匹配,或者该空间已通过调用free或realloc(7.22.3.3,7.22.3.5)解除分配。

但是,只需从刚刚释放的内存中读取值,就可以编写将在Linux上崩溃的代码。

在glibc + Linux中,有两种不同的内存分配机制。一个使用brk / sbrk来调整数据段的大小,另一个使用mmap系统调用来请求操作系统提供大块内存。前者用于小分配,例如上面的10个字符,而mmap用于大块。因此,即使在免费后访问内存,您也可能会崩溃:

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

int main(){
    char* p = malloc(1024 * 1024);
    printf("%d\n", *p);
    free(p);
    printf("%d\n", *p);
}

最后,C11标准表明,即使

,行为仍未定义
  

使用指向通过调用free或realloc函数解除分配的空间的指针的值(7.22.3)。

这意味着,不仅解除引用指针*p)之后未定义行为,而且使用它是不安全的以任何其他方式指针,即使p == NULL UB 。这是从C11 6.2.4p2开始说的:

  

当指针指向(或刚刚过去)的对象到达其生命周期的末尾时,指针的值变得不确定。