包含对malloc()/ realloc()的调用……这是一个好主意吗?

对于赋值,我需要分配一个动态缓冲区,使用malloc()作为初始缓冲区,使用realloc()来扩展该缓冲区(如果需要)。 我使用的任何地方(re | m)alloc(),代码如下所示:

 char *buffer = malloc(size); if (buffer == NULL) { perror(); exit(EXIT_FAILURE); } 

程序只读取文件中的数据并输出,所以我认为只要退出程序(re | m)alloc失败就是个好主意。 现在,真正的问题是:

包裹电话是否有益,例如像这样?

 void *Malloc(int size) { void *buffer = malloc(size); if (buffer == NULL) { perror(); exit(EXIT_FAILURE); } return buffer; } 

或者这是一个坏主意?

我是否应该在我的C代码中检测OOM(内存不足)错误?

这是我对类似问题的回答。 总而言之,我赞成设计应用程序,以便从任何类型的崩溃中恢复,然后将内存不足视为崩溃的原因。

在呈现的forms中这是一个坏主意,因为除了为作业编写的一个简单的程序之外,你想要做一些比拯救更有用/更优雅的事情。 所以最好不要养成坏习惯。 这并不是说分配的包装本身是坏的(集中式error handling可能是一件好事),只是你不检查返回值的包装器(例如,根本没有返回的包装器)失败)是一个坏主意,除非你提供了一些允许代码挂钩到纾困逻辑的机制。

如果您确实希望以您提供的forms进行此操作,我强烈建议使用比Malloc更明显不同的名称而不是malloc 。 像malloc_or_die一样。 🙂

在大多数情况下,使用空指针的尝试无论如何都会很快崩溃程序,并且它会使调试变得更容易,因为你得到了一个很好的核心转储,如果你调用exit()就得不到exit()

我给出的唯一建议是在分配之后尽快取消引用返回的指针,即使只是无偿,这样核心转储可以引导您直接进入错误的malloc调用。

你很少能从内存耗尽中恢复,所以退出通常是正确的做法。 但这样做可以使验尸更容易。

但需要明确的是,内存耗尽通常在操作系统被页面交换活动削弱后很久就会发生。 这个策略实际上只对捕捉荒谬的分配很有用,比如因为bug而尝试malloc(a_small_negative_number)。

在你的情况下,没关系。 只记得给出一条提前退出原因的消息,指定行号会很好。 有人这样想:

 void* malloc2(int size, int line_num){ void *buffer = malloc(size); if (buffer == NULL) { printf("ERROR: cannot alloc for line %d\n", line_num); perror(); exit(EXIT_FAILURE); } return buffer; }; #define Malloc(n) malloc2((n), __LINE__) 

编辑:正如其他人提到的那样,对于经验丰富的程序员来说,这不是一个好的习惯,但对于一个有困难的初学者来说,即使在“快乐”的情况下跟踪程序流程也没有问题。

“因为过度使用而检查malloc失败是无用的”或“当malloc失败时操作系统已经瘫痪”的想法严重过时了。 强大的操作系统永远不会过度使用内存,而且历史上不那么强大的操作系统(如Linux)现在可以通过简单的方法来禁用过度使用并防止操作系统因内存耗尽而瘫痪 – 只要应用程序尽其所能不崩溃当malloc失败时刻录!

malloc在现代系统上出现故障的原因有很多:

  • 没有足够的物理资源来实例化内存。
  • 虚拟地址空间耗尽,即使有足够的物理内存空闲。 这可以在具有> 4gb ram + swap的32位机器(或32位用户空间)上轻松实现。
  • 内存碎片。 如果您的分配模式非常糟糕,最终可能会有400万个16字节的块,间隔1000个字节,并且无法满足malloc(1024)调用。

如何处理内存耗尽取决于程序的性质。

当然,从系统整体健康的角度来看,你的程序死了很好。 这可以减少资源饥饿,并可能允许其他应用程序继续运行。 另一方面,用户将非常沮丧,如果这意味着丢失编辑video的工作时间,打字纸,起草博客文章,编码等等。或者,如果他们的MP3播放器突然死于外面,他们会感到高兴。 -memory意味着他们的磁盘停止颠簸,他们可以回到他们的文字处理器并点击“保存”。

至于OP的原始问题,我强烈建议不要编写在失败时死掉的malloc包装器,或编写只是假设在malloc失败时使用空指针时会立即发生段错误的代码。 这是一个容易上手的坏习惯,一旦你编写了充满未经检查的分配的代码,以后在任何鲁棒性都很重要的程序中重用该代码是不可能的。

一个更好的解决方案就是保持对调用函数的返回失败,并让调用函数返回其调用函数等失败,直到你一直回到main或类似函数,在那里你可以编写if (failure) exit(1); 。 这样,在您可能实际想要检查错误并采取某种恢复步骤以释放内存,将有价值的数据保存/转储到磁盘等的其他情况下,代码可立即重用。

我认为这是一个坏主意,因为首先检查malloc的返回并不会在现代系统上给你带来太大的影响,其次因为这会给你错误的安全性,当你使用这样的电话时,所有的分配都没问题。

(我假设你是为托管环境而写,而不是嵌入式,独立。)

具有大虚拟地址空间的现代系统将永远不会mallocrealloc返回(void*)0 ,如果参数是伪造的,也许。 你会遇到很多问题,很久以后当你的系统开始疯狂交换甚至用完交换时。

所以不,不检查这些function的返回,这没有多大意义。 相反,使用断言检查malloc的参数是否为0 (对于realloc如果两者同时为0 ),那么问题不在于mallocrealloc而是在调用它们的方式。