2011-02-24 81 views
0

我开始注意到,有时当我在某些程序中释放内存时,它们会莫名其妙地崩溃。我开始缩小的罪魁祸首,并拿出那说明我有困难的理解时的例子:C++免费()更改其他内存

#include <iostream> 
#include <stdlib.h> 

using namespace std; 

int main() { 
char *tmp = (char*)malloc(16); 
char *tmp2 = (char*)malloc(16); 

long address = reinterpret_cast<long>(tmp); 
long address2 = reinterpret_cast<long>(tmp2); 
cout << "tmp = " << address << "\n"; 
cout << "tmp2 = " << address2 << "\n"; 

memset(tmp, 1, 16); 
memset(tmp2, 1, 16); 

char startBytes[4] = {0}; 
char endBytes[4] = {0}; 

memcpy(startBytes, tmp - 4, 4); 
memcpy(endBytes, tmp + 16, 4); 
cout << "Start: " << static_cast<int>(startBytes[0]) << " " << static_cast<int>(startBytes[1]) << " " << static_cast<int>(startBytes[2]) << " " << static_cast<int>(startBytes[3]) << "\n"; 
cout << "End: " << static_cast<int>(endBytes[0]) << " " << static_cast<int>(endBytes[1]) << " " << static_cast<int>(endBytes[2]) << " " << static_cast<int>(endBytes[3]) << "\n"; 
cout << "---------------\n"; 

free(tmp); 

memcpy(startBytes, tmp - 4, 4); 
memcpy(endBytes, tmp + 16, 4); 
cout << "Start: " << static_cast<int>(startBytes[0]) << " " << static_cast<int>(startBytes[1]) << " " << static_cast<int>(startBytes[2]) << " " << static_cast<int>(startBytes[3]) << "\n"; 
cout << "End: " << static_cast<int>(endBytes[0]) << " " << static_cast<int>(endBytes[1]) << " " << static_cast<int>(endBytes[2]) << " " << static_cast<int>(endBytes[3]) << "\n"; 

free(tmp2); 

return 0; 
} 

这里是我看到的输出:我使用

tmp = 8795380 
tmp2 = 8795400 
Start: 16 0 0 0 
End: 16 0 0 0 
--------------- 
Start: 17 0 0 0 
End: 18 0 0 0 

Borland的免费编译器。我知道,我正在看的头字节是特定于实现的,像“reinterpret_cast”这样的东西是不好的做法。我只想找到答案的问题是:为什么“结束”的第一个字节从16更改为18?

被认为“结束”的4个字节是tmp之后的16个字节,它们是tmp2之前的4个字节。它们是tmp2的头文件 - 为什么在tmp上调用free()会影响内存中的这个地方?

我尝试过使用new []和delete []创建/删除tmp和tmp2的相同示例,并且会出现相同的结果。

任何信息或帮助理解为什么这个特定的地方在记忆中受到影响将不胜感激。

+5

不相关,但C++程序员应该使用new和delete(或new []和delete []),而不是malloc和free。 – 2011-02-24 22:32:10

+1

更普遍的是,如果你使用'new []'和'delete []',你可能真的需要'std :: vector'。 – birryree 2011-02-24 22:35:19

+0

我不能再给J.N.的+1评论,但如果我能的话,我会! – corsiKa 2011-02-24 22:35:51

回答

5

你将不得不问你的libc实施它为什么改变。无论如何,它为什么重要?这是libc尚未分配给您的内存区域,可能用于维护自己的数据结构或一致性检查,或者可能根本没有使用。

+0

这只是一个出于好奇而提出的问题,但无论你是否正确 - 理由并不重要,因为它不是我使用的记忆。 – 2011-02-24 22:56:48

+0

@Jason Terranova,这不是讽刺,任何理由都将取决于实施。例如,如果你使用glibc在Linux上,请查看你的glibc版本的源代码并深入研究。如果你不知道你的libc实现,那么你的问题就不能得到回答,一般来说:“X未定义当我做X时,为什么我会看到Y?“ – rlibby 2011-02-24 23:37:08

0

从堆中删除已分配元素的操作可能会修改其他堆节点,或者实现保留一个或多个字节的头以用作先前分配的保护字节。

2

基本上你正在寻找你没有分配的记忆。你不能假设你所请求的内存以外的内存会发生什么(即你分配的16个字节)。没有任何异常发生。

运行时和编译器可以自由地做他们想做的事情,所以你不应该在你的程序中使用它们。运行时可能会更改这些字节的值以跟踪其内部状态。 释放内存不太可能导致程序崩溃。另一方面,访问你已经解除分配的内存就像是你的样本,这是很大的编程错误,很可能会这样做。

一个避免这种情况的好方法是将任意指针设置为NULL。这样做会在访问释放的变量时强制程序崩溃。

0

内存管理器必须记住,例如分配的内存块的大小是多少,malloc。有不同的方法,但可能最简单的方法是分配比调用中请求的大小多4个字节,并在指针返回给调用者之前存储大小值。

然后,free的实现可以从传递的指针中减去4个字节,以获得指向存储大小的指针,然后可以将块(例如)链接到该大小的可用可重用块列表(可以再次使用这4个字节来存储到下一个块的链接)。

你不应该改变甚至在分配区域之前/之后查看字节。访问的结果,即使只是为了阅读,你没有分配的内存也是未定义的行为(是的,你真的可以通过阅读未分配的内存来真正导致程序崩溃或行为异常)。