代码之家  ›  专栏  ›  技术社区  ›  helpermethod

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

c
  •  3
  • helpermethod  · 技术社区  · 15 年前

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

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

    程序只从一个文件读取数据并输出它,所以我认为退出程序时,当(re m)分配失败时,这是个好主意。现在,真正的问题是:

    这样结束通话是否有益?

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

    或者这是个坏主意?

    6 回复  |  直到 15 年前
        1
  •  3
  •   Community Mohan Dere    9 年前

    Should I bother detecting OOM (out of memory) errors in my C code?

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

        2
  •  4
  •   T.J. Crowder    15 年前

    这不是一个好主意,因为 其他 比起为一个任务编写的简单程序,您希望做一些比释放更有用/更优雅的事情。所以最好不要养成坏习惯。这并不是说围绕分配的包装器是坏的 本身 (集中错误处理可能是一件好事),只是不检查返回值(例如,失败时根本不返回)的包装是一个坏主意,除非您提供了某种机制,允许代码钩住纾困逻辑。

    如果你真的想用你所展示的形式,我强烈建议你用一个比 Malloc 而不是 malloc . 就像 malloc_or_die . :-)

        3
  •  4
  •   Marcelo Cantos    15 年前

    在大多数情况下,使用空指针的尝试很快就会使程序崩溃,而且调试起来也会更容易,因为您可以得到一个很好的内核转储文件,如果调用 exit() .

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

    你很少能从记忆衰竭中恢复过来,所以退出通常是正确的事情。但这样做是为了让验尸更容易。

    不过,为了清楚起见,内存耗尽通常发生在操作系统被页面交换活动破坏很久之后。这个策略实际上只对捕捉荒谬的分配有用,比如由于一个bug尝试malloc(一个小的负的数)。

        4
  •  3
  •   ruslik    15 年前

    对你来说没关系。请记住给出一个提前退出的消息,最好指定行号。有人这样想:

    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__)
    

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

        5
  •  1
  •   R.. GitHub STOP HELPING ICE    15 年前

    “检查 malloc 因为失败是没有用的,因为“或者”操作系统已经被时间破坏了 马洛克 “失败”已经严重过时。健壮的操作系统从来没有过度使用内存,而历史上不那么健壮的操作系统(如Linux)现在有简单的方法来禁用overcommit并防止操作系统因内存耗尽而瘫痪- 只要应用程序尽了自己的职责,在 马洛克 失败!

    有很多原因 马洛克 在现代系统上可能会失败:

    • 物理资源不足,无法实例化内存。
    • 虚拟地址空间耗尽,即使有足够的物理内存可用。在32位计算机(或32位用户空间)上,使用4gb ram+交换很容易发生这种情况。
    • 内存碎片。如果您的分配模式非常糟糕,那么最终可能会有400万个16字节的块平均间隔1000字节,并且无法满足 malloc(1024) 打电话来。

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

    当然,从整个系统的健康角度来看,您的程序死了很好。这减少了资源匮乏,并可能允许其他应用程序继续运行。另一方面,如果这意味着编辑视频、打字、起草博客文章、编码等工作时间的减少,用户会非常不安;或者,如果他们的mp3播放器突然因内存不足而死机,意味着他们的磁盘停止跳动,并且他们能够返回文字处理器并单击“保存”,用户可能会很高兴。

    至于OP最初的问题,我强烈建议不要写 马洛克 包装器在失败时死亡,或者编写代码时假设它在使用空指针时会立即segfault,如果 马洛克 失败。这是一个很容易养成的坏习惯,一旦您编写了充满未检查分配的代码,以后就不可能在任何程序中重用该代码,因为健壮性很重要。

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

        6
  •  0
  •   Jens Gustedt    15 年前

    我认为这是个坏主意,因为首先要检查 malloc 在现代系统上买不到多少东西,其次,这给了你错误的安全性,当你使用这样的调用时,你所有的分配都很好。

    (我假设您是为托管环境而非嵌入式独立环境编写的。)

    拥有巨大虚拟地址空间的现代系统 从未 返回 (void*)0 马洛克 realloc 分开,也许,如果争论的地方是假的。当你的系统开始疯狂地交换,甚至用完交换时,你会遇到很多问题。

    所以不,不要检查这些函数的返回,这没什么意义。相反,请检查 马洛克 反对 0 (以及 重新分配 如果两者都是 同时)与断言一起,从那时起问题就不在内部 马洛克 重新分配 但你称呼他们的方式。