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

C语言fgets读取异常求助——Advent of Code 2022 Day1

问题分析与修复:Advent of Code 2022 Day1 解析异常

核心bug原因

你的代码存在栈数组越界访问,这是导致随机垃圾值和line变量被意外修改的根本原因:

  • 初始化时counter值为0,struct kv_store kv[counter];创建了长度为0的数组
  • 后续循环中往kv[counter]写入数据时,完全超出数组合法内存范围,属于非法内存访问
  • 这种越界操作会破坏栈上的其他变量(比如你的line缓冲区),因此出现特定行数据被篡改的奇怪现象

修复步骤

1. 替换非法的数组定义

把固定大小的栈数组替换为动态分配的数组(或者直接定义一个足够大的静态数组,比如struct kv_store kv[10000];),彻底避免越界:

// 替换原来的struct kv_store kv[counter];
struct kv_store *kv = NULL;
int kv_capacity = 0;

在循环写入前检查容量,不足时自动扩容:

else
{
    // 动态扩容数组
    if (counter >= kv_capacity) {
        kv_capacity = kv_capacity == 0 ? 100 : kv_capacity * 2;
        kv = realloc(kv, sizeof(struct kv_store) * kv_capacity);
        if (!kv) {
            perror("realloc failed");
            fclose(fd);
            return 1;
        }
    }
    kv[counter].key = elfno;
    sscanf(line, "%d", &kv[counter].value);
    counter ++;
    continue;
}

2. 修复空行判断逻辑

原来的strlen(line) ==1仅能识别Unix格式空行(仅含\n),如果是Windows格式换行(\r\n)会判断失败。替换为更鲁棒的空白行检查:

#include <ctype.h> // 需要包含该头文件用于isspace函数

// 循环内替换空行判断逻辑
if(strlen(line) == 0) continue;
// 跳过行首所有空白字符
char *ptr = line;
while (isspace((unsigned char)*ptr)) ptr++;
// 跳过空白后为空字符串,说明是空行
if (*ptr == '\0') {
    elfno ++;
    continue;
}

3. 补充资源释放

动态分配的内存和打开的文件需要在程序结束前释放/关闭,避免资源泄漏:

// 在return 0前添加
free(kv);
fclose(fd); // 原代码未关闭文件,需补上

修复后的完整代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define IN_FILE "input.txt"
#define MAX_LINE_LEN 20480

int main(int argc, char *argv[])
{
    char line[MAX_LINE_LEN];
    int counter = 0;
    int elfno = 0;
    FILE *fd;

    struct kv_store
    {
        int key;
        int value;
    };

    struct kv_store *kv = NULL;
    int kv_capacity = 0;

    fd = fopen(IN_FILE, "r");
    if (fd == NULL)
    {
        fprintf(stdout, "\n[*]%s not found in current working directory.", IN_FILE);
        return -1;
    }

    while(fgets(line, MAX_LINE_LEN, fd) != NULL)
    {
        if(strlen(line) == 0) continue;
        char *ptr = line;
        while (isspace((unsigned char)*ptr)) ptr++;
        if (*ptr == '\0') {
            elfno ++;
            continue;
        }
        else
        {
            if (counter >= kv_capacity) {
                kv_capacity = kv_capacity == 0 ? 100 : kv_capacity * 2;
                kv = realloc(kv, sizeof(struct kv_store) * kv_capacity);
                if (!kv) {
                    perror("realloc failed");
                    fclose(fd);
                    return 1;
                }
            }
            kv[counter].key = elfno;
            sscanf(line, "%d", &kv[counter].value);
            counter ++;
            continue;
        }
    }

    for(int i=0;i < counter;i++)
    {
        fprintf(stdout, "Iteration: %d - Elf: %d holds: %d \n", i, kv[i].key, kv[i].value);
    }

    free(kv);
    fclose(fd);
    return 0;
}

额外说明

  • 栈内存越界的表现通常具有随机性,刚好在读取6086时触发了明显的内存破坏,本质是非法访问覆盖了line缓冲区的内容
  • 动态数组扩容的方式可适配任意大小的输入文件,比静态数组更灵活
  • 始终记得释放动态分配的内存和关闭打开的文件,避免资源泄漏

内容的提问来源于stack exchange,提问作者tyrone 1988

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 13:05:15