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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 04:53:32