Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/string/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
strcpy如何更改字符串的值_C_String_Strcpy_Strncpy - Fatal编程技术网

strcpy如何更改字符串的值

strcpy如何更改字符串的值,c,string,strcpy,strncpy,C,String,Strcpy,Strncpy,我做了这个家庭作业来决定下面的代码将做什么(在纸上,没有在计算机上测试) 当我想检查我做的是否正确时,我在计算机上运行了它,得到了以下结果: s1 = SMservice s2 = ice s3 = Service 我原以为s2将是“消息服务”,但它变为“ice”。显然,它在调用strcpy(s1+2,s3)后发生变化;有人能解释一下为什么以及该函数如何影响s2吗?答案是“未定义的行为”——任何事情都可能发生。strcpy()和strncpy()的参数不能重叠。-然而在这里,strcpy

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

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

s1 = SMservice 

s2 = ice

s3 = Service
我原以为
s2
将是
“消息服务”
,但它变为
“ice”
。显然,它在调用strcpy(s1+2,s3)后发生变化;有人能解释一下为什么以及该函数如何影响s2吗?

答案是“未定义的行为”——任何事情都可能发生。
strcpy()
strncpy()
的参数不能重叠。-然而在这里,
strcpy()
的参数确实重叠

C11:

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

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
,如您所见


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

其他答案已经告诉了你一个痛苦的事实:使用
strncpy
strcpy
复制重叠字符串是未定义的行为,应该避免,尤其是在涉及更复杂的格式时(对于诸如
sprintf
之类的函数也是如此)


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

需要考虑的重要一点是,所有指针共享相同的内存位置

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|\0|
----------------------------------------------
 ^
 s1
----------------------------------------------
|S|M|o|r|t| |M|e|s|s|a|g|e| |S|e|r|v|i|c|e|\0|
----------------------------------------------
 ^           ^               ^
 s1          s2              s3
然后在第二个和第三个单词的开头设置
s2
s3

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|\0|
----------------------------------------------
 ^           ^               ^
 s1          s2              s3
由于每个字符串实际上都是从相应指针到第一个终止符的数组,如果打印三个字符串,您会看到:

s1: "Short Message Service"
s2: "Message Service"
s3: "Service"
s1: "SMService"
s2: "ice"       // Because of the terminator in the middle
s3: "Service"   // The original string ending
然后在
s1
的第一个字符之后只复制
s2
的一个字符:

strncpy(s1+1,s2,1);
请注意,当源字符串长于传递给strncpy的最大长度时,不会复制字符串终止符。数组如下所示:

----------------------------------------------
|S|h|o|r|t| |M|e|s|s|a|g|e| |S|e|r|v|i|c|e|\0|
----------------------------------------------
 ^
 s1
----------------------------------------------
|S|M|o|r|t| |M|e|s|s|a|g|e| |S|e|r|v|i|c|e|\0|
----------------------------------------------
 ^           ^               ^
 s1          s2              s3
打印字符串不会有太大的变化:
s1
刚刚变成了
“短消息服务”
。最后您使用

strcpy(s1+2,s3);

-----------------------------------------------
|S|M|S|e|r|v|i|c|e|\0|a|g|e| |S|e|r|v|i|c|e|\0|
-----------------------------------------------
 ^           ^                ^
 s1          s2               s3
这就是为什么你会

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

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

只是一个实用的建议 如果你需要一个指向每个单词的指针,你只需要像以前一样存储单词的开头,然后在每个空格的位置放置一个字符串终止符

这样,
s1
将是
“Short”
(因为将在第一个空格处找到终止符),
s2
将是
“Message”
(因为将在第二个空格处找到终止符),
s3
将是
“Service”
(因为原始终止符)


顺便说一句:
strtok
就是这样做的:查找一个令牌的出现点,在其中放置一个字符串终止符,并返回经过它的指针。

它应该是
s1=“SM”--在strncpy
@isrnick-nope之后;
strncpy()
在源字符串长度超过长度时不会以null终止。这是脚注308的内容,我最初没有在答案中包含它(但我刚刚添加了它)。
strncpy()
操作精确复制了一个字符,即
'M'
。答案不错。不仅我在“常规”方面迟到了回答,但我的回答也太慢了。@RobertoCaboni-谢谢!我的回答有点像“变色龙回答”,因为它会随着时间的推移而增长。我加了一句“但你可能需要告诉你的老师他们希望听到什么”在您发布答案几秒钟后,请参阅第节-所以,当您使用仔细的ASCII艺术图表创建答案时(创建这些图表需要时间),我发布了同一分析的更简单版本。但很好,我们对可能的结果达成一致。我正在讨论是否添加示例程序及其输出。
s1=SMservice
是一个有问题的结果,因为
's'
不会出现在字符串中的任何地方。