C程序帮助:内存分配不足但仍然有效......为什么?

时间:2013-01-31 21:42:13

标签: c

  

可能重复:
  behaviour of malloc(0)

我正在尝试理解C中的内存分配。所以我正在试验malloc。我为这个指针分配了0个字节,但它仍然可以保存一个整数。事实上,无论我在malloc的参数中放入什么数字,它仍然可以保留我给它的任何数字。这是为什么?

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int *ptr = (int*)malloc(0);

    *ptr = 9;

    printf("%i", *ptr); // 9

    free(ptr);

    return 0;
}

它还打印9,那是什么?

5 个答案:

答案 0 :(得分:3)

  

如果size为0,则malloc()返回NULL或唯一指针   以后可以成功传递给free()的值。

我猜你正在打第二个案子。 无论如何,错误的指针碰巧出现在你可以写的区域而不会产生分段错误,但你可能正在写一些其他变量的空间来搞乱其价值。

答案 1 :(得分:2)

首先,您的编译器可能会连续看到这两行并对其进行优化:

*ptr = 9;
printf("%i", *ptr);

使用如此简单的程序,您的编译器实际上可能正在优化整个内存分配/空闲周期并使用常量代替。程序的编译器优化版本可能最终看起来更像是:

printf("9");

判断这是否确实发生的唯一方法是检查编译器发出的程序集。如果您正在尝试了解C的工作原理,我建议您在构建代码时明确禁用所有编译器优化。

关于您的特定malloc用法,请记住,如果分配失败,您将获得一个NULL指针。在将malloc用于任何事情之前,始终检查malloc(0)的返回值。盲目地解除引用它是使程序崩溃的好方法。

Nick发布的链接很好地解释了为什么malloc(0)可能起作用(请注意“工作”和“似乎工作”之间的显着差异)。为了总结那里的信息,允许free()返回NULL或指针。如果它返回一个指针,则明确禁止将其用于除将其传递给malloc(0)之外的任何其他内容。如果你尝试使用这样的指针,你正在调用未定义的行为,并且没有办法告诉结果会发生什么。它似乎对你有用,但这样做可能会覆盖属于另一个程序的内存并破坏它们的内存空间。简而言之:没有什么好事可以发生,所以不要把指针放在一边,不要在{{1}}上浪费你的时间。

答案 2 :(得分:2)

这里有很多好的答案。但它肯定是未定义的行为。有些人声称,未定义的行为意味着紫色的龙可能会飞出你的计算机或类似的东西...可能有一些历史背后我失踪的那个令人发指的声称,但我向你保证紫龙无论未定义的行为是什么,都不会出现。

首先,让我提一下,在没有MMU的情况下,在没有虚拟内存的系统上,您的程序可以直接访问系统上的所有内存,无论其是什么地址。在这样的系统中,malloc()只是帮助你以有序的方式划分出记忆的人;系统实际上无法强制您仅使用malloc()给您的地址。在具有虚拟内存的系统上,情况略微不同......好吧,好吧,有很多不同。但是在程序中,程序中的任何代码都可以访问虚拟地址空间中通过MMU映射到实际物理内存的任何部分。从malloc()获取地址或者是否调用rand()并且碰巧得到一个落在程序映射区域的地址并不重要;如果它被映射并且没有标记为只执行,则可以阅读它。如果它没有标记为只读,你也可以写它。是。即使你没有从malloc()获得它。

让我们考虑malloc(0)未定义行为的可能性:

  • malloc(0)返回NULL。

好的,这很简单。大多数计算机中确实存在物理地址0x00000000,甚至所有进程中的虚拟地址0x00000000,但操作系统故意不将任何内存映射到该地址,以便它可以捕获空指针访问。那里有一整页(通常是4KB)根本就没有映射,甚至可能超过4KB。因此,如果您尝试读取或写入空指针,即使存在偏移量,您也会触及这些甚至未映射的虚拟内存页面,并且MMU将抛出异常(a <操作系统捕获的em>硬件异常或中断),它声明了SIGSEGV(在Linux / Unix上)或非法访问(在Windows上)。

  • malloc(0)将有效地址返回给最小可分配单元的先前未分配的内存。

有了这个,你实际上得到了一块真实的记忆,你可以合法地称之为你自己的记忆,你不知道它的大小。你真的不应该在那里写任何东西(可能也没有读过)因为你不知道它有多大,而且就此而言,你不知道这是否是你的特殊情况&# 39;重新体验(见下列案例)。如果是这种情况,您给出的内存块几乎保证至少为4个字节,可能是8个字节,甚至可能更大;这一切都取决于您实施的最小可分配单位的大小。

  • malloc(0)故意返回未映射页面的地址 除了NULL之外的其他内存。

这可能是实施的一个很好的选择,因为它可以让您或系统跟踪和将malloc()调用与其相应的free()调用结合在一起,但实质上,它与返回NULL相同。如果您尝试通过此指针访问(读/写),您将崩溃(SEGV或非法访问)。

  • malloc(0)返回内存的某个其他映射页面中的地址 可能被其他人使用&#34;。

我发现商业化系统不太可能采用这种方式,因为它可以简单地隐藏错误而不是尽快将它们带出来。但如果确实如此,malloc()会返回一个指向内存中你不拥有的指针。如果是这种情况,当然,你可以随心所欲地写信给你,但是你会破坏其他代码的内存,尽管它会在程序的进程中记忆,所以你可以放心,你至少不会踩到另一个程序的记忆。 (我听到有人准备说,&#34;但它是UB,所以从技术上来说,可能会踩到其他程序的内存。是的,在某些环境中,就像一个嵌入式系统,这是正确的。没有现代商业操作系统可以让一个进程可以像访问malloc(0)一样轻松访问另一个进程的内存;事实上,你根本无法从中获取一个进程到另一个进程的内存,而无需通过操作系统为你完成。) 无论如何, 回到现实......这就是& #34;未定义的行为&#34;真的开始了:如果你正在写'#34;其他人的记忆&#34; (在您自己的程序过程中),您将以难以预测的方式改变您的程序行为。了解程序的结构以及内存中所有内容的布局,它是完全可预测的。但是从一个系统到另一个系统,事物将在内存中布局(出现在内存中的不同位置),因此对一个系统的影响不一定与对另一个系统的影响相同,或者对不同系统的影响不同时间。

  • 最后......不,那就是它。真的,真的,只有那四个 可能性。你可以争论特殊情况的子集点 上面的最后两个,但最终结果将是相同的。

答案 3 :(得分:1)

malloc(0) / free()来电没有崩溃的答案可以在这里找到:

zero size malloc

关于*ptr = 9,就像溢出一个缓冲区(如malloc'ing 10个字节并访问第11个),你正在写入你不拥有的内存,这样做就是在寻找麻烦。在这个特定的实现中,malloc(0)碰巧返回指针而不是NULL。

最重要的是,即使它似乎适用于简单的情况也是错误的。

答案 4 :(得分:0)

某些内存分配器具有“最小可分配大小”的概念。因此,即使你传递零,这也会返回指向word-size内存的指针。您需要检查系统分配器文档。但是如果它确实返回指向某个内存的指针,则依赖它是错误的,因为指针只应传递给realloc()或free()。