Bo Sun said:
int p[4]={111, 222, 333, 444}, *q = p, p1, p2, p3;
p1 = *q++;
p2 = *(q++);
*p++ and *(q++) generate the same result, just drop the ()'s.
Again, the ()'s are not doing anything since ++ has higher precedence than
* as they are evaluated left to right.
It is, I think, better to say "++ binds more tightly" than "++
has higher precedence". In some sense these are the same thing,
but in another important way, they are very different.
The word "precedence" contains the word "precede", which means "to
go in front of" or "to be earlier than". People seem to read things
into this that are not there. For instance, in "f() + g() * h()",
one can say that "*" has "higher precedence" than "+". The assumption
people make is that g() and h() will get called first -- but there
is no such guarantee. A C compiler could actually call f() first,
then call h() and g() (in either order), then multiply, then add.
I think (without any real proof) that if one talks about "binding"
instead of "precedence", then shows how a parse tree works, it
helps people de-couple the concept of compile-time parse-tree
binding from that of run-time execution order.
In any case:
(It should indeed be 222.)
So in the first two cases you get the value q pointed to and then
increment q. In the last case you increment q and then get the value it
points to. So p1 points to p[0], p2 points to p[1], then you skip p[2] and
p3 points to p[3].
For:
p1 = *q++;
(and likewise for p2), you could actually "increment q" first, then
"get the value q pointed to before the increment", as if you had
written instead:
q++; p1 = q[-1];
or even the rather obfuscated:
p1 = (++q)[-1];
A C compiler might even generate the same machine code for all
three of these source code patterns.
C makes only these promises in the expression:
p1 = *q++;
- q will be incremented eventually; this will be visible some time
between "the previous sequence point" and "the next sequence
point".
- The value q had before the increment will be an operand to the
unary "*" operator.
- The unary "*" operator will find the object the pointer points
to.
- The value of that object (i.e., *q) will be obtained and, at
some point, be stored into the object named "p1". This store
will be visible some time between "the previous sequence
point" and "the next sequence point".
- The semicolon terminating this statement is a sequence point
(the "next" one as far as all of the above goes).
The sequence point from the semicolon is where the action stops.
All of the changes piled up -- "increment q", "find value of *(q
before increment)", and "store new value into p1" must all finish.
But any order these occur in is invisible, and if you write code
in which the order might *be* visible, you get no guarantees. In
some cases -- like f() + g() * h(), where each function prints "f()
called\n" and "g() called\n" and "h() called\n" -- the effect is
"unspecified behavior". Here nothing "bad" can happen, but the
output may occur in any order. In other cases, like "a
= i++;"
-- the effect is "undefined" behavior. With undefined behavior
all bets are off; arbitrarily bad things may happen (including
the example someone had earlier of "void main()" nearly frying
the computer monitor).
In this case, nothing depends on any underlying order, so the
code is correct: