我在以下C代码中得到了奇怪的结果。
int main()
{
int *p = (int *) malloc(100);
p[120] = 5;
printf("\n %d", p[120]);
}
由于我只分配了100个字节,这段代码应该会导致分段错误。然而,它打印出“5”,并且没有出现任何运行时错误。请问有人能解释一下原因吗?
我在以下C代码中得到了奇怪的结果。
int main()
{
int *p = (int *) malloc(100);
p[120] = 5;
printf("\n %d", p[120]);
}
由于我只分配了100个字节,这段代码应该会导致分段错误。然而,它打印出“5”,并且没有出现任何运行时错误。请问有人能解释一下原因吗?
不,这段代码不应该(必然)导致段错误(segfault)。当您尝试访问未分配给您的进程的虚拟内存页时,才会发生段错误。
"堆"或"自由存储区"是您的进程拥有的虚拟内存页区域。 malloc()
API将此区域细分为块,并返回指向该块的指针。
如果您的指针地址超出了您所拥有的块的末尾,通常会访问到属于堆但不属于您分配块的内存。通过这种方式,您可以破坏其他堆块,甚至是用于定义堆的malloc()
使用的数据结构。
有关堆破坏的更多信息以及在代码调试版本中检测它的方法,可以参考这本书:
《编写可靠代码:微软开发无缺陷 C 程序的技术》(Writing Solid Code: Microsoft's Techniques for Developing Bug-Free C Programs) by Steve Maguire
附注:对于学究式的人来说,极少数情况下,通过访问超出堆块末尾的内存,您可能会访问到不属于堆的内存。在这些情况下,您可能会得到您预期的段错误。您还可能会破坏堆以外的其他数据结构。这实际上是一种机率问题。但是,与典型的堆块相比,堆本身非常大,因此像您提供的示例代码那样,99%的情况下会破坏堆。您提供的示例属于这99%的情况。
无效的内存访问并不总是会导致分段错误、总线错误或其他崩溃。例如,如果在你的数组之后立即分配了另一个块,则你正在更改该块中的值,这可能是任何值。
您正在写入未初始化的内存;在 C 中是允许的,但并不是一个好主意。这种情况不一定会导致分段错误。
您正在写入未分配的内存。如果程序运行时间足够长,由于堆损坏的影响,程序可能最终会终止。
段错误的常见原因:
释放不正确的指针或未拥有的块。
int *x=0;
x = 200; / 导致段错误 */
基于被确定为非法内存访问,MMU异常会生成段错误。根据操作系统如何构造其内存,一个操作系统上的一次内存访问可能是合法的(虽然是错误的),而在另一个操作系统上可能是非法的。