在什么情况下malloc可以返回NULL?

它从来没有发生在我身上,而且我已经编程多年了。

有人能给我一个非平凡程序的例子,其中malloc实际上不起作用吗?

我不是在谈论内存耗尽 :我正在寻找一个简单的例子,当你只分配一个由用户给出的绑定大小的内存块时,假设一个整数,导致malloc失败。

是。

尝试使用比系统更多的内存(通过耗尽地址空间或虚拟内存 – 以较小者为准)。

 malloc(SIZE_MAX) 

可能会这样做。 如果没有,重复几次直到你用完为止。

你需要在嵌入式系统中做一些工作,你经常会在那里返回NULL 🙂

在现代大容量地址空间和后备存储系统中耗尽内存要困难得多,但在处理大量数据的应用程序中,例如GIS或内存数据库,或者在您错误代码导致内存泄漏。

但是,你以前从未体验过它并不重要 – 标准说它可能发生,所以你应该迎合它。 在过去的几十年里,我也没有被汽车击中,但这并不意味着我没有首先徘徊在道路上。

并重新编辑:

我不是在谈论内存耗尽,……

内存耗尽的定义malloc没有给你所需的空间。 无论是分配所有可用内存还是堆碎片导致无法获得连续块,即使内存区域中所有空闲块的聚合更高,或者使用符合标准的function人为地限制地址空间使用也无关紧要:

 void *malloc (size_t sz) { return NULL; } 

C标准不区分故障模式 ,只是成功或失败。

用c编写的任何程序都需要动态分配比当前允许的操作系统更多的内存。

为了好玩,如果您使用的是ubuntu类型

  ulimit -v 5000 

您运行的任何程序很可能会崩溃(由于malloc故障),因为您将任何一个进程的可用内存量限制为精简量。

除非您的内存已经完全保留(或严重碎片化),否则让malloc()返回NULL -pointer的唯一方法是请求大小为零的空间:

 char *foo = malloc(0); 

引用C99标准,§7.20.3,第1小节:

如果请求的空间大小为零,则行为是实现定义的:返回空指针,或者行为就像大小是非零值一样,除了返回的指针不应用于访问对象。

换句话说, malloc(0)可以返回NULL -pointer或指向零分配字节的有效指针。

只需查看malloc的手册页。

成功时,指向由函数分配的内存块的指针。
此指针的类型始终为void *,可以将其强制转换为所需类型的数据指针,以便可以取消引用。
如果函数未能分配所请求的内存块,则返回空指针。

既然你问了一个例子,这里有一个程序会(最终)看到malloc返回NULL

 perror();void*malloc();main(){for(;;)if(!malloc(999)){perror(0);return 0;}} 

什么? 你不喜欢故意混淆代码吗? ;)(如果它运行几分钟并且没有在您的机器上崩溃,请将其999 ,将999更改为更大的数字并再试一次。)

编辑:如果无论数字有多大都不起作用,那么正在发生的事情就是你的系统正在说“这里有一些记忆!” 但只要你不尝试使用它,它就不会被分配。 在这种情况下:

 perror();char*p;void*malloc();main(){for(;;){p=malloc(999);if(p)*p=0;else{perror(0);return 0;}} 

应该做的伎俩。 如果我们可以使用GCC扩展,我认为我们可以通过更改char*p;void*malloc(); void*p,*malloc(); 但如果你真的想打高尔夫球,你就会参加Code Golf SE。

选择任何平台,虽然嵌入式可能更容易。 malloc (或new )大量的RAM(或随着时间的推移泄漏RAM,甚至使用朴素算法将其分段)。 繁荣。 当“坏”事情发生时, malloc确实会为我返回NULL

回应你的编辑。 是的,再次。 随着时间的推移,内存碎片可以使得即使单个int分配也会失败。 还要记住, malloc不仅为int分配4个字节,而且可以获取所需的空间。 它有自己的簿记function,通常最少可以获得32-64字节。

在一个或多或少的标准系统上,使用标准的单参数malloc,有三种可能的故障模式(我能想到):

1)不允许分配请求的大小。 例如,即使有更多存储空间,某些系统也可能不允许分配> 16M。

2)具有默认边界的请求大小的连续空闲区域不能位于堆中。 可能仍然有很多堆,但只是一块不够。

3)总分配堆超过了一些“人为”限制。 例如,用户可能被禁止分配超过100M,即使在单个组合堆中有200M空闲且可用于“系统”。

(当然,你可以得到2和3的组合,因为有些系统在堆增长时会将非连续的地址空间块分配给堆,将“堆大小限制”放在块的总数上。)

请注意,某些环境支持其他malloc参数,例如对齐和池ID,这些参数可以添加自己的扭曲。

是。 当内核/系统lib确定无法分配内存时,Malloc将返回NULL。

您通常在现代机器上看不到这一点的原因是,Malloc并没有真正分配内存,而是它要求为您的程序保留一些“虚拟地址空间”,以便您可以在其中写入。 像现代Linux这样的内核实际上过度提交,即只要它们都适合系统的地址空间(通常是64位平台上的48位,IIRC),它们就可以让你分配比你的系统实际提供的内存(swap + RAM)更多的内存。 。 因此,在这些系统上,您可能会在触发返回NULL指针之前触发OOM杀手。 一个很好的例子是32位机器中的512MB RAM:编写一个可被OOM杀手吃掉的C程序是微不足道的,因为它试图将所有可用的RAM +交换用于malloc。

(在Linux上编译时可以禁用过度命令,因此它取决于构建选项,无论给定的Linux内核是否会过度使用。但是,库存桌面发行版内核会这样做。)