这里有很多很好的答案。但这绝对是未定义的行为。有些人声称未定义的行为意味着紫龙可能会飞出您的计算机或类似的东西......我失踪的离谱声称背后可能有一些历史,但我向您保证,无论何时紫龙都不会出现未定义的行为将是什么。
首先,让我提一下,在没有 MMU 的情况下,在没有虚拟内存的系统上,您的程序将可以直接访问系统上的所有内存,而不管其地址如何。在这样的系统上,malloc()
它只是帮助您以有序的方式划分内存的人;系统实际上不能强制您只使用malloc()
给您的地址。在有虚拟内存的系统上,情况稍微有点不同...嗯,好吧,很多不同。但是在您的程序中,您程序中的任何代码都可以访问通过 MMU 映射到真实物理内存的虚拟地址空间的任何部分。无论您是从 malloc() 获得地址,还是调用 rand() 并碰巧获得了位于程序映射区域中的地址,都无关紧要。如果它已映射且未标记为仅执行,则可以阅读它。如果它没有标记为只读,您也可以编写它。是的。即使你没有从malloc()
.
让我们考虑malloc(0)
未定义行为的可能性:
好的,这很简单。在大多数计算机中确实有一个物理地址 0x00000000,甚至在所有进程中都有一个虚拟地址 0x00000000,但是操作系统故意不将任何内存映射到该地址,以便它可以捕获空指针访问。那里有一整页(通常是 4KB)根本没有映射,甚至可能超过 4KB。因此,如果您尝试通过空指针读取或写入,即使有一个偏移量,您也会碰到这些甚至没有映射的虚拟内存页面,并且 MMU 将抛出异常(硬件异常或中断) 被操作系统捕获,并声明一个 SIGSEGV(在 Linux/Unix 上)或非法访问(在 Windows 上)。
malloc(0)
将有效地址返回到最小可分配单元的先前未分配的内存。
有了这个,你实际上得到了一块你可以合法地称之为你自己的真正的记忆,大小你不知道。你真的不应该在那里写任何东西(也可能不读),因为你不知道它有多大,就此而言,你不知道这是否是你正在经历的特殊情况(见下文例)。如果是这种情况,您获得的内存块几乎可以保证至少为 4 个字节,并且可能是 8 个字节甚至更大;这一切都取决于您的实现的最小可分配单元的大小。
malloc(0)
有意返回 NULL 以外的未映射内存页的地址。
这对于实现来说可能是一个不错的选择,因为它允许您或系统跟踪并配对 malloc() 调用及其相应的 free() 调用,但本质上,它与返回 NULL 相同。如果您尝试通过此指针访问(读/写),您将崩溃(SEGV 或非法访问)。
malloc(0)
返回“其他人”可能使用的其他映射内存页中的地址。
我发现商业上可用的系统不太可能采用这种方法,因为它只是为了隐藏错误而不是尽快将它们找出来。但如果是这样, malloc() 将返回一个指向您不拥有的内存某处的指针。如果是这种情况,当然,您可以随心所欲地对其进行写入,但是您会破坏其他代码的内存,尽管它会是您程序进程中的内存,因此您可以放心,您至少不会会踩到另一个程序的内存。(我听到有人准备说,“但它是 UB,所以从技术上讲它可以踩在其他程序的内存上。是的,在某些环境中,例如嵌入式系统,这是正确的。没有现代商业操作系统会让一个进程访问另一个进程的内存,就像简单地调用 malloc(0) 一样容易;事实上,如果不通过操作系统为你做这件事,你根本无法从一个进程到另一个进程的内存。) 无论如何,回到现实......这是“未定义行为”真正发挥作用的地方:如果您正在写入“其他人的记忆”(在您自己的程序的进程中),您将很难改变程序的行为- 预测方式。了解程序的结构以及所有内容在内存中的布局,这是完全可以预测的。但是从一个系统到另一个系统,事物会被布置在内存中(在内存中出现不同的位置),因此对一个系统的影响不一定与对另一个系统的影响相同,或者对同一系统的不同时间。
- 最后......不,就是这样。真的,真的,只有这四种可能性。您可以为上述最后两个中的特殊情况子集点争论,但最终结果将是相同的。