在malloc()之后写入指针越界,不会导致错误

当我尝试下面的代码时它工作正常。 我错过了什么吗?

main() { int *p; p=malloc(sizeof(int)); printf("size of p=%d\n",sizeof(p)); p[500]=999999; printf("p[0]=%d",p[500]); return 0; } 

我尝试使用malloc(0 * sizeof(int))或其他任何东西,但它工作得很好。 当我根本不使用malloc时,程序只会崩溃。 因此,即使我为数组p分配了0个内存,它仍然可以正确地存储值。 那么,为什么我甚至打扰了malloc呢?

似乎工作正常,但它根本不是很安全。 通过在分配的内存块之外写入数据,您将覆盖一些不应该的数据。 这是段错误和其他内存错误的最大原因之一,而你正在观察它看起来在这个简短的程序中工作是什么使得它很难找到根本原因。

阅读本文 ,特别是有关内存损坏的部分,开始了解问题。

Valgrind是分析内存错误的绝佳工具,例如您提供的错误。

@David做了很好的评论。 将运行代码的结果与运行以下代码进行比较 。 注意后者导致ideone.com上的运行时错误(几乎没有任何有用的输出!)(点击链接),而前者在你经历时成功。

 main() { int *p; p=malloc(sizeof(int)); printf("size of p=%d\n",sizeof(p)); p[500]=999999; printf("p[0]=%d",p[500]); p[500000]=42; printf("p[0]=%d",p[500000]); return 0; } 

如果你不分配内存,p中有垃圾,所以写入它可能会失败。 一旦你进行了有效的malloc调用,p指向有效的内存位置,你可以写入它。 你正在覆盖你不应该写的内存,但没有人会牵着你的手来告诉你。 如果你运行你的程序和一个内存调试器,如valgrind,它会告诉你。 欢迎来到C.

在你的记忆结束时写下Undefined Behavior™,这意味着任何事情都可能发生 – 包括你的程序运行就像你刚刚做的那样完全合法。 你的程序运行的原因好像你已经完成了malloc(501*sizeof(int))是完全特定于实现的,并且确实可以特定于任何事情,包括月亮的阶段。

这是因为无论你使用malloc()的大小,都会为P分配一些地址。 虽然零大小会引用无效内存,因为内存尚未分配,但它可能位于不会导致程序崩溃的位置,但行为将是未定义的。

现在,如果你不使用malloc(),它将指向一个服务位置并尝试访问它可能会导致程序崩溃。

我用malloc(0 * sizeof(int))试了一下

根据C99,如果传递给malloc的大小为0,则C运行时可以返回NULL指针,或者分配的行为就像请求是非零分配一样,除了不应该取消引用返回的指针。 所以它是实现定义的(例如某些实现返回一个零长度的缓冲区),在你的情况下,你没有得到一个NULL指针,但你正在使用一个你不应该使用的指针。如果你在不同的运行时尝试它可以得到一个NULL指针。

当你调用malloc()时,你会从一个较大的页面中划出一小块内存。

  malloc(sizeof(int)); 

实际上并不是在32位机器上分配4个字节(分配器将其填充到最小大小)+用于在整个生命周期内跟踪块的堆元数据的大小(块根据其大小放置在容器中并在使用中标记或由分配者释放)。 hxxp://en.wikipedia.org/wiki/Malloc或更具体地说是hxxp://en.wikipedia.org/wiki/Malloc#dlmalloc_and_its_derivatives如果您在Linux上测试它。

因此,超出大块边界的写作并不一定意味着你会崩溃。 在p + 5000,您没有在为该初始块分配的页面范围之外写入,因此您在技术上写入有效的映射地址。 欢迎记忆腐败。 http://www.google.com/search?sourceid=chrome&ie=UTF-8&q=heap+overflows

我们的CheckPointer工具可以检测到此错误。 它知道p的分配是一个4字节的块,因此进行了分配,它在分配p的区域之外。 它会告诉你p [500]赋值是错误的。