malloc()和堆内存

6

我在以下C代码中得到了奇怪的结果。

int main()
{
    int *p = (int *) malloc(100);
    p[120] = 5;
    printf("\n %d", p[120]);
}

由于我只分配了100个字节,这段代码应该会导致分段错误。然而,它打印出“5”,并且没有出现任何运行时错误。请问有人能解释一下原因吗?


4
可能只是运气好。 - Tom R
5
你把数字5写在一个不属于你的地方。如果这个地方的主人不喜欢你对它的家所做的事情,它会报复你。小心,那个地方可能属于你的USB驱动器,它会格式化你下一个插入的驱动器。 - pmg
6
实际上,在现代保护模式操作系统上,这种可能性不是很大。 - bcat
7
据您所知,malloc(100)仅为100字节/25个整数分配空间,假设sizeof(int)=4。因此,即使是p[25]也会超出边界。 - smci
6个回答

23

不,这段代码不应该(必然)导致段错误(segfault)。当您尝试访问未分配给您的进程的虚拟内存页时,才会发生段错误。

"堆"或"自由存储区"是您的进程拥有的虚拟内存页区域。 malloc() API将此区域细分为块,并返回指向该块的指针。

如果您的指针地址超出了您所拥有的块的末尾,通常会访问到属于堆但不属于您分配块的内存。通过这种方式,您可以破坏其他堆块,甚至是用于定义堆的malloc()使用的数据结构。

有关堆破坏的更多信息以及在代码调试版本中检测它的方法,可以参考这本书:

《编写可靠代码:微软开发无缺陷 C 程序的技术》(Writing Solid Code: Microsoft's Techniques for Developing Bug-Free C Programs) by Steve Maguire alt text

附注:对于学究式的人来说,极少数情况下,通过访问超出堆块末尾的内存,您可能会访问到不属于堆的内存。在这些情况下,您可能会得到您预期的段错误。您还可能会破坏堆以外的其他数据结构。这实际上是一种机率问题。但是,与典型的堆块相比,堆本身非常大,因此像您提供的示例代码那样,99%的情况下会破坏堆。您提供的示例属于这99%的情况。


实际上,由于他要求更改堆栈末尾20个字节,根据架构和周围代码的不同,他可能会破坏堆栈,甚至更改程序本身。这种行为是真正未定义的。 - T.E.D.
3
T.E.D.那句话只是“有点”正确。毫无疑问,他正在使用具有虚拟内存页大小的机器。可执行代码的VM保护可能与堆有所不同。我敢打赌他正在使用这样一个系统,因为C标准库的malloc()函数存在。但你所说的在特定情况下是正确的,例如在运行TRS-DOS的Z-80上的Radio Shack Model III机器上。 ;) - Heath Hunnicutt
是的,即使在虚拟内存页面系统上,你所写的代码也有可能发生。但这种情况并不常见,而且深入讲解这些细节对于教学意义也不大。应该在问题所在的层面上解决问题,避免过度追求细节。 - Heath Hunnicutt
@T.E.D. 实际上,你写的是“stack”,而我读成了“heap”。你写的是完全错误的。 - Heath Hunnicutt

6

无效的内存访问并不总是会导致分段错误、总线错误或其他崩溃。例如,如果在你的数组之后立即分配了另一个块,则你正在更改块中的值,这可能是任何值。


4
不,它可以导致段错误,但仅当内存位于进程之外时。否则,它只会修改程序内存的其他区域。C不会检查这一点,也不会以任何方式保护您,即使在上述明显情况下也是如此。许多软件破解者使用C的这个“特性”来重写具有提升特权的程序,并控制您的计算机。这被称为缓冲区溢出漏洞利用
这就是为什么应该避免使用C(和C ++)编写新软件,而应该选择更安全的语言,如Ada。

1
我知道最后一句话会让人投反对票。我不在乎。它是真的。 - T.E.D.

1

您正在写入未初始化的内存;在 C 中是允许的,但并不是一个好主意。这种情况不一定会导致分段错误。


3
你使用“allowed”这个词是具有误导性的。它在语法上是合法的,但会引发未定义行为。 - R.. GitHub STOP HELPING ICE

0

您正在写入未分配的内存。如果程序运行时间足够长,由于堆损坏的影响,程序可能最终会终止。


0

段错误的常见原因:

  • 解引用具有无效值的指针
  • 解引用空指针
  • 尝试写入只读段
  • 释放不正确的指针或未拥有的块。

    int *x=0;

    x = 200; / 导致段错误 */

基于被确定为非法内存访问,MMU异常会生成段错误。根据操作系统如何构造其内存,一个操作系统上的一次内存访问可能是合法的(虽然是错误的),而在另一个操作系统上可能是非法的。


虽然有些晚了,但是... x = 200 是可以的, *x = 200 会导致段错误。 - Peter Krnjevic

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