使用strcmp比较字符串触发EXC_BAD_ACCESS的红黑树插入问题
我帮你仔细梳理了代码里的问题,这个EXC_BAD_ACCESS错误主要是由逻辑颠倒、内存使用不当和哨兵节点误用导致的,下面是具体分析和修复方案:
核心错误分析
1. 哨兵节点初始化与使用错误
你的哨兵节点sent在主函数里被初始化为left/right/parent全为NULL,但红黑树的哨兵应该作为所有叶子节点的统一替代(所有真实节点的左右子节点都指向哨兵)。第一次插入后,sent的left/right仍为NULL,后续循环中temp = sent->left会取到空指针,访问temp->id_prod直接触发内存错误。
另外,循环终止时temp == sent,你直接用strcmp比较新节点和哨兵的id_prod,但哨兵的id_prod从未初始化,读取未定义内存必然触发崩溃。
2. 插入路径的比较逻辑完全颠倒
在寻找插入位置的循环中:
if(strcmp(temp->id_prod, new_node->id_prod) < 0) { temp = temp->left; }
strcmp(a,b) < 0表示a的字典序小于b,也就是当前节点temp的ID比新节点小,按照红黑树左小右大的规则,新节点应该放在temp的右子树,但你却让temp移向left,这会导致循环走向错误分支,要么死循环,要么访问空指针。
3. 内存泄漏与指针赋值错误
你在insert_id开头执行了:
node *temp = malloc(sizeof(struct node)); temp = sent->left;
先分配内存给temp,立刻又让它指向sent->left,导致之前的内存地址丢失,造成内存泄漏。如果sent->left是NULL,temp就变成空指针,后续访问temp->id_prod直接崩溃。
4. 红黑树颜色规则错误
红黑树插入新节点时默认应该设为红色(根节点除外),你把所有新节点都设为黑色,会破坏“根到叶子路径黑色节点数相同”的性质,后续平衡修复也无法正常工作。
修复后的完整代码方案
1. 修正哨兵节点初始化
主函数里哨兵节点初始时应自身指向自身,避免空指针访问:
#define STORE "Magazzino.txt" typedef enum { red, black } color; // 补充color枚举定义 int main() { FILE *input; node *sent = malloc(sizeof(struct node)); sent->color_node = black; // 哨兵初始:左右子节点指向自身,parent为NULL表示树为空 sent->left = sent; sent->right = sent; sent->parent = NULL; input = fopen(STORE, "r"); if (!input) { // 增加文件打开失败判断 perror("Failed to open file"); return 1; } node *new_node; while ((new_node = get_node_file(input)) != NULL) { insert_id(sent, new_node); } fclose(input); // 后续记得递归释放红黑树内存,避免泄漏 return 0; }
2. 修正insert_id函数逻辑
修复比较逻辑、内存泄漏和哨兵使用:
void insert_id(node *sent, node *new_node) { if (sent->parent == NULL) { // 树为空,新节点作为根 sent->parent = new_node; new_node->parent = sent; new_node->left = sent; new_node->right = sent; new_node->color_node = black; } else { // 从根节点开始寻找插入位置(sent->parent是根) node *temp = sent->parent; while (temp != sent) { int cmp = strcmp(temp->id_prod, new_node->id_prod); if (cmp < 0) { // 当前节点ID更小,往右子树走 temp = (temp->right != sent) ? temp->right : sent; } else { // 当前节点ID更大/相等,往左子树走 temp = (temp->left != sent) ? temp->left : sent; } if (temp == sent) break; } // 找到父节点,回溯一步 node *parent = temp->parent; int cmp = strcmp(parent->id_prod, new_node->id_prod); if (cmp < 0) { parent->right = new_node; } else { parent->left = new_node; } new_node->parent = parent; new_node->left = sent; new_node->right = sent; new_node->color_node = red; // 新节点默认红色 setup_tree(sent, new_node); // 修复红黑树平衡 } }
3. 完善get_node_file函数
增加内存分配和读取失败的判断,防止溢出:
#define FORMAT_IN "%4s %19s %4s" // 适配char数组长度,防止溢出 node* get_node_file(FILE *in) { node *new_node = malloc(sizeof(node)); if (!new_node) { perror("Failed to allocate node"); return NULL; } // 检查fscanf返回值,确保读取3个字段成功 if (fscanf(in, FORMAT_IN, new_node->id_prod, new_node->name, new_node->id_piece) != 3) { free(new_node); return NULL; } new_node->left = NULL; new_node->right = NULL; new_node->parent = NULL; new_node->color_node = black; return new_node; }
内容的提问来源于stack exchange,提问作者Kenyskrachor

