"while(*s ++ =*t ++)"如何复制字符串?

Dev*_*ted 55 c

我的问题是,这段代码是做什么的(来自http://www.joelonsoftware.com/articles/CollegeAdvice.html):

while (*s++ = *t++);
Run Code Online (Sandbox Code Playgroud)

网站说上面的代码复制了一个字符串,但我不明白为什么......

它与指针有关吗?

Jer*_*ten 41

它相当于:

while (*t) {
    *s = *t;
    s++;
    t++;
}
*s = *t;
Run Code Online (Sandbox Code Playgroud)

t指向的char时,'\0'while循环将终止.在此之前,它将复制t指向指向的char的char s,然后递增st指向其数组中的下一个char.

  • 请参阅下面的答案.这个答案是对的. (6认同)

pax*_*blo 31

这有很多内容:

while (*s++ = *t++);
Run Code Online (Sandbox Code Playgroud)

st变量的指针(几乎可以肯定字符),s作为目的地.以下步骤说明了发生的情况:

  • t(*t)的内容被复制到s(*s),一个字符.
  • s并且t都是递增的(++).
  • 赋值(copy)返回被复制的字符(到while).
  • 所述while继续进行,直到该字符是零(字符串中的端部C).

实际上,它是:

while (*t != 0) {
    *s = *t;
    s++;
    t++;
}
*s = *t;
s++;
t++;
Run Code Online (Sandbox Code Playgroud)

但是以更紧凑的方式写出来.


Chr*_*utz 19

让我们假设s,并tchar *指向字符串S(并假设s至少是一样大t).在C中,字符串都以0(ASCII"NUL")结尾,对吗?那么这是做什么的:

*s++ = *t++;
Run Code Online (Sandbox Code Playgroud)

首先,它确实*s = *t将值复制*t*s.然后,它确实s++如此,所以s现在指向下一个字符.它确实t++如此,所以t指向下一个角色.这与运算符优先级前缀与后缀增量/减量有关.

运算符优先级是解析运算符的顺序.举个简单的例子,看看:

4 + 2 * 3
Run Code Online (Sandbox Code Playgroud)

是这个4 + (2 * 3)还是(4 + 2) * 3?好吧,我们知道它是第一个因为优先级 - 二进制*(乘法运算符)具有比二进制+(加法运算符)更高的优先级,并且首先被解析.

*s++,我们有一元*(指针解引用运算符)和一元++(后缀增量运算符).在这种情况下,++具有更高的优先级(也称为"绑定更严格")*.如果我们说过++*s,我们会增加 *s而不是指向地址,s因为前缀增量具有较低的优先级*作为取消引用,但我们使用了后缀增量,它具有更高的优先级.如果我们想要使用前缀增量,我们可以这样做*(++s),因为括号会覆盖所有较低的优先级并被强制++s先行,但这会产生不希望的副作用,即在字符串的开头留下一个空字符.

请注意,仅仅因为它具有更高的优先级并不意味着它首先发生.Postfix增量特别在使用该值之后发生,这是他*s = *t之前发生的原因s++.

所以现在你明白了*s++ = *t++.但他们把它放在一个循环中:

while(*s++ = *t++);
Run Code Online (Sandbox Code Playgroud)

这个循环什么-行动是一切的条件.但检查一下条件 - 如果*s为0 则返回"false" ,这意味着*t为0,这意味着它们位于字符串的末尾(对于ASCII"NUL"而言).所以这个循环只要有字符就会循环t,然后尽职尽责地复制它们s,递增st完全复制.当这个循环退出时,s已经NUL终止,并且是一个正确的字符串.唯一的问题是,s指向最后.保持另一个指向开头的指针s(即循环s之前while()) - 将是你复制的字符串:

char *s, *string = s;
while(*s++ = *t++);
printf("%s", string); // prints the string that was in *t
Run Code Online (Sandbox Code Playgroud)

或者,看看这个:

size_t i = strlen(t);
while(*s++ = *t++);
s -= i + 1;
printf("%s\n", s); // prints the string that was in *t
Run Code Online (Sandbox Code Playgroud)

我们从获取长度开始,所以当我们结束时,我们做了更多的指针算法,以便s在开始时放回去.

当然,这段代码片段(以及我的所有代码片段)都会忽略缓冲区问题.更好的版本是这样的:

size_t i = strlen(t);
char *c = malloc(i + 1);
while(*s++ = *t++);
s -= i + 1;
printf("%s\n", s); // prints the string that was in *t
free(c);
Run Code Online (Sandbox Code Playgroud)

但是你已经知道了,或者你很快会在每个人最喜欢的网站上提出一个问题.;)

*实际上,它们具有相同的优先级,但这是由不同的规则解决的.在这种情况下,它们实际上具有较低的优先级


Pat*_*ter 16

while(*s++ = *t++);
Run Code Online (Sandbox Code Playgroud)

