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

时间:2009-05-01 04:01:27

标签: c

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

while (*s++ = *t++);

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

是否与指针有关?

17 个答案:

答案 0 :(得分:39)

相当于:

while (*t) {
    *s = *t;
    s++;
    t++;
}
*s = *t;

t指向的char为'\0'时,while循环将终止。在此之前,它会将t指向的char复制到s指向的char,然后递增st以指向下一个char他们的阵列。

答案 1 :(得分:29)

这有很多内容:

while (*s++ = *t++);

st变量是指针(几乎可以肯定是字符),s是目标。以下步骤说明了正在发生的事情:

  • 将t(*t)的内容复制到s(*s),一个字符。
  • st都会增加(++)。
  • 赋值(copy)返回已复制的字符(while)。
  • while继续,直到该字符为零(C中的字符串结尾)。

实际上,它是:

while (*t != 0) {
    *s = *t;
    s++;
    t++;
}
*s = *t;
s++;
t++;

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

答案 2 :(得分:19)

我们假设st是指向字符串的char *(假设s至少与t一样大)。在C中,字符串都以0结尾(ASCII“NUL”),对吗?那么这是做什么的:

*s++ = *t++;

首先,它*s = *t,将*t的值复制到*s。然后,它会s++,因此s现在指向下一个字符。然后它t++,所以t指向下一个字符。这与运算符优先级前缀与后缀增量/减量有关。

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

4 + 2 * 3

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

*s++中,我们有一元*(指针取消引用运算符)和一元++(后缀增量运算符)。在这种情况下,++具有比*更高的优先级(也称为“绑定更严格”)。如果我们说过++*s,我们会在 *s处增加值,而不是 s指向的地址,因为前缀增量具有较低的优先级*作为取消引用,但我们使用 postfix 增量,它具有更高的优先级。如果我们想要使用前缀增量,我们可以完成*(++s),因为括号会覆盖所有较低的优先级并强制++s先来,但这会产生不良的副作用。字符串开头的空字符。

请注意,仅仅因为它具有更高的优先级并不意味着它首先发生。在使用值之后发生后缀增量,这是*s = *ts++之前发生的原因。

现在您了解*s++ = *t++。但他们把它放在一个循环中:

while(*s++ = *t++);

这个循环没有什么都没有 - 动作都处于这种状态。但检查一下条件 - 如果*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

或者,请查看:

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

我们从获取长度开始,所以当我们结束时,我们做了更多的指针算法,将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);

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

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

答案 3 :(得分:16)

while(*s++ = *t++);

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

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

当它显然不是。

char tmp = 0;
do {
   tmp = *t;
   *s = tmp;
   s++;
   t++;
} while(tmp);

更像是

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

答案 4 :(得分:3)

这方面神秘的方面是操作的顺序。如果您查找C语言规范,它会声明在此上下文中,操作顺序如下:

1. * operator
2. = (assignment) operator
3. ++ operator

然后while循环变为,用英语:

while (some condition):
  Take what is at address "t" and copy it over to location at address "s".
  Increment "s" by one address location.
  Increment "t" by one address location.

现在,“某些条件”是什么? C lang规范还说赋值表达式的值是赋值本身,在本例中是*t

因此“某些条件”是“t指向非零的内容”,或以更简单的方式,“位置t处的数据不是NULL

答案 5 :(得分:1)

它的工作原理是将“t”指向的字符串中的字符复制到“s”指向的字符串中。对于每个字符副本,两个指针都会递增。循环在找到NUL字符(等于零,因此退出)时终止。

答案 6 :(得分:1)

HINTS:

  • 运营商'='做什么?
  • 表达式“a = b”的值是多少?例如:如果你做“c = a = b”c得到什么价值?
  • 什么终止了C字符串?它评估是真还是假?
  • 在“* s ++”中,哪个运算符具有更高的优先级?

通知:

  • 请改用strncpy()。

答案 7 :(得分:1)

Brian W. Kernighan和Dennis M. Ritchie的 C编程语言(K& R)详细解释了这一点。

第二版,第104页:

  

5.5字符指针和功能

     

字符串常量,写为

"I am a string"
     

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

     

最常见的字符串常量可能是函数的参数,如

printf("hello, world\n");
     

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

     

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

char *pmessage;
     

然后声明

pmessage = "now is the time";
     

pmessage指定一个指向字符数组的指针。这是字符串副本;只涉及指针。 C不提供任何操作符来处理整个字符串作为一个单元。

     

这些定义之间存在重要差异:

char amessage[] = "now is the time"; /* an array */
char *pmessage = "now is the time"; /* a pointer */
     

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

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

          +--------------------+
amessage: | now is the time \0 |
          +--------------------+
     

我们将通过研究从标准库改编的两个有用函数的版本来说明指针和数组的更多方面。第一个函数是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 ++;
}
     

相比之下,这是一个带有指针的strcpy版本:

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

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

     

在实践中,s不会像我们上面所示那样被写入。有经验的C程序员更喜欢

strcpy
     

这会将/* strcpy: copy t to s; pointer version 2 */ void strcpy(char *s, char *t) { while((*s++ = *t++) != '\0') ; } s的增量移动到循环的测试部分。 t的值是*t++t递增之前指向的字符;在获取此字符之前,后缀t不会更改++。以同样的方式,在t递增之前,角色将存储在旧的s位置。此字符也是与s进行比较以控制循环的值。最终效果是将字符从'\0'复制到t,直至并包括终止s

     

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

