C语言 strcpy 如何更改字符串的值



我有这个家庭作业来决定下面的代码将做什么(在纸上,没有在计算机上测试(。

char s1[]="Short Message Service", *s2, *s3;
s2=strchr(s1,'M');
s3=strchr(s2,'S');
strncpy(s1+1,s2,1);
strcpy(s1+2,s3);

当我想检查我是否正确时,我在计算机上运行它并得到以下结果:

s1 = SMservice 
s2 = ice
s3 = Service

我以为s2"Message Service"但它变成了"ice".显然,它在调用strcpy(s1+2,s3)后会发生变化;有人可以解释为什么以及该功能如何影响s2吗?

答案是"未定义的行为"——任何事情都可能发生。strcpy()strncpy()的论点不得重叠。——然而在这里,strcpy()的论点确实重叠。

C11 §7.24.2.3strcpy函数 ¶2:

strcpy函数将s2指向的字符串(包括终止空字符(复制到s1指向的数组中。如果在重叠的对象之间进行复制,则行为是未定义的。

§7.24.2.4strncpy函数 ¶2

strncpy函数从s2指向的数组复制到s1指向的数组中不超过n个字符(不复制空字符后面的字符(。(308(如果在重叠的对象之间进行复制,则行为是不确定的。

308(因此,如果s2指向的数组的前n个字符中没有空字符,则结果将不会以空值结尾。

这意味着没有可靠的答案可以给出。 您可能会决定随后描述如果复制操作从源的开头复制到目标会发生什么情况,这是教师可能期望的。 但这不是保证的行为。

给定以下代码和从左到右复制假设:

char s1[] = "Short Message Service";
char *s2 = strchr(s1, 'M');
char *s3 = strrchr(s2, 'S');
strncpy(s1+1, s2, 1);
strcpy(s1+2, s3);

我们可以推断出s2指向&s1[6]s3指向&s1[14](这是强制性的(。 不同阶段s1的值为:

s1 = "Short Message Service"   -- initial text
s1 = "SMort Message Service"   -- after strncpy
s1 = "SMService"               -- after strcpy (but this assumes UB works as expected)

所以从s2开始的字符串现在包含ice,正如您所发现的。

但是,必须再次强调,这不是必需的行为。

其他答案已经告诉你一个痛苦的事实:复制带有strncpystrcpy的重叠字符串是未定义的行为,应该避免,特别是当涉及到更复杂的格式时(对于sprintf等函数也是如此(。


无论如何,所看到的可以逐步解释分析您的代码。我想再次强调,当存在未定义的行为时,任何编译器都可以选择不同的行为,因此我们无法确定这是一个普遍的解释。

要考虑的重要一点是所有指针共享相同的内存位置。初始化s1

char s1[]="Short Message Service", *s2, *s3;

它指向的字符数组如下所示:

----------------------------------------------
|S|h|o|r|t| |M|e|s|s|a|g|e| |S|e|r|v|i|c|e||
----------------------------------------------
^
s1

然后你在第二个和第三个单词的开头设置s2s3

s2=strchr(s1,'M');
s3=strrchr(s2,'S');

这是三个指针的位置

----------------------------------------------
|S|h|o|r|t| |M|e|s|s|a|g|e| |S|e|r|v|i|c|e||
----------------------------------------------
^           ^               ^
s1          s2              s3

由于每个字符串实际上是从相应指针到第一个终止符的数组,因此如果您打印三个字符串,您将看到:

s1: "Short Message Service"
s2: "Message Service"
s3: "Service"

然后,在s1的第一个字符之后仅复制s2的一个字符:

strncpy(s1+1,s2,1);

请注意,当源字符串的长度超过传递给strncpy的最大长度时,不会复制字符串终止符。数组将如下所示:

----------------------------------------------
|S|M|o|r|t| |M|e|s|s|a|g|e| |S|e|r|v|i|c|e||
----------------------------------------------
^           ^               ^
s1          s2              s3

打印字符串不会有太大变化:s1只是变得"Short Message Service"。最后你使用

strcpy(s1+2,s3);
-----------------------------------------------
|S|M|S|e|r|v|i|c|e||a|g|e| |S|e|r|v|i|c|e||
-----------------------------------------------
^           ^                ^
s1          s2               s3

这就是为什么你会得到

由于每个字符串实际上是从相应指针到第一个终止符的数组,因此如果您打印三个字符串,您将看到:

s1: "SMService"
s2: "ice"       // Because of the terminator in the middle
s3: "Service"   // The original string ending

只是一个实用的建议

如果你需要一个指向每个单词的指针,你只需要存储单词的开头,就像你已经做的那样,然后在每个空格的位置放置一个字符串终止符

这样,s1将被"Short"(因为将在第一个空格所在的位置找到终结者(,s2将被"Message"(因为将在第二个空格所在的位置找到终结者(和s3将被"Service"(因为原始终止符(。

顺便说一下:这就是strtok的作用:查找标记的出现,在其中放置一个字符串终止符,然后返回指针经过它。

相关内容

  • 没有找到相关文章

最新更新