C - 在内存释放后访问数据()?

问题描述 投票:0回答:2

我在标准 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()
ed,但是我在这个内存块中写入的数据值(s)仍然“在那里”,这就是为什么我可以通过第二个指针访问它们吗?

(我并不是说这是个好主意,我只是想了解系统的逻辑。)

c pointers malloc free
2个回答
5
投票

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

确切的行为是未定义的,但在大多数系统上,您可以继续访问内存,或者您会遇到段错误。

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

这样做的原因是当你malloc数据的时候(当然取决于malloc的实现),malloc首先向操作系统请求一块数据(通常比malloc请求大很多),malloc会给你一段那段记忆不过,您将能够访问最初从操作系统获得的内存 malloc 的任何部分,因为对于操作系统而言,这是您的程序在内部使用的所有内存。当您释放时,您是在告诉 malloc 系统内存是空闲的,稍后可以将其还给程序。

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

  1. 它可能会出现段错误,具体取决于您的 c 实现
  2. 您可以覆盖 malloc 所依赖的元数据结构,这会在您稍后释放/malloc 更多数据时导致非常糟糕的问题

如果您有兴趣了解更多信息,我建议您通过泄漏检测器 valgrind 运行您的程序,以更好地了解已释放/未释放的内容。

PS:在没有操作系统的系统上,您很可能根本不会遇到段错误,并且您可以随意写入任何地方。操作系统负责触发段错误(当您写入/读取您无权访问的内存时,例如内核或受保护的内存)

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


2
投票

您的代码崩溃是由于双重

free
C11 的附录 J.2 表示行为未定义,例如:

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

但是,仅通过从刚刚释放的内存中读取一个值,就可以编写在 Linux 上崩溃的代码。

在 glibc + Linux 中有两种不同的内存分配机制。一个使用

brk
/
sbrk
调整数据段的大小,另一个使用
mmap
系统调用要求操作系统提供大块内存。前者用于小分配,如上面的 10 个字符,
mmap
用于大块。所以你可能会在 free 之后访问内存而导致崩溃:

#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
)具有未定义的行为,而且以任何其他方式use指针是不安全的,即使做
p == NULL
也有UB .这是从 C11 6.2.4p2 说的:

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

© www.soinside.com 2019 - 2024. All rights reserved.