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

C语言union类型双链表旧值被覆盖问题排查与解决

问题描述

需求为从文件读取结构化数据,构建以union联合体为节点数据类型的双向链表,以下核心结构定义保持固定不修改:

// 支持存储int整型、double浮点型、char*字符串三种类型的联合体
typedef union val {
    int int_val;
    double dbl_val;
    char *str_val;
} val_t;

// 带前驱、后继指针的双向链表节点
typedef struct node {
    struct node *prev;
    struct node *next;
    val_t *vals;
} node_t;

// 链表结构,存储头尾节点与元信息
typedef struct list {
    node_t *head;
    node_t *tail;
    size_t size;
} list_t;

// 表头结构,存储字段数量、类型、名称
typedef struct header {
    int field_cnt;
    int *field_type;
    char **field_name;
} header_t;

初始实现包含尾插法节点插入函数insert、逐行解析文件内容的read_file函数,运行时出现稳定复现的异常:每次向链表插入新节点时,之前已经插入的历史节点存储的值会被异常篡改,最终遍历链表时所有节点的值都和最后一条读入的记录一致。

根因分析

问题出在内存分配的位置错误:
初始错误写法中,用于缓存单条记录的val_t类型value数组的calloc内存分配操作写在了文件读取循环的外部,直接导致所有链表节点的vals指针都指向同一块堆内存地址。循环中每次写入新解析的文件数据时,都会直接覆盖该内存地址上存储的旧值——所有节点因为指向同一块内存,读出来的自然都是最后一次写入的内容,看起来就像历史值被“篡改”了。

修复方案

核心修复逻辑三点:

  • 将value数组的calloc内存分配操作移动到每次读取单条记录的循环内部,为每一个待插入的节点分配独立的内存空间,从根源上避免多节点共享内存的问题
  • 补全内存分配失败的校验逻辑,出现分配错误时及时回滚、回收已申请的资源,避免内存泄漏
  • 针对char*字符串类型字段,配套完善节点销毁、解析失败场景下的字符串内存回收逻辑,避免野指针和内存泄漏

错误实现核心片段

// 问题代码:内存分配在循环外
int read_file(list_t *list, header_t *header, const char *file_path) {
    FILE *fp = fopen(file_path, "r");
    if (!fp) return -1;
    char line_buf[2048];
    // 错误点:所有循环迭代共用同一块value内存
    val_t *value = (val_t*)calloc(header->field_cnt, sizeof(val_t));
    if (!value) {
        fclose(fp);
        return -1;
    }
    while (fgets(line_buf, sizeof(line_buf), fp)) {
        // 逐字段解析行内容写入value数组
        parse_line_to_val(line_buf, header, value);
        // 节点内vals指针直接指向全局共用的value
        insert_node_to_tail(list, value);
    }
    fclose(fp);
    return 0;
}

修复后正确实现核心片段

// 修复代码:每次循环单独分配内存
int read_file(list_t *list, header_t *header, const char *file_path) {
    FILE *fp = fopen(file_path, "r");
    if (!fp) return -1;
    char line_buf[2048];
    while (fgets(line_buf, sizeof(line_buf), fp)) {
        // 修复点:为当前待插入的单条记录分配独立内存
        val_t *value = (val_t*)calloc(header->field_cnt, sizeof(val_t));
        if (!value) {
            fclose(fp);
            list_destroy(list, header); // 销毁已插入节点,回收所有内存
            return -1;
        }
        // 逐字段解析行内容写入当前独立的value数组
        if (parse_line_to_val(line_buf, header, value) != 0) {
            free_val_record(value, header); // 先释放记录内的字符串堆内存
            free(value);
            continue;
        }
        // 每个节点持有独立的value内存,互不干扰
        insert_node_to_tail(list, value);
    }
    fclose(fp);
    return 0;
}

说明:修复前后的代码均保留原始C语言语法、关键字、标准库函数名,未修改原有核心结构定义与对外函数接口。

内容的提问来源于stack exchange,提问作者Joki_004

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 19:27:19