如何在C语言中检测malloc内存溢出并触发realloc避免未定义行为?
动态分配字符串的安全输入与扩容实现
问题描述
我正在尝试在C语言中处理动态分配的字符串输入,希望当输入字符串超出先前分配的内存时,调用
realloc()重新分配内存,而非进入未定义行为。以下是我的代码:
#include <stdio.h> int main() { char *text, *retext; text = malloc(10 * sizeof(*text)); scanf("%s", text); if(text == NULL) { printf("\nTry Again: "); retext = realloc(text, 20 * sizeof(*retext)); scanf("%s", retext); printf("\n%s", retext); free(retext); retext = NULL; } printf("\n%s", text); free(text); text = NULL; }我想要在text的长度(包含
\0)超过10时调用realloc()。但我了解到malloc()在内存溢出时不会返回NULL,而是直接进入未定义行为,即便超出10个字符程序看似仍能运行。我希望能够检测到这种即将触发未定义行为的时机,转而执行realloc()相关代码,该如何实现?
原代码的核心问题
你的代码存在两个致命逻辑错误:
if(text == NULL)判断的是**malloc内存分配是否失败**,和输入字符串长度是否超限完全无关。只要malloc成功,这个分支永远不会执行,输入溢出时根本不会触发扩容。- 使用
scanf("%s", text)本身就是不安全的:它会无限制读取输入直到遇到空白字符,完全无视text的内存大小,直接导致缓冲区溢出(未定义行为),此时再检测已经为时已晚。
正确实现思路
要实现安全的动态扩容输入,核心是在输入过程中主动监控已使用的内存,在达到当前分配上限前提前扩容,而非等溢出后处理。推荐两种可靠实现方式:
方式一:逐字符读取(精准控制)
逐个读取输入字符,每存入一个字符就检查当前已使用内存是否接近上限,达到上限时调用realloc扩容,直到遇到终止字符(换行或EOF)。
#include <stdio.h> #include <stdlib.h> int main() { // 初始分配10字节内存(含字符串结束符\0) size_t current_size = 10; char *text = malloc(current_size); if (text == NULL) { fprintf(stderr, "内存分配失败\n"); return 1; } size_t length = 0; int ch; // 逐字符读取输入,直到换行或EOF while ((ch = getchar()) != '\n' && ch != EOF) { // 检查:当前已存字符数+1(当前字符)+1(\0)是否超过分配内存 if (length + 2 > current_size) { // 扩容策略:每次翻倍,减少realloc调用次数 current_size *= 2; char *temp = realloc(text, current_size); if (temp == NULL) { fprintf(stderr, "内存扩容失败\n"); free(text); return 1; } text = temp; } text[length++] = (char)ch; } // 添加字符串结束符 text[length] = '\0'; // 输出结果 printf("输入内容:%s\n", text); printf("内容长度:%zu\n", length); printf("当前分配内存大小:%zu\n", current_size); // 释放内存 free(text); text = NULL; return 0; }
关键细节说明
- 必须检查
malloc和realloc的返回值:避免空指针操作,同时防止realloc失败时丢失原内存地址(用临时指针接收返回值)。 - 扩容判断逻辑:
length + 2 > current_size确保预留出存储当前字符和最终\0的空间,不会触发溢出。 - 翻倍扩容策略:这是行业常用的高效策略,减少内存分配的系统调用次数,提升性能。
方式二:使用fgets结合截断检查
fgets可以指定最大读取长度,我们通过判断输入是否被截断(缓冲区满且无换行符)来触发扩容,然后继续读取剩余内容。
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { size_t current_size = 10; char *text = malloc(current_size); if (text == NULL) { fprintf(stderr, "内存分配失败\n"); return 1; } char *newline_pos; while (1) { // 读取最多current_size-1个字符(留一个位置给\0) if (fgets(text, current_size, stdin) == NULL) { break; // 读取失败或遇到EOF } // 检查输入是否完整:查找换行符 newline_pos = strchr(text, '\n'); if (newline_pos != NULL) { // 去掉换行符,输入完成 *newline_pos = '\0'; break; } else { // 无换行符,说明输入被截断,需要扩容 current_size *= 2; char *temp = realloc(text, current_size); if (temp == NULL) { fprintf(stderr, "内存扩容失败\n"); free(text); return 1; } text = temp; } } printf("输入内容:%s\n", text); free(text); text = NULL; return 0; }
内容的提问来源于stack exchange,提问作者Abdullah Omar Nasseef
相关产品推荐
相关产品推荐

