【发布时间】:2015-08-21 09:45:55
【问题描述】:
直到今天,我一直相信在内存空间上调用 free() 会释放它以供进一步分配而无需进行任何其他修改。特别是考虑到this SO question 明确指出free() 不会将内存归零。
然而,让我们考虑一下这段代码(test.c):
#include<stdlib.h>
#include<stdio.h>
int main()
{
int* pointer;
if (NULL == (pointer = malloc(sizeof(*pointer))))
return EXIT_FAILURE;
*pointer = 1337;
printf("Before free(): %p, %d\n", pointer, *pointer);
free(pointer);
printf("After free(): %p, %d\n", pointer, *pointer);
return EXIT_SUCCESS;
}
编译(GCC 和 Clang):
gcc test.c -o test_gcc
clang test.c -o test_clang
结果:
$ ./test_gcc
Before free(): 0x719010, 1337
After free(): 0x719010, 0
$ ./test_clang
Before free: 0x19d2010, 1337
After free: 0x19d2010, 0
为什么会这样?我一直生活在谎言中还是我误解了一些基本概念?还是有更好的解释?
一些技术信息:
Linux 4.0.1-1-ARCH x86_64
gcc version 4.9.2 20150304 (prerelease) (GCC)
clang version 3.6.0 (tags/RELEASE_360/final)
【问题讨论】:
-
当内存返回分配系统时,它可以用于系统喜欢的任何目的。它可能将控制信息存储在内存空间中,修改返回的内容。分配器没有限制;他们既不需要修改也不需要保持返回给他们的内存不变。对已释放内存的任何访问都是无效的。
-
对于它的价值,你实际上是在测试同样的东西,因为
free是 C 库的一部分,gcc和clang在你的系统上使用glibc。尝试分配一大块内存而不是 8 个字节,比如 16 MB,看看取消引用释放的内存是否会崩溃。 -
您看到这种特定行为的原因完全有可能与动态内存库的元数据管理有关。许多使用未分配块的前几个字节来跟踪大小、使用中和前后指针。有可能在释放的过程中,它以某种方式修改了数据,从而产生了这种行为作为副作用,因为在释放内存后您没有业务取消引用内存。 :)
-
@browning0:嗯,正如我在回答中所说,是的,这就是调试实现通常会做的事情。但这仅适用于调试实现。而被释放块的开始通常用于完全不同的家庭用途。顺便说一句,在您的示例中,您正在专门检查块的开头,这并不能很好地表明块的其余部分会发生什么。
-
另外请注意,如果在调用 free 之后,您的分配器决定删除虚拟页面,当它稍后再次映射它们时,内核(在现代系统中)将在出现故障时将它们擦除干净(或者归零或随机化),因为读取另一个进程丢弃的内存页是安全故障。因此,实际上发生了很多事情,出于所有意图和目的,内存缓冲区的内容在释放后变得不确定。
标签: c gcc memory-management clang free