在C语言中,while循环中使用指针的后增操作。

4
我正在编写 strcat 函数。
/*appends source string to destination string*/

#include <stdio.h>

int main()
{
    char srcstr[100], deststr[100];
    char *psrcstr, *pdeststr;

    printf("\n Enter source string: ");
    gets(srcstr);
    printf("\n Enter destination string: ");
    gets(deststr);

    pdeststr = deststr;
    psrcstr = srcstr;

    while(*pdeststr++)
        ;
    while(*pdeststr++ = *psrcstr++)
        ;

    printf("%s", deststr);
    return 0;
}

对于 srcstr = " world"deststr = "hello",我得到的结果是 hello,但我期望看到的是 hello world,如果我将第一个 while 改成如下所示,那么我就会看到 hello world
while(*pdeststr);
    pdeststr++;

为什么我不能像第二个while一样,在第一个while中将所有内容写在同一行中?


3
请勿使用 gets() 函数。它不安全,而且甚至已经不再是 C 语言的一部分了。请使用 fgets() 函数代替。 - alk
1
又是一个很好的例子,为什么一次只做一件事情是首选的方式。 - alk
4
由于while(*pdeststr++);即使找到了空字符终止符,指针仍会增加。然后,下面的“复制”行将保持原始的空字符终止符不变,并且对于字符串处理函数来说是无用的。 - Weather Vane
1
@Hugo,在这个快节奏的世界中,请使用您能找到的最新材料。 - Weather Vane
2
吹毛求疵地说,在“正常工作”的版本中的 while (*pdeststr); 循环中有一个多余的分号。如果字符串为空,则此循环永远不会循环,如果不为空,则会无限循环。请删除这两个分号中的第一个分号。 - Jonathan Leffler
显示剩余5条评论
6个回答

5

你的单行循环

 while(*pdeststr++);

等同于

while(*pdeststr)
    pdeststr++;
pdeststr++;

由于后自增运算符是在测试条件之前执行的,但在确定测试值后执行。

因此,您可以通过以下方式进行适应:

 while(*pdeststr++);
 pdeststr--;

2
这个:“postincrement操作符在测试条件之前执行,但在确定测试值之后执行。”是一个可能的解释,但不一定正确。递增变量是++运算符的 副作用 ,副作用会一直持续到达到下一个序列点(sequence point)。如果 ++ 在其操作数之后,则表达式计算结果为递增之前的值。但你不知道事件的确切顺序,比如“测试条件” ;) - user2371524
2
笔误:不必要的 ;,可能导致无限循环。我相信后置递增发生的时间是未定义的。 - Weather Vane
更准确的说法是“在测试条件之前执行前缀递增运算符”。 - Weather Vane
@WeatherVane "undefined exactly"? ;) 我会用“没有完全定义”来表达,但是没错,就是这样。换句话说,在两个序列点之间的区域中,永远不要假设任何有关发生副作用状态的事情。 - user2371524
@FelixPalmen 抱歉我的英文不太好 ;) 我的意思是“什么时候未定义……” - Weather Vane

4

while条件的布尔值在++后缀递增之前计算。

因此,当您的while循环退出时,后缀递增运算符会执行一次,因此pdeststr指向紧接着单词"hello"后面的空字符char

然后程序的其余部分会在该空char之后附加更多数据。最终得到字符串"hello\0world\0"。打印函数认为字符串在遇到第一个空char时结束。


我正在写我的结论,然后看到了你的答案,很高兴能够确认我的结论,谢谢。 - Hugo

4

必要的介绍:不要使用 gets(),使用 fgets()!

您的问题在这里:

while(*pdeststr++)
    ;

增量的副作用是在最后一次迭代步骤中执行的(当 pdeststr 指向 NUL 结束符时),因此在此循环后,pdeststr 指向您的 NUL 结束符之后一个位置。请改为以下写法:
while(*pdeststr) ++pdeststr;

1
你多了一个增量,它指向了NULL字符之后,因此最终你只能打印出第一个字符串。

2
或者使用C语言的表示方式\0 :) - user2371524
@WeatherVane:但那是你的<tm>,不是吗? - alk
@alk同意使用'\0'更好。我的意思是,如果您正在使用一个单词来描述0 - Weather Vane
@alk 我也考虑过这个问题。其实并不重要...但是NULL最多只能算是误导性的。 - user2371524
虽然从严格意义上讲不算,但我认为char s[1]; s[0] = NULL;是错误的。没有任何理由写出这样的代码。 - alk
显示剩余2条评论

1

通过precedence,后缀递增运算符(ptr++)比间接引用运算符(*ptr优先级更高。因此

while(*pdeststr++);

该操作将始终先增加pdeststr,然后再评估先前指向的值。因此,当评估结果为0时,pdeststr实际上指向下一个元素,因此您的连接单词之间将有一个空终止符字符('\0')。

作为使用while循环的一行解决方案,您可以使用短路评估,如下所示:

while(*pdeststr && pdeststr++);

上面的代码片段将在*foo结果为0时停止,并且不会评估foo++部分。

3
优先级与总是执行副作用无关。 - Matteo Italia

0
我的结论是最终,deststr = "hello\0 world\0",因此printf("%s", deststr);找到第一个\0并输出hello

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接