C strcpy和strncpy组合中出现意外结果

C strcpy和strncpy组合中出现意外结果,c,pointers,strcpy,string.h,C,Pointers,Strcpy,String.h,在我高中的期末考试练习中,我们遇到了以下问题: 执行代码后查找字符串s1、s2和s3的值: char s1[] = "Short Message Service", *s2, *s3; s2 = strchr(s1, 'M'); s3 = strrchr(s2,'S'); strncpy(s1 + 1, s2, 1); strcpy(s1 + 2, s3); 全班预期成绩为: s1 = SMService s2 = Message Service s3 = Service 当我们通过执行代码

在我高中的期末考试练习中,我们遇到了以下问题:

执行代码后查找字符串s1、s2和s3的值:

char s1[] = "Short Message Service", *s2, *s3;
s2 = strchr(s1, 'M');
s3 = strrchr(s2,'S');
strncpy(s1 + 1, s2, 1);
strcpy(s1 + 2, s3);
全班预期成绩为:

s1 = SMService
s2 = Message Service
s3 = Service
当我们通过执行代码对其进行测试时,我们惊讶地看到结果是:

s1 = SMService
s2 = ice
s3 = Service
问题是没人能弄明白为什么s2缩短了。在试图弄清楚它的时候,我发现s2仍然是“消息服务”,直到执行“strcpy”函数的最后一行代码。我假设问题可能出在指针地址上,但我无法理解strcpy是如何影响s2的


所以我的问题是为什么s2不是我们所期望的,为什么它被缩短了

在您的代码中
s2
指向
s1
中的
M
,然后在上一次
strcpy中被
s3
覆盖:

char s1[] = "Short Message Service", *s2, *s3;
s2 = strchr(s1, 'M');   // s2 is pointing to s1 + 6 = Message Service
s3 = strrchr(s2, 'S');  // s3 is pointing to s1 + 14 = Service 
strncpy(s1 + 1, s2, 1); // Write M in to s1[1], s1 = SMort Message Service 
strcpy(s1 + 2, s3);     // Write Service into s1 + 2
                        // s1 = SMService but s2 is pointing to s1 + 6 = ice

它似乎缩短了,因为最后的
strcpy
插入了终止符。这一切都发生在同一个缓冲区中(
s1[]
),调试器将使这一点变得显而易见。查看
s2
点的位置:
s1+6
。现在看看最后一个strcpy之后的
s1+6
是什么:
SMService
的尾部字符,这就是
ice
和终止符。@WhozCraig谢谢你的解释。但如果是这样的话(即
s2
指向:
s1+6
)是否
s3
指向
s2+7
或直接指向
s1+13
?在这两种情况下,
s3
最终如何等于
“服务”
?在分配
strrchr
结果后,
s3
中存储的地址从未改变,即
s1+14
没有任何内容通过缓冲区的末端重写或更改该地址的数据,因此,当呈现时,
s3
的呈现显示为简单的
Service
。@WhozCraig谢谢,您的解释真的很有帮助<代码>strcpy(s1+2,s3)是UB,因为源和目标重叠。违反
char*strcpy的
restrict
(char*restrict s1,const char*restrict s2)。这使得测试用例失败了。@chux:我不相信这是真的<代码>s3
具有值
s1+14
strcpy
将通过
s1+9将8个字符(包括终止NUL)复制到位置
s1+2
。没有重叠。通过
strcpy
参数
s2
访问的对象都不会通过参数
s1
修改(甚至读取)。
restrict
施加的限制适用于指针引用的对象;对于
char*
类型的指针,所有这些对象都具有
char
类型。换句话说,
restrict
与指针引用的值的范围无关;仅适用于实际值。见6.7.3.1第8和9段中的示例。对
f(50,d+50,d)
的调用是有效的,因为该调用中两个指针参数引用的范围不重叠。在第二次调用中,
f(50,d+1,d)
,有一个重叠,调用是UB。@rici同意-对我来说太晚了。靠近边缘不超过边缘。