为何非空格非*字符未全复制?strcpy为何修改源字符串?
先来看你提供的原代码:
#include <stdio.h> #include <string.h> int main(void){ char q[100*1000+2]; fgets(q,100*1000+2,stdin);//enter string int len=strlen(q)-1; char s[len]; int i,j; for(i=j=0;q[i]!='\0';i++) if(q[i]!=' '&&q[i]!='\n'&&q[i]!='*') {s[j]=q[i];j++;}//remove all spaces, \n's and * len=j;//new length is length of s s[j]='\0'; char copy[len];//copy s printf("s=%s\n",s); strcpy(copy,s); printf("s=%s\n",s); return 0; }
测试用例异常情况
下面是你提到的三个测试用例的表现:
测试用例0:输入
x^125-228-7x^27实际输出:
s=x125-228-7x27
s=x125-228-7x27
符合预期,无异常。测试用例1:输入
+x^125-228-7x^274实际输出:
s=+x125-228-7x27
s=+x125-228-7x27
预期输出:
s=+x125-228-7x274
s=+x125-228-7x274
出现字符串截断异常。测试用例2:输入
+x^125-228-7x^27实际输出:
s=+x125-228-7x27
s=
预期输出:
s=+x125-228-7x27
s=+x125-228-7x27
执行strcpy后原字符串s被清空。
异常原因分析
1. 测试用例1的截断问题
原代码中,char s[len];的len是处理前的字符串长度(strlen(q)-1,即去掉换行符后的原字符串长度)。当输入的字符串没有需要过滤的字符(空格、*、换行)时,处理后的j会等于这个初始len,此时执行s[j]='\0';就会越界写入——因为变长数组s的大小是len,下标范围是0~len-1,j=len已经超出了数组的有效范围。这种越界会触发未定义行为,导致字符串被意外截断。
2. 测试用例2的s被清空问题
定义char copy[len];时,len是处理后的s的长度(不包含终止符'\0')。而strcpy会复制从s开始直到'\0'的所有字符,包括'\0'本身。这意味着copy的大小至少需要len+1才能容纳所有内容,否则strcpy会越界写入,破坏栈上的其他变量(比如s的内存区域),最终导致s的内容被清空。
当你换成memcpy(copy,s,len)时,只复制了len个字节(没有包含'\0'),虽然避免了越界破坏s,但copy本身会缺少终止符,依然是隐患,而且测试用例1的越界问题没解决,所以截断还是存在。
修复方案
针对这两个问题,我们可以调整代码的数组定义和终止符处理逻辑:
#include <stdio.h> #include <string.h> int main(void){ char q[100*1000+2]; fgets(q,100*1000+2,stdin);//enter string // 先过滤字符,得到处理后的长度j int i,j; char s[100*1000+2]; // 直接用足够大的数组,避免变长数组的越界问题 for(i=j=0;q[i]!='\0';i++) if(q[i]!=' '&&q[i]!='\n'&&q[i]!='*') {s[j]=q[i];j++;}//remove all spaces, \n's and * s[j]='\0'; // 此时j是处理后的有效字符数,s的大小足够,不会越界 int len = j; char copy[len+1]; // 分配足够容纳终止符的空间 printf("s=%s\n",s); strcpy(copy,s); printf("s=%s\n",s); printf("copy=%s\n",copy); // 可以验证copy的内容 return 0; }
修复要点:
- 直接使用和
q同大小的s数组,避免变长数组的长度预判错误,确保s[j]='\0'不会越界。 copy数组的大小设为len+1,保证能容纳s的所有字符(包括终止符'\0'),避免strcpy的越界写入。- 调整逻辑顺序:先完成过滤得到
j,再设置终止符,最后处理复制操作。
这样修改后,两个测试用例的异常都会被修复,所有输入的处理结果都能符合预期。
内容的提问来源于stack exchange,提问作者Anonymix321

