结构体嵌套内存访问违规及单向图节点分配、遍历问题求助
问题描述
我要实现的功能是:构建包含listNode的链表,每个listNode持有指向graphNode的指针,这些graphNode构成有向图,需要从指定起点随机遍历到名为"Home"的节点。
目前遇到的核心问题:
- 将图节点关联到链表节点的过程出错,疑似存在无限循环
- 读取结构体嵌套内容时触发内存访问违规(Memory access violations)
需要实现一个接收链表头节点、节点名称和路径权重的函数,完成图节点的正确关联,并解决遍历问题。
输入文件结构
Applebees GroundRound BlueMoose DrunkenNoodle Home STOP Applebees BlueMoose 10 Applebees DrunkenNoodle 13 GroundRound Applebees 2 GroundRound DrunkenNoodle 7 GroundRound Home 52 STOP STOP 0 GroundRound
现有代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> struct graphNode { char name[100]; // 场所名称 int arcCnt; // 该节点的出边数量 int weights[10]; // 每条出边的权重 struct graphNode* arcs[10]; // 存储该节点指向的其他图节点指针 }; struct listNode { char name[100]; // 场所名称 struct listNode* next; struct graphNode* graph; // 指向对应图节点的指针 }; void listInsert(struct listNode **head, char name[100]) { // 创建新的图节点 struct graphNode* newGraph = (struct graphNode*)malloc(sizeof(struct graphNode)); for (int i = 0; i < 100; i++) { newGraph->name[i] = name[i]; } for (int i = 0; i < 10; i++) { newGraph->arcs[i] = NULL; } newGraph->arcCnt = 0; // 创建新的链表节点 struct listNode* newNode = (struct listNode*)malloc(sizeof(struct listNode)); for (int i = 0; i < 100; i++) { newNode->name[i] = name[i]; } newNode->next = NULL; newNode->graph = newGraph; // 如果链表为空,直接作为头节点 if (*head == NULL) { *head = newNode; return; } // 否则添加到链表末尾 struct listNode* current = *head; while (current->next != NULL) { current = current->next; } current->next = newNode; } void graphInsert(struct listNode** head, char src[100], char dst[100], int weight) { struct listNode* srcNode = *head; printf("CALL:"); while (srcNode->next != NULL) { // 遍历链表查找源节点 printf("%s %s", srcNode->name, src); if (strcmp(srcNode->name, src) == 0) { // 找到源节点后,查找目标节点并更新图数据 printf("FOUND"); struct listNode* dstNode = *head; while (dstNode->next != NULL) { // 遍历链表查找目标节点 printf(" %s %s", dstNode->name, dst); if (strcmp(dstNode->name, src) == 0) { // 找到目标节点后更新信息 printf("FOUND"); // 根据当前出边数量分配新边,然后返回退出循环 srcNode->graph->arcs[srcNode->graph->arcCnt] = dstNode->graph; srcNode->graph->weights[srcNode->graph->arcCnt] = weight; srcNode->graph->arcCnt++; return; } dstNode = dstNode->next; } } srcNode = srcNode->next; } } int main(){ srand(2021); // 初始化变量 struct listNode* head = NULL; struct graphNode* sourceNode = NULL; FILE* data = fopen("./hw10.data", "r"); int i = 0; int section = 1; // 用于区分文件读取的不同阶段 // 读取文件内容 while (1) { char name[100]; char name2[100]; int weight = 0; if (section == 1) { // 读取第一部分:节点名称 fscanf(data, "%100s", name); if (strcmp(name, "STOP") == 0) { // 遇到STOP则进入下一阶段 section++; } else { // 否则将名称添加到链表 listInsert(&head, name); } } else if (section == 2) { // 读取第二部分:边的信息,构建图 fscanf(data, "%100s %100s %d", name, name2, &weight); if (strcmp(name, "STOP") == 0) { // 遇到STOP则进入下一阶段 section++; } else { //graphInsert(&head, name, name2, weight); } } else if (section == 3) { // 读取第三部分:起始节点名称 char tmp[100]; fscanf(data, "%100s", tmp); struct listNode* current = head; while (current->next != NULL) { // 遍历链表找到对应节点 if (strcmp(current->name, tmp) == 0) { // 匹配成功则记录起始图节点 sourceNode = current->graph; break; } current = current->next; } } if (feof(data)) break; i++; } // 调试打印链表节点 printf("\n"); struct listNode* current = head; while (current != NULL) { printf("%s\n", current->name); current = current->next; } printf("\n"); // 打印起始节点 printf("%s ", sourceNode->name); // 从起始节点开始随机遍历,直到到达"Home"节点 int totalWeight = 0; i = 0; while (i < 100) { char* tmp = sourceNode->name; if (strcmp(tmp, "Home") == 0) { // 到达Home则退出程序 // 打印终点和总权重 printf("%s %d", sourceNode->name, totalWeight); return 0; } else { // 未到达则继续遍历 int index = (rand() % sourceNode->arcCnt); // 生成0到出边数量-1的随机索引 sourceNode = sourceNode->arcs[index]; //printf("Going to: %s, Index: %d", sourceNode->name, totalWeight); } i++; } return 0; }
错误分析与修复方案
1. 链表遍历漏节点问题
- 问题:
graphInsert和main中查找节点的循环条件用了xxx->next != NULL,会漏掉链表最后一个节点,导致无法匹配末尾节点(比如输入中的"Home")。 - 修复:将所有遍历链表的循环条件改为
xxx != NULL,确保遍历所有节点:
// graphInsert中查找源节点 while (srcNode != NULL) { // ...原有逻辑 srcNode = srcNode->next; } // graphInsert中查找目标节点 while (dstNode != NULL) { // ...原有逻辑 dstNode = dstNode->next; } // main中查找起始节点 while (current != NULL) { // ...原有逻辑 current = current->next; }
2. 目标节点匹配错误
- 问题:
graphInsert中匹配目标节点时,误写为strcmp(dstNode->name, src) == 0,应该和dst比较。 - 修复:修改为
strcmp(dstNode->name, dst) == 0。
3. 字符串拷贝不安全
- 问题:
listInsert中用循环逐个拷贝字符,未处理字符串结束符\0,会导致字符串残留垃圾数据,引发匹配错误。 - 修复:用
strncpy安全拷贝,并手动添加结束符:
strncpy(newGraph->name, name, 99); newGraph->name[99] = '\0'; strncpy(newNode->name, name, 99); newNode->name[99] = '\0';
4. 未启用图边构建函数
- 问题:
main中graphInsert被注释,导致图的边未构建,arcCnt始终为0,rand()%0会触发未定义行为。 - 修复:取消注释
graphInsert(&head, name, name2, weight);。
5. 遍历未累加权重
- 问题:随机遍历过程中未累加路径权重,最终输出的
totalWeight始终为0。 - 修复:切换节点时累加对应边的权重:
int index = (rand() % sourceNode->arcCnt); totalWeight += sourceNode->weights[index]; sourceNode = sourceNode->arcs[index];
内容的提问来源于stack exchange,提问作者FunkyMunky
相关产品推荐
相关产品推荐

