malloc(): 内存损坏

10
这是我认为可能导致此错误的简化程序。
char *p = (char*)malloc(8192);
for(int i = 0; i < 9200; ++i){
  p[i] = '1';
}
char *s = (char*)malloc(strlen(p)); 

原始项目比较复杂,因此我对其进行了简化。我使用malloc分配了8192字节的空间。然后我的程序会向数组中写入超过8192个字符。接着我会再次使用malloc来分配内存。
这个小程序并没有崩溃。但在原始大型项目中,它会出现以下错误:

malloc(): memory corruption: 0x0000000007d20bd0 ***

这种差异可能是由什么引起的呢?

8
你已经分配了8192字节,但是却试图写入9200字节。这有什么意义吗?不如试试不要这样做,这样就不会破坏内存了 :) - user7881131
3
除了尝试写入您未分配的内存之外,在您的for循环之后,变量p现在指向您写入的最后一个“1”后面的字节。这意味着strlen(p)将无法返回任何有意义的内容。 - Героям слава
10
“这种差异可能是由什么引起的?” 未定义行为是未定义的。 - alk
3
由于没有写入任何0终止符,因此strlen()在任何情况下都会失败(导致未定义的行为)。 - alk
5
在C语言中,使用malloc()及其相关函数时,不需要进行类型转换。 - alk
显示剩余3条评论
4个回答

16

这是未定义行为,因为您已经分配了8192字节的内存,但是您正在尝试写入9200字节,超出了边界。


我知道,但是是哪种未定义行为?@rsp - Yuan Wen
15
不存在不同种类的未定义行为。未定义行为就是未定义行为。 - Sergej Christoforov
1
@YuanWen:这里列出了许多调用未定义行为的情况:http://stackoverflow.com/documentation/c/364/undefined-behavior#t=20170424105058111517 - alk
3
@rootTraveller:完全没有必要让代码崩溃……再说了,未定义的行为就是未定义的。我曾经见过这样的代码在生产环境中运行了好几年… :-( - alk
6
@YuanWen undefined behavior的意思是任何事情都可能发生。你的程序可能会崩溃,可能会正常运行,也可能会引入微妙的数据损坏,甚至可能格式化你的硬盘。如果连接了适当的硬件,它还可能创建Skynet并开始灭绝人类。 - JeremyP
显示剩余2条评论

12

这种差异可能是什么原因呢?

基本上,内存分配器一次会分配 的内存供程序使用,并为您提供其中的指针(确保后面的空间可以自由使用)。由于这些页面通常比8KiB大,因此您的迷你程序没有问题。但是,如果一个更大的程序正在分配更大量的内存并继续向已分配空间的末尾写入,那么您最终将尝试写入未分配的内存(或其他程序使用的内存!),从而破坏内存。


char *p = (char*)malloc(8192); for(int i = 0; i < 9200; ++i){ p[i] = '1'; } 这样写不会出异常吗? - roottraveller
不一定。只要超出您分配的空间的内存是为您的进程保留且未使用的,那么就“可以”。但是,如果没有深入挖掘操作系统的内存分配,就无法知道这一点,因此请尽量避免访问超出您分配的空间的内存。该代码肯定也会破坏内存;只需要将malloc结果放在正确的位置即可。 - user7881131
3
在现代操作系统和带有MMU的CPU上,除非使用显式共享内存,否则在用户空间中写入另一个进程的内存并不是常见的操作。但通常情况下,如果需要进行这样的操作,你可能想要写入“另一个程序使用的内存”。请注意,本翻译力求通俗易懂,未进行解释。 - Ilja Everilä
操作系统是否有纠正损坏内存的方法? - Alex S

2

向未分配的内存写入数据是未定义行为。这是因为malloc()返回一段可写入的内存区域,当你写入超出该区域末尾的位置时,你会覆盖一些不属于你的内容。

这可能会覆盖malloc自身使用的结构或其他完全不相关的内容。


1
这是一件运气问题。你的操作系统可能会保留比你请求的8kB更多的内存。此外,你之前和之后所保留的内存也可能会影响程序的行为。
并不是说你的程序一定会在缓冲区溢出时崩溃。实际上,程序的行为是未定义或由具体实现决定的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接