user1542389
user1542389

Reputation: 11

C - Is *x++ = *y++ well defined?

Recently I saw a project using code like this:

while ((*dest++ = *src++) != '\0')
  ...

and I was wondering whether the behavior of *dest++ = *src++ is well defined? Will it always perform as:

while((*dest = *src) != '\0') {
  ++dest;
  ++src;

  ...
}

Or does this code fall into the category of a[i] = i++;?

Upvotes: 1

Views: 432

Answers (5)

M Buch
M Buch

Reputation: 1

Yes, it seems to be well defined. I took C a while ago, but I'll say that the pointers *dest and *src are pointing to the memory addresses of dest and src. It's fine if you try to increment the values of the dest and src variables themselves with the ++ operator. I hope this makes sense.

Upvotes: 0

Ken Clement
Ken Clement

Reputation: 768

The construct ((*dst++ = *src++) != 0) or '\0' which is the same as 0 IS both well-defined and is a common idiom especially in earlier C code. It copies one value of the pointer type from the src to the dest and positions the src and dst pointers to copy the next adjacent src value to the next adjacent dst location.

This is used in implementations of things like strcpy.

Upvotes: 0

msc
msc

Reputation: 34638

Yes. K&R by Brian W. Kernighan and Dennis M. Ritchie gives an explanation of this.

Second Edition, Page 104:

For contrast, here is a version of strcpy with pointers:

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

Because arguments are passed by value, strcpy can use the parameters s and t in any way it pleases. Here they are conveniently initialized pointers, which are marched along the arrays a character at a time, until the '\0' that terminates t has been copied to s.

In practice, strcpy would not be written as we showed it above. Experienced C programmers would prefer

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

This moves the increment of s and t into the test part of the loop. The value of *t++ is the character that t pointed to before t was incremented; the postfix ++ doesn't change t until after this character has been fetched. In the same way, the character is stored into the old s position before s is incremented. This character is also the value that is compared against '\0' to control the loop. The net effect is that characters are copied from t to s, up to and including the terminating '\0'.

As the final abbreviation, observe that a comparison against '\0' is redundant, since the question is merely whether the expression is zero. So the function would likely be written as

/* strcpy: cope t to s; pointer version 3 */
void strcpy(char *s, char *t)
{
    while(*s++, *t++);
}

Upvotes: 0

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726809

The code in question is almost identical to the code from K&R, and its behavior is well defined.

The reason there is no undefined behavior is that the four accesses on that line are done on three separate memory locations:

  • dest++ increments dest pointer,
  • src++ increments src pointer,
  • *dest = assigns what's pointed to by dest,
  • *src reads what's pointed to by src.

The code would become illegal if it tried to access the same location multiple times without a sequence point, e.g. x += x++. This is undefined behavior, because both ++ and += are modifying the same location of x.

Upvotes: 3

ScottK
ScottK

Reputation: 1556

x++ = y++ is illegal because the left hand side of an assignment must be a storage reference, but x++ is a value, not an lvalue.

*dest++ = *src++ is valid because in this case, dest is a pointer (that the ++ post-increments), but the assignment is to what dest points to, and so is a valid address which can be assigned a value.

If you look at an implementation of strcpy, it uses this construct:

char *strcpy(char *d, const char *s)
{
   char *saved = d;
   while (*s)
   {
       *d++ = *s++;
   }
   *d = 0;
   return saved;
}

Upvotes: 0

Related Questions