C语言二叉树通讯录内存分配与文件加载问题求助
二叉树通讯录TXT加载:内存乱码与fscanf读取失败问题修复
Hey there! 作为刚接触编程的大二同学,能自己动手实现二叉树通讯录已经超棒了!咱们来一步步拆解你遇到的问题,搞定内存乱码和fscanf读取失败的麻烦~
核心问题分析
1. 初始化后的乱码是正常现象,但fscanf用法彻底错了
你在creatnullnode里用malloc分配的内存是未初始化的堆内存,里面本来就是随机的垃圾值(就是你看到的ÍÍÍÍÍÍÍÍ...),这其实是正常的——只要后续用fscanf写入数据就能覆盖。但你犯了一个致命错误:
在
fscanf里给node->firstname这类char指针加了&!
node->firstname本身就是char*类型,直接传给fscanf的%s参数就对了。你加了&之后,相当于把char**(指针的地址)传进去,这会直接覆盖你之前malloc分配的内存地址,导致指针指向完全错误的内存区域,自然会出现<Error reading the characters of the string.>的错误。
2. 行数统计与文件跳转的逻辑混乱
- 用
while(!feof(fp))统计行数会多算一行(因为feof是在读取失败后才会置位),正确的做法是用fgetc的返回值判断是否到文件末尾。 fseek(fp, 0,(int)lines % 2);这行完全没意义,你应该用SEEK_SET(或者0)直接回到文件开头,而且你后面又调用了fseek(fp,0,0),逻辑重复且混乱。char ch = "";是类型错误:""是字符串常量的地址,不能赋值给char类型,应该写成char ch;。
3. 节点加载的循环逻辑缺失
你现在的代码只尝试加载了一个节点,没有实现循环加载所有行的逻辑,这显然无法重建完整的二叉树。
修复后的代码片段
1. 修正creatnullnode:加内存检查与初始化(可选但友好)
#define CLEN 100 struct binarytree { struct binarytree *left; struct binarytree *right; char *firstname; char *lastname; char *city; char *street; char *addr; char *tel; } typedef btree; btree *creatnullnode(void) { btree *node = malloc(sizeof(btree)); if (!node) { // 必须检查malloc是否成功 perror("Failed to allocate memory for tree node"); return NULL; } node->left = NULL; node->right = NULL; // 为每个字符串分配内存,并检查分配结果 node->firstname = malloc(CLEN * sizeof(char)); node->lastname = malloc(CLEN * sizeof(char)); node->city = malloc(CLEN * sizeof(char)); node->street = malloc(CLEN * sizeof(char)); node->addr = malloc(CLEN * sizeof(char)); node->tel = malloc(CLEN * sizeof(char)); // 可选:初始化内存为0,避免调试时看到乱码 if (node->firstname) memset(node->firstname, 0, CLEN); if (node->lastname) memset(node->lastname, 0, CLEN); if (node->city) memset(node->city, 0, CLEN); if (node->street) memset(node->street, 0, CLEN); if (node->addr) memset(node->addr, 0, CLEN); if (node->tel) memset(node->tel, 0, CLEN); return node; }
2. 修正loadtree:修复fscanf、文件跳转与循环逻辑
btree *loadtree(char *filename) { FILE *fp; btree *tree = NULL; char ch; int lines = 0; fp = fopen(filename,"r"); if (!fp) { perror("Error opening contact file"); // 用perror输出更清晰的错误信息 return NULL; } // 正确统计文件行数 while ((ch = fgetc(fp)) != EOF) { if (ch == '\n') lines++; } // 回到文件开头,准备读取数据 fseek(fp, 0, SEEK_SET); // 逐行加载每个节点 for (int i = 0; i < lines; i++) { btree *node = creatnullnode(); if (!node) { printf("Skipping line %d: Failed to create node\n", i+1); continue; } // 关键:去掉&符号,直接传char指针给%s // %s会自动跳过空白字符(空格、制表符、换行),适配大部分文件格式 int read_count = fscanf(fp, "%s %s %s %s %s %s", node->firstname, node->lastname, node->addr, node->city, node->street, node->tel); // 检查是否成功读取6个字段 if (read_count != 6) { printf("Warning: Line %d has invalid format, skipping\n", i+1); // 释放当前节点的内存,避免泄漏 free(node->firstname); free(node->lastname); free(node->city); free(node->street); free(node->addr); free(node->tel); free(node); continue; } // 插入节点到二叉树 tree = insertnode(tree, node); } fclose(fp); return tree; }
额外提示
- 内存泄漏防范:如果加载过程中某个节点分配失败或格式错误,一定要释放已经分配的内存,避免内存泄漏。
- fscanf格式适配:如果你的TXT文件里字段是用制表符分隔的,
%s依然有效,因为它会自动跳过所有空白字符;如果字段包含空格,那你需要用fgets配合sscanf来处理。 - 始终检查系统调用的返回值:malloc、fopen、fscanf这些函数都可能失败,检查返回值能帮你快速定位问题。
内容的提问来源于stack exchange,提问作者Pleasure
相关产品推荐
相关产品推荐

