我在标准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()
,但是 数据值 我在这块内存中写的仍然是“那里”,这就是我可以通过第二个指针访问它们的原因吗?
(我不是建议这是一个好主意,我试图理解系统的逻辑。)
答案 0 :(得分:3)
当你使用malloc内存时,你会得到一个指向某个空间的指针,当你释放它时,你会把它交还给系统。通常,你仍然可以访问这个内存,但是在你释放它之后使用内存是非常糟糕的。
确切的行为是未定义的,但在大多数系统上,您可以继续访问内存,也可以获得段错误。
您可以尝试的一个有趣的实验是在释放该指针后尝试使用malloc更多内存。在我尝试过的大多数系统上,你得到相同的块(如果你依赖于释放块中的数据,这是一个问题)。你的程序最终会使用两个指针,但由于它们指向相同的物理数据,你将覆盖自己的数据!
原因是当你使用malloc数据时(当然取决于malloc实现),malloc首先从操作系统请求一个数据块(通常比malloc请求大得多),malloc会给你一个那段记忆。您将能够访问最初从操作系统获取的内存malloc的任何部分,因为对于操作系统,它是您的程序在内部使用的所有内存。当你自由时,你告诉malloc系统内存是免费的,并且可以稍后返回给程序。
在malloc区域外写作是非常危险的,因为
如果您有兴趣了解更多信息,我建议您通过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开始说的:
当指针指向(或刚刚过去)的对象到达其生命周期的末尾时,指针的值变得不确定。