'\0'
     

虽然这看起来可能看起来很神秘,但是标记的便利性相当大,而且应该掌握成语,因为你会经常看到C程序。

     

标准库中的/* strcpy: cope t to s; pointer version 3 */ void strcpy(char *s, char *t) { while(*s++ = *t++); } strcpy)返回目标字符串作为其函数值。

这是本节相关部分的结尾。

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

答案 8 :(得分:0)

  1. 用于 int i
    char t[]="I am a programmer",s[20];
    for(int i=0;*(t+i)!='\0';i++)
        *(s+i)=*(t+i);
    *(s+i)=*(t+i); //the last char in t '\0'
    printf("t is:%s\n",t);
    printf("s is:%s\n",s);
  1. 用于 pointer ++
    char t[]="I am a programmer",s[20];
    char *p1,*p2;
    p1=t,p2=s;
    for(;*p1!='\0';p1++,p2++)
        *p2 = *p1;
    *p2 = *p1;
    printf("t is:%s\n",t);
    printf("s is:%s\n",s);
  1. pointer ++ 一起使用同时
    char t[]="I am a programmer",s[20];
    char *p1,*p2;
    p1=t,p2=s;
    while(*p2++=*p1++);
    printf("t is:%s\n",t);
    printf("s is:%s\n",s);
    printf("t is:%s\n",p1-18);
    printf("s is:%s\n",p2-18);
  1. 使用数组初始化指针
    char a[20],*t="I am a programmer",*s;
    s=a;
    while(*s++=*t++);
    printf("t is:%s\n",t-18);
    printf("s is:%s\n",s-18);
    printf("s is:%s\n",a);

答案 9 :(得分:0)

许多С语言的拥护者都相信“while(* s ++ = * t ++)” 是一种真正的恩典。

在循环“while”的条件表达式中,插入了三个副作用(一个指针的移位,第二个指针的移位,赋值)。

结果循环体是空的,因为所有功能都放在条件表达式中。

答案 10 :(得分:0)

说你有这样的事情:

char *someString = "Hello, World!";

someString 指向字符串中的第一个字符 - 在本例中为“H”。

现在,如果将指针递增1:

someString++

someString 现在指向'e'。

while ( *someString++ );

将循环,直到任何 someString 点变为NULL,这是信号字符串结束的信号(“NULL Terminated”)。

代码:

while (*s++ = *t++);

等于:

while ( *t != NULL ) { // While whatever t points to isn't NULL
    *s = *t;           // copy whatever t points to into s
    s++;
    t++;
}

答案 11 :(得分:0)

它复制一个字符串,因为数组总是通过引用传递,而string只是一个char数组。基本上发生的事情是(如果我记得正确的话)指针算术。这是a bit more information from wikipedia on c arrays

您正在存储从t中取消引用的值,然后通过++移动到下一个索引。

答案 12 :(得分:-1)

启动while循环....

* s = * t首先,这指定t指向什么点。即,它将一个字符从t字符串复制到s字符串。

被分配的内容被传递给while条件...任何非零都是“true”所以它将继续,0为false,它将停止....并且它恰好发生在字符串的结尾是也是零。

s ++和t ++他们增加指针

这一切都重新开始

所以它不断分配循环,移动指针,直到它达到0,这是字符串的结尾

答案 13 :(得分:-1)

是的,它使用指针,并在评估while条件时完成所有工作。 C允许条件表达式产生副作用。

“*”运算符取消引用指针s和t。

增量运算符(“++”)在赋值后递增指针s和t。

循环在空字符的条件下终止,在C中计算为false。

另外一条评论......这不是安全的代码,因为它没有确保s有足够的内存分配。

答案 14 :(得分:-1)

我提供以下答案的问题已作为此问题的副本而结束,因此我在此处复制答案的相关部分。

while循环的实际语义解释如下:

for (;;) {
    char *olds = s;             // original s in olds
    char *oldt = t;             // original t in oldt
    char c = *oldt;             // original *t in c
    s += 1;                     // complete post increment of s
    t += 1;                     // complete post increment of t
    *olds = c;                  // copy character c into *olds
    if (c) continue;            // continue if c is not 0
    break;                      // otherwise loop ends
}

保存st的顺序以及st递增的顺序可以互换。在保存*oldt之后和使用c之前,oldtc的保存可能会发生。在c*olds保存后,c olds的分配可以随时进行。在我的信封背面,这至少有40种不同的解释。

答案 15 :(得分:-1)

是的,它确实与指针有关。

读取代码的方法是:“指针指向的值”s“(在此操作后递增)获取指针”t“指向的值(增加值)在此操作之后;此操作的整个值将计算为复制的字符的值;迭代此操作,直到该值等于零“。因为字符串null终止符的值是字符值零('/ 0') ,循环将迭代,直到字符串从t指向的位置复制到s指向的位置。

答案 16 :(得分:-2)

这是真的,就char而言,如果没有\ 0并且它是一个整数数组,程序将崩溃,因为会有一个地址,其元素不是数组或指针的一部分,如果系统具有使用malloc分配的内存,则系统将继续提供内存