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

C - Accessing data AFTER memory has been free()ed?

我在标准 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,但是数据值我写的这块内存还在"there",这就是为什么我可以通过第二个指针访问它们?

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

当您 malloc 内存时,您会得到一个指向某些 space 的指针,而当您释放它时,您会将它还给系统。通常,您仍然可以访问此内存,但在释放内存后使用内存非常糟糕。

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

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

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

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

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

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

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

如果您有兴趣了解更多,您应该尝试编写自己的 malloc,and/or read/learn 关于内存管理操作系统的事情。

您的代码崩溃是由于双重 freeAppendix J.2 of C11 表示行为未定义,例如当:

The pointer argument to the free or realloc function does not match a pointer earlier returned by a memory management function, or the space has been deallocated by a call to free or 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 标准规定即使

行为也是未定义的

The value of a pointer that refers to space deallocated by a call to the free or realloc function is used (7.22.3).

这意味着在 取消引用指针 (*p) 之后不仅有 未定义的行为,而且它以任何其他方式 使用 指针都是不安全的,即使 p == NULL 也有 UB。这是从 C11 6.2.4p2 得出的:

The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime.