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
相关产品推荐
相关产品推荐

