我在标准 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)仍然“在那里”,这就是为什么我可以通过第二个指针访问它们吗?
(我并不是说这是个好主意,我只是想了解系统的逻辑。)
当你 malloc 内存时,你会得到一个指向某个空间的指针,当你释放它时,你会把它还给系统。通常,你仍然可以访问这个内存,但是在你释放它之后使用内存是非常糟糕的。
确切的行为是未定义的,但在大多数系统上,您可以继续访问内存,或者您会遇到段错误。
您可以尝试的一个有趣的实验是在释放该指针后尝试分配更多内存。在我试过的大多数系统上,你会得到相同的块(这是一个问题,如果你依赖于释放块中的数据)。您的程序最终会使用两个指针,但由于它们指向相同的物理数据,您将覆盖自己的数据!
这样做的原因是当你malloc数据的时候(当然取决于malloc的实现),malloc首先向操作系统请求一块数据(通常比malloc请求大很多),malloc会给你一段那段记忆不过,您将能够访问最初从操作系统获得的内存 malloc 的任何部分,因为对于操作系统而言,这是您的程序在内部使用的所有内存。当您释放时,您是在告诉 malloc 系统内存是空闲的,稍后可以将其还给程序。
在 malloc 区域之外写是非常危险的,因为
如果您有兴趣了解更多信息,我建议您通过泄漏检测器 valgrind 运行您的程序,以更好地了解已释放/未释放的内容。
PS:在没有操作系统的系统上,您很可能根本不会遇到段错误,并且您可以随意写入任何地方。操作系统负责触发段错误(当您写入/读取您无权访问的内存时,例如内核或受保护的内存)
如果你有兴趣了解更多,你应该尝试编写自己的 malloc,和/或阅读/了解内存管理操作系统。
您的代码崩溃是由于双重
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 说的:
当指针指向(或刚过去)的对象达到其生命周期的终点时,指针的值变得不确定。