自学C充满了惊喜。我做了这个简短的片段来测试strcat()
,它应该将第二个参数附加到第一个参数:
#include <stdio.h>
#include <string.h>
char s1[4] = "Foo ";
char s2[] = "Bar";
int main(void) {
strcat(s1, s2);
printf("%s %d %d \n", s1, strlen(s1), strlen(s2));
return 0;
}
我期待一些溢出错误,因为s1
是一个包含4个字符的数组,但我得到了这个:
Foo BarBar BarBar 10 6
我是在Windows上使用MS Visual Studio Express 2013完成此操作(顺便说一下,它提出了一些关于使用strcat的警报)。那么......为什么strcat重复了s2的值?这不在文档中。
答案 0 :(得分:2)
此
char s1[4] = "Foo ";
创建一个没有零终结符的字符序列s1
。这意味着s1
不是字符串,将其传递给strcat
是违法的。行为未定义。
(在上面的初始化声明中,你使用的是C语言的一个不起眼的特性,它允许零终结符“脱落”正在初始化的char数组的末尾。在C ++中,这个初始化将是错误的,因为初始化字符串需要大小为5的缓冲区,而不是4。)
实际上,这会导致strcat
在s1
数组的末尾运行并进入s2
(显然意外地位于内存附近),在第一个参数中寻找零终止符。因此,最后您将s2
添加到内存中s1+s2
的组合中,这会产生s2
重复的效果。不用说,结果毫无意义。
当然,即使大小为s1
的代码,代码仍然会显示未定义的行为,因为目标缓冲区中没有连接结果的空间。 s1
的大小必须至少为8才能使结果适合它。
答案 1 :(得分:0)
首先:s1不是以空值终止的
第二:你为s1和s2分配静态内存。编译器通常将它们打包到可执行文件的共享数据块中。如果你正在阅读s1的边界,你正在阅读这个区块,因为你的程序“拥有”这个记忆,操作系统不会抱怨它。
答案 2 :(得分:0)
首先要注意的是C字符串是“空终止” - 即它以空字节(“\ 0”)结束(因此不能包含多个空字节)。所以字符串“Foo”实际上是五个个字符{'F', 'o', 'o', ' ', '\0'}
。
您定义一个4个字符的数组,并用五个元素填充它。
char s1[4] = "Foo ";
这意味着下一个可能放在第一个数组旁边的数组s2
将覆盖s1
的空字节。
因为C字符串定义为空字节,strcat
将从第一个字符串复制字符,直到达到nullbyte。但是,因为s2
覆盖了空字节,strcat
遇到的第一个空是来自s2
的空值。
因此,从strcat
的角度来看,s1
看起来像"Foo Bar\0"
;