我正在经历K& R,并且我很难增加指针。练习5.3(p.107)要求你使用指针编写一个strcat函数。
在伪代码中,该函数执行以下操作:
我得到了一个有效的答案:
void strcats(char *s, char *t)
{
while (*s) /* finds end of s*/
s++;
while ((*s++ = *t++)) /* copies t to end of s*/
;
}
但我不明白为什么这段代码也不起作用:
void strcats(char *s, char *t)
{
while (*s++)
;
while ((*s++ = *t++))
;
}
显然,我遗漏了一些关于指针增量如何工作的东西。我认为增量的两种形式是等价的。但第二个代码只打印出字符串s。
我尝试了一个虚拟变量i来检查函数是否经历了两个循环。它做了。我读了K&节的5.4和5.5节。 R,但我找不到任何可以解释这一点的东西。
任何人都可以帮我弄清楚为什么我的功能的第二个版本没有按照我的意愿行事吗?谢谢!
编辑:谢谢大家。令人难以置信的是,在没有注意到的情况下,您可以在多长时间内盯着相对简单的错误。有时没有比别人看一眼更好的补救措施了。
答案 0 :(得分:8)
此:
while(*s++)
;
由于后递增,将nul字节定位在字符串的末尾,然后在退出循环之前再次递增。在{nil:
之后复制t
scontents␀tcontents␀
打印s
将在第一个时间停止。
此:
while(*s)
s++;
当找到0时,从循环中断,所以你指向nul字节。 t
被复制到nul:
scontentstcontents␀
答案 1 :(得分:3)
这是一个一个接一个的问题。您的第二个版本会在评估测试的每个时间增加指针。原始增量减少一个时间 - 上次测试评估为0时,增量不会完成。因此,在第二个版本中,新字符串在原始终止\0
之后附加,而在第一个版本中,新字符串的第一个字符将覆盖\0
。< / p>
答案 2 :(得分:2)
while (*s) ++s;
当*s
为零时,操作减少一次,然后循环中断,而while (*s++)
形式中断,但最后一次增加s
。
严格来说,如果您尝试形成无效指针,后一种形式可能不正确(即UB)。当然,这是设计的,但这是一个例子:char x = 0, * p = &x; while (*x++) { }
。
独立于此,最好编写干净,可读和深思熟虑的代码,而不是试图超越自己。有时你可以在C中编写实用优雅的漂亮代码,有时候最好拼出正确的东西。使用您的判断,并向其他人寻求反馈(或在他们查看您的代码时观察他们的面孔)。
答案 3 :(得分:2)
此:
while (*s)
s++;
一旦*s
为'\0'
,就会停止,此时它会在s
处离开(因为它不会执行循环体)。
此:
while (*s++)
;
*s
'\0'
后
会立即停止,但仍会执行后递增++
,因此s
会在 {之后指向 {1}}。所以字符串终止'\0'
永远不会被覆盖,它仍然会终止字符串。
答案 4 :(得分:1)
让我们假设内存中有以下字符:
Address 0x00 0x01 0x02 0x03
------- ---- ---- ---- ----
0x8000 'a' 'b' 'c' 0
0x8004 ...
执行循环时,它会在内存中发生。
1. *s = 'a'
2. s = 0x8001
3. *s = 'b'
4. s = 0x8002
5. *s = 'c'
6. s = 0x8003
7. *s = 0;
8. s = 0x8004
9. end loop
在评估时,即使 * s 的值为0, * s ++ 也会使指针前进。
//向前移动直到它指向一个 0 字符
while (*s++);
它根本不起作用,因为 s 最终指向不同的地方。
总结一下,我们将垃圾值作为目标字符串中的最后一个字符。那个垃圾字符串是因为while循环超过'\ 0'的限制向前一步。
您可以使用以下代码消除它,我认为它是高效的
while (*s)
s++;
它在内存透视中执行如下。
1. *s = 'a'
2. s = 0x8001
3. *s = 'b'
4. s = 0x8002
5. *s = 'c'
6. s = 0x8003
7. *s = 0
8. end loop