C语言嵌套结构体添加、删除函数实现错误排查
已定义的基础结构
当前定义了3个业务结构体,代码如下:
typedef struct { char *name; // allocated int commonality; int weight; } monster; typedef struct { char *name; // allocated char *description; // allocated double area; int monsters; monster **monsters; // allocated } region; typedef struct { char *name; // allocated double diameter; int regions; region **regions; // allocated } planet;
目前已经实现了怪物实例创建函数:
monster *new_monster(char *name, int commonality, int weight);
需要实现两个功能函数:
void add_monster_to_region(region *r, char *mname, int mcommonality, int weight); void delete_region_from_planet(planet *p, char *rname);
当前编写的add_monster_to_region逻辑存在问题,代码如下:
void add_monster_to_region(region *r, char *mname, int mcommonality, int mweight) { for (int i = 0; i < mcommonality; i++) { if (strcmp(mname, r->monsters[i]->name) == 0) { r->monsters[i]->name = mname; } } }
现有代码错误说明
当前编写的add_monster_to_region存在以下核心问题:
- 循环边界逻辑完全错误:遍历区域内怪物时,循环上限错误使用了传入的参数
mcommonality(怪物常见度),正确的遍历上限应该是区域结构体中存储的现有怪物数量r->monsters。这个错误会导致两种严重问题:如果mcommonality大于当前区域怪物总数,会越界访问r->monsters数组触发内存崩溃;如果mcommonality小于现有怪物数,会漏遍历部分怪物。 - 没有实现「新增怪物」的核心逻辑:函数整体只有遍历匹配同名怪物的分支,完全没有处理“怪物不存在时,扩容数组、创建新怪物实例、更新区域怪物计数”的新增逻辑,根本没有完成add的功能。
- 同名匹配后的操作逻辑错误:就算匹配到同名怪物,代码直接将
r->monsters[i]->name指针指向传入的mname,既没有释放name字段之前已分配的堆内存(造成内存泄漏),也没有更新怪物的commonality、weight属性;同时直接引用外部传入的字符串指针,一旦外部字符串被释放就会产生野指针。 - 缺少入参合法性校验:没有判断传入的区域指针
r、怪物名指针mname是否为空,空指针传入时会直接触发崩溃。
修正后参考实现
修正后的add_monster_to_region
#include <string.h> #include <stdlib.h> void add_monster_to_region(region *r, char *mname, int mcommonality, int mweight) { // 空指针直接返回 if (r == NULL || mname == NULL) return; // 先遍历现有怪物列表,检查是否存在同名怪物 for (int i = 0; i < r->monsters; i++) { if (strcmp(mname, r->monsters[i]->name) == 0) { // 存在同名怪物直接更新属性即可,无需重复新增 r->monsters[i]->commonality = mcommonality; r->monsters[i]->weight = mweight; return; } } // 未找到同名怪物,执行新增逻辑 // 给怪物指针数组扩容,多存储1个怪物指针 r->monsters = realloc(r->monsters, sizeof(monster*) * (r->monsters + 1)); // 调用已实现的构造函数创建怪物实例 monster *new_m = new_monster(mname, mcommonality, mweight); // 将新怪物存入数组末尾,更新怪物计数 r->monsters[r->monsters] = new_m; r->monsters++; }
配套delete_region_from_planet参考实现
void delete_region_from_planet(planet *p, char *rname) { if (p == NULL || rname == NULL || p->regions == 0) return; int target_idx = -1; // 查找待删除区域的下标 for (int i = 0; i < p->regions; i++) { if (strcmp(rname, p->regions[i]->name) == 0) { target_idx = i; break; } } // 未找到对应区域直接返回 if (target_idx == -1) return; // 释放待删除区域的所有关联内存:先释放区域内的怪物,再释放区域自身 region *del_r = p->regions[target_idx]; for (int i = 0; i < del_r->monsters; i++) { free(del_r->monsters[i]->name); free(del_r->monsters[i]); } free(del_r->monsters); free(del_r->name); free(del_r->description); free(del_r); // 移动数组元素,覆盖被删除的位置 for (int i = target_idx; i < p->regions - 1; i++) { p->regions[i] = p->regions[i+1]; } // 更新区域计数,缩容数组 p->regions--; if (p->regions == 0) { free(p->regions); p->regions = NULL; } else { p->regions = realloc(p->regions, sizeof(region*) * p->regions); } }
说明:以上实现默认
new_monster函数内部已经完成了怪物结构体内存分配、name字段的字符串堆拷贝逻辑,和代码注释中标注的各指针为“已分配”的约定一致。如果new_monster没有做字符串拷贝,需要在创建怪物时自行用strdup(mname)给name字段分配独立堆内存,避免野指针。
内容的提问来源于stack exchange,提问作者Hailey Baker
相关产品推荐
相关产品推荐

