C语言AVL树加载CSV插入节点出现垃圾值问题排查
故障定位结论
手动插入、删除、打印树功能正常,说明AVL树旋转、平衡维护、根指针传递的核心逻辑没有问题。插入CSV数据时出现的“插入前字段值正常、插入后提示元素已存在(匹配到垃圾值)、ID为异常整数、字符串为乱码”现象,本质是内存生命周期不匹配+浅拷贝导致的野指针问题,和树的核心操作逻辑无关。
具体触发场景与修复方案
场景1:结构体字符串成员直接存储临时缓冲区指针,未做深拷贝
逐行解析CSV时通常会复用同一个栈上的行缓冲区(比如char line[512])存每行读入的内容,解析出的姓名字段、分类字段等字符串如果直接把缓冲区的地址赋值给Avl_info的char*类型成员,当下一轮读行覆盖缓冲区内容、或是加载函数执行结束释放栈内存后,节点中存储的指针就会指向已被覆写/回收的内存,读出的值自然是乱码或垃圾值。
修复方式:给每个字符串字段单独分配堆内存,将解析到的内容完整拷贝到堆上,不要直接存储临时变量的地址:// 错误写法:仅存临时指针 csv_aux.product_name = parsed_name; // 正确写法:深拷贝到堆内存 csv_aux.product_name = malloc(strlen(parsed_name) + 1); strcpy(csv_aux.product_name, parsed_name);场景2:AVL节点仅存储外部结构体的指针,未拷贝结构体完整内容
如果循环插入时每次都把同一个csv_aux变量的地址传给insert函数,创建节点时只把这个地址存在节点里,没有把csv_aux存储的实际数据复制到节点自身的内存空间,那么循环过程中csv_aux被反复覆写、加载函数执行结束后csv_aux所在的栈内存被回收,所有节点存储的指针都会指向无效内存,不仅会读出乱码,还可能因为随机垃圾值和已有节点的ID匹配,误报“元素已存在”。
修复方式:创建新节点时,单独为节点分配堆内存,将传入的结构体数据完整拷贝到节点内存中,不要仅存储外部传入的指针:AvlNode* create_node(Avl_info* data) { AvlNode* node = malloc(sizeof(AvlNode)); // 错误写法:直接存储外部指针 // node->data = data; // 正确写法:完整拷贝结构体内容 memcpy(&node->data, data, sizeof(Avl_info)); node->left = node->right = NULL; node->height = 1; return node; }注意:如果
Avl_info内部包含char*类型的字符串成员,memcpy只会拷贝指针地址,必须配合场景1的字符串深拷贝逻辑使用,否则依然会出现野指针问题。场景3:存储CSV解析结果的数组为栈上局部变量,生命周期短于AVL树
如果csv_aux数组是定义在load_file_test函数内部的局部栈数组,插入节点时没有把数组元素的内容拷贝到节点,而是直接存储数组元素的地址,当load_file_test执行完毕栈帧销毁后,这块内存会被系统回收复用,节点访问时就会读到垃圾值。
快速验证方法
在两个位置加打印即可快速定位具体出错点:
- 在insert函数入口处,立刻打印传入的待插入数据的所有字段
- 单条数据插入完成、回到调用方逻辑后,立刻读取刚插入的节点内容打印
- 如果insert入口处打印就已经是乱码:说明传参前内存已经失效,检查待插入数据所在的变量是不是临时缓冲区、是不是已经被释放
- 如果insert入口打印正常,回到调用方后打印乱码:说明节点创建时没有做数据深拷贝,检查节点初始化的内存拷贝逻辑
补充说明:手动插入测试正常是因为测试用的结构体通常定义为全局变量、或是main函数内的局部变量——main函数的栈内存会持续到程序退出才释放,不会触发生命周期不匹配的问题,这也是两类测试场景表现不一致的核心原因。
内容的提问来源于stack exchange,提问作者Vinnicius Stein

