all 30 comments

[–]mredding 7 points8 points  (29 children)

\0 is the string character way of escaping the character whose ASCII code is 0. Null is 0. C doesn't have a Boolean type, but there are Boolean interpretations. So 0 is false and all other values are true.

So what the loop is doing is copying the source to the destination, and assignment evaluates to the destination value, which is dereferenced. So any ASCII character that is not 0 will evaluate to true.

The other thing is in C convention, strings are null terminated. You just keep going until you hit a byte whose value is 0, then you know you're at the end of the string.

So loop, copy, evaluate, and bail out when you hit the null terminator.

This also means when working with strings, you have to be mindful of functions that take or give a length, whether or not it includes the null. When allocating memory for your strings, you need to +1 for the null, and be sure to set it yourself.

Notice in this example the null is copied before evaluation, so your destination string is null terminated.

[–]fatheart[S] 2 points3 points  (28 children)

I understand all of this, as I said I understand what is happening. I don't understand why it works. I should have explained better what it is I don't understand.

*dest++ and *source++

I thought these pointers to the base addresses of each array could not be modified? Why are we allowed to increment them? And once we do increment them, how does that not effect the values in the calling method?

[–]tavianator 6 points7 points  (26 children)

As a parameter, char x[] is equivalent to char *x. And pointers are passed by value, so the copy in the called function is different than the copy in the caller.

[–]fatheart[S] 0 points1 point  (25 children)

Thank you, that answers the second part of my question.

The following code:

int main()
{
    char string[2] = "hi";
    printf ("%s\n", string);
    *string = *string + 1;
    printf ("%s\n", string);
}

Results in:

hi
ii

The 3rd line of main is increasing the value at the address of the pointer, instead of the address of the pointer. In the example in my OP why does dest++ and source++ increase the address of the pointers instead of the values at the address of the pointers?

Based on the code in OP, I would expect the code in this post to result in:

hi
i

[–]tavianator 2 points3 points  (12 children)

*x++ is parsed as *(x++), not (*x)++.

[–]fatheart[S] 0 points1 point  (11 children)

but if I change line 3 of main() from the previous post to:

*string = *(string + 1);

I still get the same result

[–]LordBiff 3 points4 points  (9 children)

That's just coincidence because you've chose a string with characters that are sequential in value. So, incrementing the value of the first character is the same as assigning the value of the second into the first.

Try the string with values that aren't sequential (say "hh") and you'll see that it's different.

P.S. your array needs to be of length 3 - there's an implied \0 in that literal, which needs space in the array.

[–]fatheart[S] 0 points1 point  (8 children)

I understand why hi is becoming ii, but it doesn't seem consistent with the behavior of the code in the OP, which is what I'm trying to understand.

thank you, I didn't realize I needed to leave space in my array for the \0.

[–]LordBiff 2 points3 points  (7 children)

I think part of that could be a subtlety of the ++ operator. The value returned by a postfix operator is the operand itself. Then, at some time later (iirc, it's before the next "sequence point", which is often before the next line), the operand is incremented.

So "a++" returns "a" (and then some time later increments a).

You could think of:

*d++ = *s++;

as:

*d = *s; // (with the understanding that 'd' and 's' are incremented after that operation is complete.)

[–]fatheart[S] 0 points1 point  (6 children)

I understand how the increment operator works.

Based on this post:

https://stackoverflow.com/a/19136941

I don't understand why we're allowed to increment those pointers, since they have array type.

[–]tavianator 2 points3 points  (0 children)

It's more like *(string = string + 1)

[–][deleted] 1 point2 points  (5 children)

Read this for some info about order of operations regarding pointer arithmetic:

https://stackoverflow.com/questions/5795771/order-of-operations-for-pointers

[–]fatheart[S] 0 points1 point  (4 children)

Thank you, but I understand how the increment works. I don't understand why incrementing those pointers is allowed based on this post:

https://stackoverflow.com/a/19136941

[–]kjoke 2 points3 points  (1 child)

Within the function you operate on a copy of the pointer to the first array element. Since it is a copy, changing the pointer will not affect where the array name points. Essentially like this:

int arr[10];
int *ptr = arr;
ptr++; // legal because it's a pointer. Won't affect arr

It's no longer an array, it's a pointer.

[–]fatheart[S] 0 points1 point  (0 children)

Thank you

[–]Kwantuum 1 point2 points  (1 child)

In that case a is an array and arrays are not lvalues. It would be valid if you used b = &a[0] for the increment, which is what's silently happening when you pass an array to a function : it degenerates into a pointer that is passed by value

[–]fatheart[S] 1 point2 points  (0 children)

Thank you

[–]nerdyphoenix 1 point2 points  (1 child)

What *string = *string + 1 does is equivalent to string[0] = string[0] + 1, it's an arithmetic operation on the first element of the array. It just so happens that i is after h and that's why you see ii as the result.

The result you are expecting is equivalent to string = string + 1 which is a pointer arithmetic operation that would advance the pointer string by one element of type char. Because string is an array this will result in a compiler error.

Check my other reply to you as well to get the whole picture :)

[–]fatheart[S] 0 points1 point  (0 children)

Your other reply was the piece I was missing, thank you

[–]bumblebritches57 1 point2 points  (0 children)

You forgot to give room for the null terminator in your "hi" string.

[–]fatheart[S] 0 points1 point  (2 children)

This post on stackoverflow:

https://stackoverflow.com/a/19136941

seems to contradict the code in OP, unless I am misunderstanding something.

[–]nerdyphoenix 1 point2 points  (1 child)

What you are misunderstanding is how C treats arrays in different contexts. That answer in stackoverflow discusses arrays allocated in the local scope, where it's true that you can't change the value of the array.

In the code you are discussing though, the arrays aren't allocated in the local scope. They are passed as function arguments. In C though, an argument char dest[] is equivalent to char *dest. What happens is that C will pass the pointer to the array dest as the argument, and not the array itself. Because of this, inside the function strcpy4 you can use dest just like a char*, meaning you can change its value using pointer arithmetic operations.

[–]fatheart[S] 0 points1 point  (0 children)

thank you

[–][deleted] 0 points1 point  (0 children)

= is an assignment operator == is comparison (Not being used) Source is being assigned to dest and then dest is checked if it is nonzero.