C语言字符串替换程序调试:str1未正确定义致运行异常
原程序意图是在目标字符串中查找指定子串并替换,但存在多个致命错误导致无法正常运行,核心问题及其他错误的排查与修复如下:
核心错误:字符串未添加终止符
1. str1未正确终止
原程序仅将string的前start个字符复制到str1,但未手动添加字符串终止符'\0',导致str1成为未终止的非法字符串。后续调用strcat、strlen等函数时,会因找不到终止符而读取内存越界,引发乱码或崩溃。
修复:复制完成后立即添加终止符
// 复制string前start个字符到str1 for(int i=0;i<start;i++) str1[i]=string[i]; str1[start] = '\0'; // 必须添加,让str1成为合法字符串
2. check数组未正确终止
复制string中start到end的字符到check后,同样未添加'\0',导致strcmp函数读取超出数组范围的内存,匹配结果不可控。
修复:复制完成后添加终止符
// 复制string中start到end的字符到check for(int i=start;i<end;i++) check[i-start]=string[i]; check[end - start] = '\0'; // 等价于check[strlen(s1)] = '\0'
其他致命错误修复
3. start变量未初始化
若输入的s1首字符不在string中,循环不会给start赋值,此时start为随机垃圾值,后续计算end和内存操作会直接崩溃。
修复:初始化start为-1,循环结束后判断是否找到匹配
int start = -1, end, compare; for(int i=0;string[i]!='\0';i++){ if(string[i]==s1[0]){ start=i; break; } } // 未找到匹配直接退出 if(start == -1){ printf("未找到匹配的字符串\n"); return 1; }
4. gets函数存在缓冲区溢出风险
gets不会检查输入长度,若输入超过s1/s2的数组大小(10字节),会直接溢出覆盖内存,引发崩溃或安全问题。
修复:用fgets替代,并手动去除换行符
printf("Enter string 1 "); fgets(s1, sizeof(s1), stdin); s1[strcspn(s1, "\n")] = '\0'; // 去掉输入末尾的换行符 printf("Enter string 2 "); fgets(s2, sizeof(s2), stdin); s2[strcspn(s2, "\n")] = '\0';
5. if语句逻辑缩进错误
原代码中if(compare==0)仅控制strcat一行,printf会无条件执行,不符合"找到匹配后才输出"的逻辑。
修复:给if块添加大括号,同时修正原程序未使用s2的问题(推测意图是替换为s2)
if(compare==0){ strcat(str1, s2); // 将匹配的s1替换为s2 printf("\n替换后的中间字符串:%s", str1); }
6. 复制剩余字符后未添加终止符
将string中end后的字符复制到str1时,未添加终止符,导致strcpy读取越界。
修复:复制完成后给str1加终止符
int j = strlen(str1); for(int i=end; string[i]!='\0'; i++){ str1[j++] = string[i]; } str1[j] = '\0'; // 添加终止符
7. string数组大小不足
原string是固定大小的数组(仅能容纳原字符串),替换后若长度超过原大小,strcpy会导致缓冲区溢出。
修复:扩大string的数组大小
char string[100] = "We will rock you"; // 预留足够空间
修复后的完整代码
#include<stdio.h> #include<string.h> int main() { char string[100] = "We will rock you"; // 扩大数组避免溢出 char s1[10], s2[10]; printf("Enter string 1 "); fgets(s1, sizeof(s1), stdin); s1[strcspn(s1, "\n")] = '\0'; // 去除换行符 printf("Enter string 2 "); fgets(s2, sizeof(s2), stdin); s2[strcspn(s2, "\n")] = '\0'; int start = -1, end, compare; // 查找s1首字符的位置 for(int i=0; string[i]!='\0'; i++){ if(string[i] == s1[0]){ start = i; break; } } if(start == -1){ printf("未找到匹配的字符串\n"); return 1; } end = start + strlen(s1); char str1[100], check[10]; // 复制string前start个字符到str1 for(int i=0; i<start; i++) str1[i] = string[i]; str1[start] = '\0'; // 添加终止符 // 复制string中start到end的字符到check for(int i=start; i<end; i++) check[i-start] = string[i]; check[end - start] = '\0'; // 添加终止符 compare = strcmp(check, s1); if(compare == 0){ strcat(str1, s2); // 替换为s2 printf("\n替换后的中间字符串:%s", str1); } else { printf("\n未找到完整匹配的字符串\n"); return 1; } // 复制string中end之后的剩余字符到str1 int j = strlen(str1); for(int i=end; string[i]!='\0'; i++){ str1[j++] = string[i]; } str1[j] = '\0'; // 添加终止符 strcpy(string, str1); printf("\n最终结果:%s", string); return 0; }
关于已知漏洞说明
你提到的无法处理重复单词、误匹配包含s1的单词(如s1=ask匹配task)属于逻辑缺陷:
- 处理重复单词:需要循环查找所有匹配位置,而非找到第一个就停止;
- 避免部分匹配:匹配
s1后需检查前后是否为单词边界(空格、标点或字符串首尾),确保匹配完整单词。
内容的提问来源于stack exchange,提问作者Raon

