练习5-3:编写strcat指针版本,求解同字符串参数时程序失效原因
Why Passing the Same String to Custom
strcat Breaks Things 嘿,这个问题我之前踩过坑!咱们来掰扯清楚为啥你把同一个字符串传给自己写的strcat会出问题。
首先先把你的代码贴出来方便分析:
#include <string.h> // 你代码里没写,但strlen需要这个头文件 void strcat(char *s, char *t) { int len = strlen(s); while (*t != '\0') *(s + len++) = *t++; *(s + len) = '\0'; } int main (void) { char s[] = "123"; strcat(s, s); return 0; }
问题出在哪?
咱们一步步走一遍执行流程:
- 一开始
s是{'1','2','3','\0'},strlen(s)返回3,len被设为3。 - 进入循环:
*t就是s[0]也就是'1',把它赋值给s[3]——这时候原来的结束符'\0'被覆盖成'1'了! t++指向s[1],len变成4,接着把s[1]的'2'赋值给s[4]。- 接下来循环会一直继续:因为
t指向的是s本身,而我们一直在往s后面加字符,原来的结束符早就没了,*t != '\0'这个条件永远成立!
循环会无限运行,不断往内存里写数据,直到超出数组s的边界,触发内存越界,程序直接崩溃。
额外说一句:标准库的strcat也救不了你
其实标准库的strcat明确规定不支持重叠的输入参数,当s和t指向的内存区域有重叠时,行为是未定义的——也就是说你可能得到奇怪的结果,或者直接崩溃,完全看运气。你的实现只是把这种未定义行为变成了更直观的无限循环而已。
怎么改?
如果确实需要实现字符串自我拼接(比如把"123"变成"123123"),得换一种思路:先把原字符串的内容保存下来,或者用反向复制的方式,避免覆盖还没读取的内容。比如这样写:
#include <string.h> // 方式1:用临时内存保存原内容 void strcat_self_safe(char *s) { // 先获取原字符串的长度 size_t orig_len = strlen(s); // 这里要保证temp足够大,或者用动态内存malloc char temp[100]; strcpy(temp, s); // 再把temp拼到s后面 size_t i = 0; while (temp[i] != '\0') { s[orig_len + i] = temp[i]; i++; } s[orig_len + i] = '\0'; } // 方式2:反向复制,不用额外内存 void strcat_self_reverse(char *s) { size_t s_len = strlen(s); size_t t_len = s_len; // 因为t就是s本身 char *s_dest = s + s_len + t_len; *s_dest = '\0'; // 先放结束符 // 从后往前复制原字符串内容 while (t_len > 0) { *--s_dest = s[--t_len]; } }
总结一下
- 你的
strcat实现中,当s和t指向同一个字符串时,会覆盖掉原字符串的结束符,导致循环无法终止,最终内存越界崩溃。 - 永远不要把重叠的字符串传给
strcat(不管是自定义的还是标准库的),这属于未定义行为。 - 如果需要自我拼接,得用安全的方式先保存原内容,或者反向复制避免覆盖。
内容的提问来源于stack exchange,提问作者Andy Xu
相关产品推荐
相关产品推荐

