C语言使用指针算术实现移除子数组功能的代码修复
问题描述
需要在C语言中实现基于指针算术的子数组移除函数,满足以下要求:
- 从原数组中移除第一个匹配的目标子数组
- 函数返回实际被移除的元素数量
- 实现过程不允许使用辅助数组,只能通过指针操作在原数组上完成修改
原始实现代码如下:
#include <stdio.h> int remove_subarray(int * first_start, int * first_end,const int * second_start,const int * second_end) { int size_of_second = second_end-second_start; int *subarray_start, *last = first_end - 1; const int *pok = second_start,*second_start_copy = second_start; int number_of_the_same = 0; while (first_start != first_end) { if ( * first_start == * second_start) { if (number_of_the_same == 0) subarray_start = first_start; first_start++; second_start++; number_of_the_same++; if (number_of_the_same == size_of_second) { first_start = subarray_start; while (1) { if ( *first_start == *last) break; subarray_start = first_start; subarray_start += size_of_second; *first_start = *subarray_start; first_start++; } break; } } else { number_of_the_same = 0; first_start++; second_start = second_start_copy; } } return size_of_second; } int main() { // This gives correct result int niz1[14] = {1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, -1},i; int niz2[4] = {2, 3, 4, 5}; int k1 = remove_subarray(niz1, niz1 + 14, niz2, niz2 + 4); for (i = 0; i < 14 - k1; ++i) printf("%i ", niz1[i]); printf("\n"); // This gives wrong result int niz3[10] = {1, 1, 2, 3, 5, 6, 1, 2, 4, 10}; int niz4[3] = {1, 2, 3}; int k2 = remove_subarray(niz3, niz3 + 10, niz4, niz4 + 3); for (i = 0; i < 10 - k2; i++) printf("%d ", niz3[i]); return 0; }
原有设计思路
原实现的逻辑框架:
- 遍历原数组,逐元素和目标子数组的元素比对
- 若当前元素匹配子数组对应位置元素,记录匹配起始位置,累计连续匹配长度
- 当连续匹配长度等于目标子数组长度时,判定定位到待移除子数组
- 定位完成后将子数组后方的元素向前移动,覆盖待移除区间完成删除
问题排查
原代码存在3个核心问题,导致第二组测试用例运行异常:
- 元素移动终止条件逻辑错误
原代码移动元素时,以「当前指针指向的值等于原数组最后一个元素的值」作为循环终止条件,该逻辑完全不成立:- 第一组测试用例运行正确纯为巧合:该用例最后一个元素是全局唯一的
-1,移动时刚好把-1拷贝到前方后触发终止 - 第二组用例会触发数组越界:当源指针已经超出原数组范围时仍会继续解引用,触发未定义行为,输出结果不符合预期
- 正确终止条件应为:用于读取后续元素的源指针走到原数组尾端
first_end时停止移动
- 第一组测试用例运行正确纯为巧合:该用例最后一个元素是全局唯一的
- 未处理无匹配子数组的边界场景
原代码无论是否找到匹配的子数组,都固定返回目标子数组的长度,不符合返回值要求。如果原数组中不存在目标子数组,应该返回0,且不对原数组做任何修改。 - 冗余代码与指针复用混乱
代码中定义了从未使用的指针变量pok,同时复用传入的first_start指针同时做遍历、写操作,很容易出现指针指向错误。
修复后代码
#include <stdio.h> int remove_subarray(int *first_start, int *first_end, const int *second_start, const int *second_end) { int size_of_second = second_end - second_start; // 边界处理:待移除子数组为空直接返回0 if (size_of_second == 0) return 0; int *subarray_start = NULL; const int *second_iter = second_start; const int *second_head = second_start; int match_len = 0; int *cur = first_start; int found = 0; while (cur != first_end) { if (*cur == *second_iter) { if (match_len == 0) { subarray_start = cur; } cur++; second_iter++; match_len++; if (match_len == size_of_second) { found = 1; // 重写元素移动逻辑:读写指针分离,按位置判断终止 int *dst = subarray_start; int *src = subarray_start + size_of_second; while (src != first_end) { *dst++ = *src++; } break; } } else { match_len = 0; cur++; second_iter = second_head; } } return found ? size_of_second : 0; } int main() { // 第一组测试用例 int niz1[14] = {1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, -1}, i; int niz2[4] = {2, 3, 4, 5}; int k1 = remove_subarray(niz1, niz1 + 14, niz2, niz2 + 4); printf("移除元素数量:%d,结果数组:", k1); for (i = 0; i < 14 - k1; ++i) printf("%i ", niz1[i]); printf("\n"); // 第二组测试用例 int niz3[10] = {1, 1, 2, 3, 5, 6, 1, 2, 4, 10}; int niz4[3] = {1, 2, 3}; int k2 = remove_subarray(niz3, niz3 + 10, niz4, niz4 + 3); printf("移除元素数量:%d,结果数组:", k2); for (i = 0; i < 10 - k2; i++) printf("%d ", niz3[i]); printf("\n"); // 无匹配子数组边界测试 int niz5[5] = {1,2,3,4,5}; int niz6[2] = {7,8}; int k3 = remove_subarray(niz5, niz5+5, niz6, niz6+2); printf("移除元素数量:%d,结果数组:", k3); for (i=0; i<5 -k3; i++) printf("%d ", niz5[i]); printf("\n"); return 0; }
修复说明
- 新增空数组边界判断,避免非法长度输入导致的异常
- 增加
found标记位,仅在确实找到匹配子数组时返回子数组长度,否则返回0 - 重写元素移动逻辑,使用独立的读、写指针,完全基于指针位置判断循环终止,不会出现值判断导致的巧合正确和越界问题
- 拆分遍历、匹配、写入场景的指针变量,避免复用指针导致的指向混乱
- 删除所有未使用的冗余变量
修复后第二组测试用例的正确输出为1 5 6 1 2 4 10,符合预期。
内容的提问来源于stack exchange,提问作者codproe
相关产品推荐
相关产品推荐

