You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
}

问题分析与修复方案

核心问题点

  1. 未初始化数组元素:malloc和realloc分配的内存是未初始化的垃圾值,直接执行calories[i] += atoi(line)会累加随机值,这就是运行时输出异常的原因。Valgrind会自动将未初始化内存置0,所以能得到正确结果。
  2. 空行判断逻辑不全:nread == 1只能匹配单一换行符,如果空行包含空格或其他空白字符,判断会失效。
  3. realloc无错误处理:直接将realloc结果赋值给原指针,若分配失败会导致原内存地址丢失,引发泄漏。
  4. 终止符位置与类型错误:用字符'\0'作为int数组的终止符不够规范,且循环结束后终止符的位置计算有误。
  5. 冗余代码:调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 02:41:28