未对malloc做失败保护会引发哪些错误?malloc堆分配失败原因
两种malloc调用写法对比
// 风险写法:未做malloc失败校验 char *str = malloc(1000); // that is ok // do somthing ... free(str); // 安全写法:添加malloc失败校验逻辑 char *str = malloc(1000); if (!str) return (0); //do something ... free (str);
注意:第一段代码里的
// that is ok属于错误认知,哪怕申请的内存块很小,不做返回值校验都存在运行风险。
为什么malloc会出现堆内存分配失败
malloc本身不直接分配物理内存,它是先在进程的虚拟地址空间里找一段满足长度要求的连续空闲地址块,再和内核交互完成内存映射,只要这个流程里任意一步走不通,就会返回NULL,常见触发场景:
- 进程地址空间不足或碎片化严重。最典型的是32位程序,用户态可用的虚拟地址总共只有2~3G,如果程序长时间运行频繁申请、释放内存,堆区会产生大量零散的小空闲块,哪怕所有空闲块的总大小远大于申请长度,只要凑不出要求的连续块,就会分配失败。64位程序虽然虚拟地址空间极大,但如果通过ulimit、cgroup给进程设置了内存使用上限,碰到阈值后再申请内存也会失败。
- 系统整体内存资源耗尽。当物理内存+交换分区/交换文件的空间全被占满,且内核关闭了内存超售(overcommit)策略时,新的内存申请会被直接拒绝。就算开启了内存超售,如果单次申请的内存块大到超出内核允许的超售阈值,一样会分配失败。
- 申请参数非法。如果计算申请长度时出现整数溢出,传给malloc的size值变成接近
SIZE_MAX的异常超大值,堆管理器会直接判定请求非法,返回NULL。
不做malloc失败校验会触发的运行时错误
不要抱有“我只申请1000字节,不可能失败”的侥幸,内存分配失败在内存紧张的嵌入式设备、长时间运行的服务进程里是很常见的场景,不做校验会触发各类问题:
- 进程当场崩溃。拿到
NULL指针后如果直接读写指针指向的内存(比如执行str[0] = 0、strcpy(str, "test")这类操作),会立刻触发段错误(SIGSEGV),进程被操作系统强制终止。 - 故障排查成本极高。如果拿到
NULL后没有立刻使用,而是把它存到结构体成员、全局变量里,传递给多层调用后的下游逻辑,可能程序运行几十分钟甚至几小时后,才在和malloc调用点完全无关的代码位置触发崩溃,很难定位到根因。 - 连锁资源泄漏。malloc失败后如果没有走专门的错误处理分支,之前已经申请成功的文件描述符、互斥锁、其他内存块可能因为流程提前中断,没有走到对应的释放逻辑,进程运行时间越长,泄漏的资源越多,最终会拖垮整个系统的资源可用性。
- 触发不可预判的未定义行为。C标准明确规定,对空指针做偏移、解引用,或者把空指针传给要求参数非空的标准库函数(比如
strcpy、memcpy、fprintf等)都属于未定义行为,轻则崩溃,重则出现内存数据被篡改、程序执行流跑飞的极端情况,没有任何稳定的行为预期。
内容的提问来源于stack exchange,提问作者Mittous Issmail
相关产品推荐
相关产品推荐

