This is definitely a strange and surprising result. To understand what's going on, it will help to take a closer result at what the "autoincrement" operators ++x
and x++
really do.
Most expressions simply compute new values. If I say
a = b * 3;
that means, "take the value of the variable b
, multiply it by 3, and that's the value we'll assign to a
". Similarly, if I say
a = a + 1;
that means, "take the (old) value of the variable a
, add 1 to it, and that's the new value we'll assign back to a
".
But ++
is special, because it has the "assign the value back" part built in. Any time you use ++
(or --
), two things are happening: we're computing a new value, but we're also modifying the variable whose value we just fetched.
To make this very clear, if I say
a = ++b;
that means, "take the value of the variable b
, add 1 to it, assign that new value back to b
, and that's the value we'll assign to a
". That's for the "prefix" form ++b
. For b++
, it's a little different:
a = b++;
That means, "take the value of the variable b
, add 1 to it, assign that new value back to b
, but the value we'll assign to a
is the old value of b
, before we added 1 to it." In other words, the value of the subexpression b++
, the value that "pops out" to participate in the larger expression, is the old value of b
.
The other thing to keep in mind here is that when it comes to assigning values, we obviously need a variable to assign the value to. We can't say
3 = b * 3; /* WRONG */
On the right-hand side of the =
sign, we fetch b
's value and multiply it by 3, but then where we store the new value? On the left-hand side of the =
sign, 3
is not the name of a variable, nor is it any kind of a location where we can store a value. So an assignment like this is illegal.
(Formally, what we've been talking about here is the difference between an rvalue and an lvalue. Those are interesting and useful terms that you might want to learn about some day, perhaps even today, but I'm not going to say anything more about them for now.)
But now we have almost enough information to answer your original question. Let's look at the expression that worked:
++*ptr++
What the heck does that mean?
In one sense, it's kind of meaningless, because it's not something that you would probably ever write in a real program. It has very little practical value, which is actually kind of good, which means it's not so bad that, at first glance, it's pretty badly cryptic, in that it's not obvious what it should do.
To understand what it does, we have to be clear about the precedence. Which operands bind more tightly to their operands? Precedence is what tells us that if we write
1 + 2 * 3
the multiplication operator *
binds more tightly, meaning that the expression is evaluated as if we had written
1 + (2 * 3)
Now, it happens that the autoincrement operator ++
binds more tightly that the unary contents-of operator *
. That is, when we write
++*ptr++
the expression is evaluated as if we had written
++ *(ptr++)
So the first thing we're going to do, inside the parentheses, is ptr++
. This means, as we saw before, "take the value of the variable ptr
, add 1 to it, assign that new value back to ptr
, but the value that pops out to the larger expression is the old value of ptr
, before we added 1 to it."
And then the next thing that happens in the "larger expression" is the *
or contents-of operator. *
works on a pointer, and accesses the object pointed to by the pointer. In your original program, the object pointed to by the pointer ptr
was the first cell of the array arr
, that is, arr[1]
. So *
is going to operate on whatever the old value of ptr
was, whatever ptr
used to point to. And that's arr[0]
. So what we end up doing is the equivalent of
++(arr[0])
We're going to take arr[0]
's old value, add 1 to it, store it back in arr[0]
, and (since this is prefix ++
we're talking about), the value that will "pop out" to the larger expression would be the new value of arr[0]
. So, if you had written
printf("%d\n", ++*ptr++);
it would have printed the new value of arr[0]
, or 4.
The bottom line is that although ++*ptr++
is a complicated-looking expression that's hard to understand and does something so obscure that it might not even be useful, it does do something, and is legal.
So now, finally, it's time to look at
*++ptr++
What does that do?
The first thing we have to know is whether prefix ++
or postfix ++
binds more tightly. It's a question that hardly ever comes up (and we're about to see why), but the answer is that postfix ++
binds more tightly. So this expression is interpreted as if you had written
* ++(ptr++)
So, once again, the first thing we're going to do is take ptr
's value, add 1 to it, store that new value back into ptr
, and then the value that's going to "pop out" to the larger expression is going to be the old value — but only the old value — of ptr
.
Let me say that again. The value that "pops out" to the larger expression is just the old value of ptr
. By that time we no longer know or care that it was the variable ptr
that we got this value from.
So then we come to the prefix ++
. And now we have a serious problem. Remember, ++
wants to fetch a value from an object, add 1 to it, and store the new value back into an object. But at this point we don't have an object to fetch from or store to, we just have a value — remember, the old value of the variable ptr
.
This will be easier to understand if we think about integer variables, instead of pointer-to-integer. Suppose I said
int a;
int b = 5;
a = ++(b++);
So we fetch b
's value, which is 5, and add 1 to it, and store the new value — which is 6 — back in b
, and the value that "pops out" to the larger expression s the old value of b
. So now it's as if we had written
a = ++5; /* WRONG */
And this makes no sense. We can't "fetch the old value from the variable 5
", because 5 isn't a variable. It's just as wrong as when we said 3 = b * 3;
, and for the same reason.
You might also be interested in Question 4.3 in the C FAQ list.