Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does while (*p2++ = *p1++); mean? [duplicate]

Tags:

c

I have a code block:

int main ()
{
    char *p1 = "Hello";
    char *p2;
    p2 = (char*)malloc (20);
    memset (p2, 0, 20);
    while (*p2++ = *p1++);
    printf ("%s\n", p2);
}

But I can not explain the working of the line while (*p2++ = *p1++); Could you let me know the order of operation in this formula?

like image 792
mommomonthewind Avatar asked May 16 '13 07:05

mommomonthewind


3 Answers

It's classic C code trying to look extremely clever by putting everything in one line.

while (*p2++ = *p1++); is equivalent to

strcpy(p2, p1);
p1 += strlen(p1) + 1;
p2 += strlen(p2) + 1;

In other words, it copies a null-terminated string, with p1 ending up pointing to the end of the source string and p2 pointing to the end of the destination string.

like image 124
user541686 Avatar answered Oct 15 '22 22:10

user541686


This is a string copy, but you are losing the original pointer value. You should save the original pointer value.

int main ()
{
    char *p1 = "Hello";
    char *p2 = malloc(20);
    char *p3 = p2;
    memset (p2, 0, 20);
    while (*p2++ = *p1++);
    printf ("%s\n", p3);
}

The actual semantic explanation of the while loop would be something like:

for (;;) {
    char *q2 = p2;              // original p2 in q2
    char *q1 = p1;              // original p1 in q1
    char c = *q1;               // original *p1 in c
    p2 += 1;                    // complete post increment of p2
    p1 += 1;                    // complete post increment of p1
    *q2 = c;                    // copy character *q1 into *q2
    if (c) continue;            // continue if c is not 0
    break;                      // otherwise loop ends
}

The order that q1 and q2 are saved, and the order that p2 and p1 are incremented may be interchanged. The save of *q1 to c can occur any time after q1 is saved. The assignment of c to *q2 can occur any time after c is saved. On the back of my envelop, this works out to at least 40 different interpretations.

like image 39
jxh Avatar answered Oct 16 '22 00:10

jxh


The while loop is evaluating the expression: *p2++ = *p1++. The while loop expression:
*p2 = *p1 is evaluated using the result of *p1. However, this value is still assigned to *p2 even if the expression evaluates as false or (0). Rewriting this:

char c;

do
{
    c = *p1; /* read the src byte */
    *p2 = c; /* write to dst byte */

    p2++, p1++; /* increment src, dst pointers */
}
while (c != 0);

You will notice that a read / write will occur at least once. That's OK, as long as the C string p1 is nul-terminated, and p2 has enough storage for the C string. That is, malloc should allocate at least strlen(p1) + 1 bytes. In this code provided, this is true.

As others have pointed out, the final iteration will leave p1 at an address one-past-the-end, which is still a valid pointer, but has undefined results when dereferenced. The address of p2 is both a valid pointer, and a valid dereference, since you're allocating 20 bytes. However, p2 does not point to the C string copy any longer. What you want is an equivalent to:

char *p1 = "Hello";
char *p2, *tmp;

p2 = (char*)malloc (20);
memset (p2, 0, 20);

tmp = p2;
while (*tmp++ = *p1++);

printf ("%s\n", p2);

Most operating systems will release the memory at p2 on exit from main, but it is good practice to unwind the resources with a corresponding call to:

free(p2);

at the end. While on the subject of good practice, you should also check the return value of malloc to ensure that the allocation succeeded.

like image 1
Brett Hale Avatar answered Oct 16 '22 00:10

Brett Hale