C ++ free()改变其他内存

时间:2011-02-24 22:29:20

标签: 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的相同示例,并且会出现相同的结果。

非常感谢任何有关理解记忆中这个特定地方受影响的信息或帮助。

4 个答案:

答案 0 :(得分:5)

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

答案 1 :(得分:2)

基本上你在看你没有分配的记忆。你不能对你所请求的内存(即你分配的16个字节)发生的事情做出任何假设。没有任何异常情况发生。

运行时和编译器可以随意执行任何操作,因此您不应在程序中使用它们。运行时可能会更改这些字节的值以跟踪其内部状态。 释放内存不太可能导致程序崩溃。另一方面,访问你已经取消分配的内存就像你的样本一样,这可能是一个很大的编程错误。

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

答案 2 :(得分:0)

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

答案 3 :(得分:0)

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

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

您不应该更改甚至查看已分配区域之前/之后的字节数。访问,甚至只是为了读取你没有分配的内存的结果是Undefined Behavior(是的,你真的可以让程序真正崩溃或者因为读取未分配的内存而疯狂行为)。