使用fscanf()读取txt构建图结构时出现垃圾值与程序崩溃问题
图读取问题根源与修正方案
核心问题定位
- fscanf 循环判断逻辑完全错误
现有循环仅判断返回值是否等于EOF,忽略了fscanf的返回值规则:该函数返回成功匹配并赋值的参数个数,而非仅返回EOF。当遇到空行、行尾多余空白、格式不匹配的内容时,返回值会是1/2而非EOF,循环不会终止,反而会带着未初始化的残留变量值继续执行addEdge,最终因传入非法节点编号触发内存越界崩溃。这就是grafo.txt读取到一半直接闪退的核心原因。 - 未按文件声明的边数控制读取流程
读取循环没有使用文件首行解析出的edges值作为读取上限,只要未碰到文件结束符就持续读取,完全忽略文件格式约束。哪怕文件末尾多一个空行、半行数字,都会触发读值错误。 - 邻接表初始化存在内存未清零问题
initialize函数通常会给每个节点的邻接表维护一个当前已用长度的计数器,如果这个计数器在初始化时没有显式置0,就会带上栈内存的随机值。addEdge写邻接表、print读邻接表的时候都会从这个随机下标开始操作,读到的就是栈内存的垃圾值——这就是grafo1.txt中Node3邻接权重出现超大负数值的根本原因。 - 未做节点编号合法性校验
解析出的fromNode/toNode没有校验是否在0 ~ nodes-1的合法范围内,一旦读值异常传入越界编号,就会直接访问邻接表数组的非法内存地址,触发崩溃。
具体修正实现
1. 重写读取循环逻辑
替换原有的错误循环:
int edgeCount = 0; // 严格按照文件声明的边数循环,不依赖EOF判断 while (edgeCount < edges) { int readRet = fscanf(filePointer, "%d %d %f", &fromNode, &toNode, &weight); // 校验是否完整读取了三个边参数 if (readRet != 3) { fprintf(stderr, "[READ-GRAPH] 第%d条边格式不合法,实际读取返回值%d\n", edgeCount + 1, readRet); fclose(filePointer); // 初始化的图结构需要手动释放,避免内存泄漏 destroyGraph(graph); return 0; } // 校验节点编号合法性 if (fromNode < 0 || fromNode >= nodes || toNode < 0 || toNode >= nodes) { fprintf(stderr, "[READ-GRAPH] 第%d条边存在越界节点:%d -> %d,总节点数%d\n", edgeCount + 1, fromNode, toNode, nodes); fclose(filePointer); destroyGraph(graph); return 0; } addEdge(graph, fromNode, toNode, weight); edgeCount++; }
这种写法完全不受文件末尾多余空行、多余内容的影响,严格按照声明的边数完成读取,从根源上避免了格式异常导致的循环死锁。
2. 补全邻接表初始化的清零逻辑
initialize函数必须确保所有运行时状态被显式置0,不能依赖栈内存默认值:
void initialize(Graph* graph, int nodeNum) { graph->nodeNum = nodeNum; // 为每个节点分配邻接表,无向图单节点最多连通nodeNum-1条边 graph->adjList = (AdjNode**)malloc(sizeof(AdjNode*) * nodeNum); graph->adjLen = (int*)malloc(sizeof(int) * nodeNum); for (int i = 0; i < nodeNum; i++) { graph->adjList[i] = (AdjNode*)malloc(sizeof(AdjNode) * (nodeNum - 1)); // 关键:邻接表已用长度必须显式置0,不能留随机值 graph->adjLen[i] = 0; } }
这步修正后,grafo1.txt中Node3邻接的垃圾权重值会完全消失,所有边的权重都会正确显示。
3. 错误处理补全
原有的文件打开失败逻辑、函数返回值逻辑需要补全:
fopen失败时直接返回,不需要执行后续关闭、初始化操作- 读取流程中任何一步失败,都需要先关闭文件指针、释放已初始化的图内存,再返回错误码,避免内存泄漏
为什么两个同格式文件报错表现不同
grafo.txt的异常是流程类错误:读取过程中遇到行尾多余空白,触发fscanf返回非3、非EOF的状态,进入异常循环后传入越界节点编号,直接触发段错误崩溃。grafo1.txt的异常是初始化类错误:邻接表的长度计数器没有置0,print阶段读到了栈内存的残留垃圾值,因为没有触发内存越界所以没有崩溃,只是输出了错误的权重。
内容的提问来源于stack exchange,提问作者padariadoce
相关产品推荐
相关产品推荐

