C语言结构体数组删重名元素:deleteAnimalByName仅删除单个对象问题排查
核心问题定位
原删除函数存在两处逻辑错误,直接导致重名条目无法被全部删除:
- 元素移位逻辑笔误:原本需要将
j+1位置的元素拷贝到j位置完成前移,却误写成了j+i作为源索引,不仅移位动作完全错位,还可能引发数组越界、数据随机覆盖的问题。 - 遍历逻辑缺陷:当匹配到目标条目并完成前移、更新数组长度后,外层循环会直接将索引
i自增,跳过了当前i位置被新元素填充的位置——刚好这个位置就可能是之前未被检查到的同名条目,最终造成漏删。
修复方案
基础修正版(兼容原代码逻辑框架)
调整遍历的索引递增规则:仅当当前位置元素不匹配删除条件时,才推进遍历索引;匹配删除后不推进索引,重新检查当前位置新挪入的元素,同时修正移位的源索引为正确的j+1:
void deleteAnimalByName(char *animalName, int *nrOfAnimals, ANIMAL *animalArray) { int i = 0; while (i < *nrOfAnimals) { if (strcmp(animalArray[i].Name, animalName) == 0) { // 将后续元素整体前移覆盖当前匹配位置 for (int j = i; j < *nrOfAnimals - 1; j++) { animalArray[j].Age = animalArray[j + 1].Age; strcpy(animalArray[j].Name, animalArray[j + 1].Name); animalArray[j].Species = animalArray[j + 1].Species; } // 更新有效数组长度 (*nrOfAnimals)--; // 不推进索引,下一轮直接检查挪到当前i位置的新元素 continue; } // 无匹配时才推进索引 i++; } }
优化版(双指针实现,效率更高)
原逻辑每删除一个元素就要整体拷贝后续所有元素,时间复杂度最坏为O(n²)。可以用读写双指针一次遍历完成筛选:读指针遍历全部有效元素,遇到需要保留的非同名元素就写入写指针位置,遍历完成后直接将数组有效长度设为写指针的位置,无需反复移位:
void deleteAnimalByName(char *animalName, int *nrOfAnimals, ANIMAL *animalArray) { int writePos = 0; // 遍历所有有效元素 for (int readPos = 0; readPos < *nrOfAnimals; readPos++) { // 保留非同名元素 if (strcmp(animalArray[readPos].Name, animalName) != 0) { // 同类型结构体直接赋值,替代逐个成员拷贝 animalArray[writePos] = animalArray[readPos]; writePos++; } } // 更新有效数组长度 *nrOfAnimals = writePos; }
注意:该优化版要求
ANIMAL结构体可直接赋值(即不含动态内存指针成员),若结构体包含指向堆内存的指针,需替换为逐个成员的深拷贝逻辑,避免浅拷贝导致的内存泄漏。
内容的提问来源于stack exchange,提问作者JessyGazar
相关产品推荐
相关产品推荐

