C语言中realloc()扩容int数组后值异常,Valgrind结果正常求助
Advent of Code 2022 Day1 内存分配异常问题
我在做2022年Advent of Code第一天的题目,需求是读取txt文件,将空行分隔的每组数字求和后存入int数组。为了按需分配内存,我在循环里使用realloc(),但运行时输出的数组值异常,不过用Valgrind运行代码却能得到正确结果。
输入文件示例
7569 1357 10134 4696 4423 8869 3562 6597 4038 9038 1352 8005 4811 6281 3961 4023 7234 3510 7728 1569 4583 7495 3941 6015 6531 2637
预期输出
elf [0] = 47207 elf [1] = 41509 elf [2] = 51243
实际错误输出
elf [245] = 63138 elf [246] = 181168 elf [247] = 41570 elf [248] = 36264 elf [249] = 59089 elf [250] = 185061
Valgrind下的正确输出
elf [245] = 63138 elf [246] = 52399 elf [247] = 41570 elf [248] = 36264 elf [249] = 59089 elf [250] = 56308
我的代码
int *read_calories(char *filename) { FILE *fp = fopen(filename, "r"); char *line = NULL; int i = 0; size_t len = 0; ssize_t nread; struct stat size; stat(filename, &size); int tab_size = 1; int *calories = malloc(sizeof(int) * 2); if (fp == NULL) { perror("Can't open file\n"); exit(EXIT_FAILURE); } while ((nread = getline(&line, &len, fp)) != -1) { if (nread == 1) { i++; ++tab_size; calories = realloc(calories, tab_size * sizeof(int)); } else { calories[i] += atoi(line); } } calories[i + 1] = '\0'; free(line); fclose(fp); return calories; } int main() { int *calories = read_calories("input.txt"); for (int i = 0; calories[i] != '\0'; i++) { printf("elf [%d] = %d \n", i, calories[i]); } free(calories); return 0; }
问题分析与修复方案
核心问题点
- 未初始化数组元素:
malloc和realloc分配的内存是未初始化的垃圾值,直接执行calories[i] += atoi(line)会累加随机值,这就是运行时输出异常的原因。Valgrind会自动将未初始化内存置0,所以能得到正确结果。 - 空行判断逻辑不全:
nread == 1只能匹配单一换行符,如果空行包含空格或其他空白字符,判断会失效。 - realloc无错误处理:直接将
realloc结果赋值给原指针,若分配失败会导致原内存地址丢失,引发泄漏。 - 终止符位置与类型错误:用字符
'\0'作为int数组的终止符不够规范,且循环结束后终止符的位置计算有误。 - 冗余代码:调用
stat获取文件大小但未使用,属于无效代码。
修复后的代码
#include <stdio.h> #include <stdlib.h> #include <string.h> int *read_calories(char *filename) { FILE *fp = fopen(filename, "r"); char *line = NULL; int i = 0; size_t len = 0; ssize_t nread; int tab_size = 1; int *calories = malloc(sizeof(int) * tab_size); if (fp == NULL) { perror("Can't open file"); exit(EXIT_FAILURE); } // 初始化第一个elf的总和为0 if (!calories) { perror("malloc failed"); fclose(fp); exit(EXIT_FAILURE); } calories[i] = 0; while ((nread = getline(&line, &len, fp)) != -1) { // 去掉末尾换行符,处理空行 if (nread > 0 && line[nread - 1] == '\n') { line[nread - 1] = '\0'; } if (strlen(line) == 0) { i++; tab_size++; // 用临时变量接收realloc结果,避免内存泄漏 int *temp = realloc(calories, tab_size * sizeof(int)); if (!temp) { perror("realloc failed"); free(calories); free(line); fclose(fp); exit(EXIT_FAILURE); } calories = temp; // 初始化新elf的总和为0 calories[i] = 0; } else { calories[i] += atoi(line); } } // 分配额外空间存储终止符0 int *temp = realloc(calories, (tab_size + 1) * sizeof(int)); if (temp) calories = temp; calories[i + 1] = 0; // 用int类型的0作为终止符 free(line); fclose(fp); return calories; } int main() { int *calories = read_calories("input.txt"); if (!calories) exit(EXIT_FAILURE); for (int i = 0; calories[i] != 0; i++) { printf("elf [%d] = %d \n", i, calories[i]); } free(calories); return 0; }
关键修复说明
- 每次新增elf时,初始化对应数组元素为0,避免累加垃圾值。
- 优化空行判断:先去掉换行符再检查字符串长度,覆盖所有空行场景。
- 对
realloc添加错误处理,防止内存泄漏和空指针访问。 - 用int类型的0作为数组终止符,逻辑更清晰。
- 初始化第一个elf的总和为0,确保第一组数字从0开始累加。
内容的提问来源于stack exchange,提问作者aisezc
相关产品推荐
相关产品推荐