为什么人们认为它等同于:

while (*t) {
    *s = *t;
    s++;
    t++;
}
*s = *t; /* if *t was 0 at the beginning s and t are not incremented  */
Run Code Online (Sandbox Code Playgroud)

什么时候显然不是.

char tmp = 0;
do {
   tmp = *t;
   *s = tmp;
   s++;
   t++;
} while(tmp);
Run Code Online (Sandbox Code Playgroud)

更喜欢它

编辑:更正了编译错误.该tmp变量必须在循环之外声明.


小智 5

Brian W. Kernighan 和 Dennis M. Ritchie 所著的C 编程语言(K&R) 对此给出了详细的解释。

第二版,第 104 页:

5.5 字符指针和函数

一个字符串常量,写为

"I am a string"
Run Code Online (Sandbox Code Playgroud)

是一个字符数组。在内部表示中,数组以空字符终止'\0',以便程序可以找到结尾。因此,存储长度比双引号之间的字符数多 1。

也许字符串常量最常见的出现是作为函数的参数,如

printf("hello, world\n");
Run Code Online (Sandbox Code Playgroud)

当程序中出现这样的字符串时,访问它是通过字符指针;printf接收一个指向字符数组开头的指针。也就是说,字符串常量是通过指向其第一个元素的指针来访问的。

字符串常量不必是函数参数。如果pmessage声明为

char *pmessage;
Run Code Online (Sandbox Code Playgroud)

然后声明

pmessage = "now is the time";
Run Code Online (Sandbox Code Playgroud)

分配给pmessage一个指向字符数组的指针。这不是字符串副本;仅涉及指针。C 不提供任何将整个字符串作为一个单元进行处理的运算符。

这些定义之间有一个重要的区别:

char amessage[] = "now is the time"; /* an array */
char *pmessage = "now is the time"; /* a pointer */
Run Code Online (Sandbox Code Playgroud)

amessage是一个数组,大小足以容纳字符序列并'\0'对其进行初始化。数组中的各个字符可能会被更改,但amessage将始终引用相同的存储空间。另一方面,pmessage是一个指针,初始化为指向一个字符串常量;指针随后可能会被修改为指向其他地方,但如果您尝试修改字符串内容,结果是未定义的。

          +---+       +--------------------+
pmessage: | o-------->| now is the time \0 |
          +---+       +--------------------+

          +--------------------+
amessage: | now is the time \0 |
          +--------------------+
Run Code Online (Sandbox Code Playgroud)

我们将通过研究改编自标准库的两个有用函数的版本来说明指针和数组的更多方面。第一个函数是strcpy(s,t)将字符串复制t到字符串s。最好只是说,s = t但这会复制指针,而不是字符。要复制字符,我们需要一个循环。数组版本首先是:

/* strcpy: copy t to s; array subscript version */
void strcpy(char *s, char *t)
{
    int i;

    i = 0;
    while((s[i] = t[i]) != '\0')
        i ++;
}
Run Code Online (Sandbox Code Playgroud)

作为对比,这里有一个带有指针的版本strcpy

/* strcpy: copy t to s; pointer version 1 */
void strcpy(char *s, char *t)
{
    while((*s = *t) != '\0')
    {
        s ++;
        t ++;
    }
}
Run Code Online (Sandbox Code Playgroud)

因为参数是按值传递的,所以可以以任何喜欢的方式strcpy使用参数。在这里,它们是方便初始化的指针,它们沿着数组一次一个字符地行进,直到终止的被复制到。st'\0'ts

实际上,strcpy不会像我们上面展示的那样写。有经验的 C 程序员会更喜欢

/* strcpy: copy t to s; pointer version 2 */
void strcpy(char *s, char *t)
{
    while((*s++ = *t++) != '\0')
        ;
}
Run Code Online (Sandbox Code Playgroud)

s这会将和的增量移动t到循环的测试部分。的值是之前所指向的被递增的*t++字符;在获取该字符之前,后缀不会更改。同样,字符被存储到增量之前的旧位置。该字符也是用于比较以控制循环的值。最终效果是字符从to复制到,直到并包括终止。tt++tss'\0'ts'\0'

作为最后的缩写,请注意与 的比较'\0'是多余的,因为问题仅仅是表达式是否为零。所以该函数可能会写成

/* strcpy: cope t to s; pointer version 3 */
void strcpy(char *s, char *t)
{
    while(*s++ = *t++);
}
Run Code Online (Sandbox Code Playgroud)

尽管乍一看这似乎很神秘,但表示法相当方便,并且应该掌握该习惯用法,因为您会在 C 程序中经常看到 if 。

strcpy标准库中的 ( )<string.h>返回目标字符串作为其函数值。

本节相关部分到此结束。

PS:如果您喜欢阅读本文,请考虑购买 K&R 的副本 - 它并不昂贵。