使用C语言去除字符串连续重复字符的代码问题排查
我来帮你拆解代码里的几个关键问题,这就是为什么程序只输出第一个字符、空格处理失效的原因:
1. 字符数组声明完全错误
不管是Ex6里的char* string[80],还是removeDup里的char* newString[80],你声明的都是指针数组(用来存放80个字符指针的数组),而不是存储字符串的字符数组。正确的声明应该是:
char string[80]; // 字符数组,用来存储输入的字符串
和
char newString[80]; // 字符数组,用来存储去重后的结果
指针数组根本不能直接存单个char字符,这直接导致你存储字符的逻辑完全偏离预期。
2. scanf参数错误+空格读取限制
你写的scanf("%s", &string);是错的:当string是字符数组时,它的名字本身就是指向数组首元素的地址,不需要加&。正确写法是:
scanf("%s", string);
另外,%s会自动忽略空格,只读取到第一个空格为止——这就是你说"空格处理失效"的核心原因:输入里的空格根本没被scanf读进去。如果要读取包含空格的完整字符串,应该用fgets,记得之后要去掉它读取的换行符:
fgets(string, 80, stdin); size_t len = strlen(string); if (len > 0 && string[len-1] == '\n') { string[len-1] = '\0'; }
3. 循环逻辑遗漏最后一个字符+越界访问
你的循环条件是i < strlen(string),然后判断string[i] != string[i+1]。当i是字符串最后一个有效字符时,string[i+1]是字符串结束符'\0',这时候如果最后一个字符和前一个不同,它不会被加入newString;而且i等于strlen(string)-1时,i+1已经超出字符串的有效索引范围,会导致未定义行为。
正确逻辑应该是先把第一个字符加入结果,然后从第二个字符开始遍历,只要当前字符和前一个不同就加入:
int c = 0; if (strlen(string) == 0) return NULL; // 处理空字符串边界情况 newString[c++] = string[0]; // 先加入第一个字符 for (int i = 1; i < strlen(string); i++) { if (string[i] != string[i-1]) { // 和前一个字符比较,而非后一个 newString[c++] = string[i]; } } newString[c] = '\0'; // 必须手动添加字符串结束符!
4. 返回局部数组的未定义行为
你直接返回newString,但newString是removeDup函数里的局部变量,函数执行完毕后,局部变量的内存会被系统回收,返回它的地址会导致后续使用时出现乱码、程序崩溃等未定义行为。
解决办法有两种:
- 让调用者提供一个缓冲区作为参数传给
removeDup - 在
removeDup里用malloc动态分配内存,使用完后记得手动free
修复后的完整代码
这里是修复后的完整示例,采用动态分配内存的方式,支持读取带空格的字符串:
#include <stdio.h> #include <string.h> #include <stdlib.h> void Ex6() { char string[80]; // 读取包含空格的字符串 fgets(string, 80, stdin); // 去掉fgets读取的换行符 size_t len = strlen(string); if (len > 0 && string[len-1] == '\n') { string[len-1] = '\0'; } char* result = removeDup(string); puts(result); free(result); // 释放动态分配的内存 } char* removeDup(char *string) { int len = strlen(string); if (len == 0) return NULL; // 动态分配足够内存,最多和原字符串长度一致 char* newString = (char*)malloc((len + 1) * sizeof(char)); if (!newString) return NULL; // 处理内存分配失败的情况 int c = 0; newString[c++] = string[0]; for (int i = 1; i < len; i++) { if (string[i] != string[i-1]) { newString[c++] = string[i]; } } newString[c] = '\0'; // 添加字符串结束符 return newString; } int main() { Ex6(); return 0; }
测试输入bssdffFdcrrrtttii ***#,输出就会是bsdfFdcrti *#,完全符合你的预期。
内容的提问来源于stack exchange,提问作者Tom S

