In article <11**********************@i42g2000cwa.googlegroups .com>
neha <ne*********@gmail.comwrote:
>My question is : Would the expression *p++ = c be disallowed by the
complier.
(As an aside, remarkably few things are "disallowed" in C even if they
make no sense. :-) See <http://web.torek.net/torek/c/compiler.html>
for additional remarks.)
>The answer given in the book is: No.Because here even though the vlue
of p is acced twice it is used to modify two different objects p and
*p.
This answer is ... not so great.
Let us assume that the expression:
*p++ = c
is made into a complete statement, so that there are "sequence
points" before and after it. We might as well also assume that p
and c have appropriate types and useful values, otherwise the entire
thing falls apart.
Given these assumptions, we can next note that:
*p++ = c;
is parsed the same as:
(*(p++)) = (c);
This has four sub-expressions, each of which may be evaluated in
any order (perhaps even in parallel), with the obvious requirement
that the value of the "something"s below be known by the time they
are used:
p++
c
*(something) [specifically *p]
(something) = (something) [specifically *p = c]
Each of these four sub-expressions produces a value (of some type);
each can, but need not, also have some "side effect". A "side
effect" is, essentially, a change to something in memory: putting
a new value in some object. (In computer science, "side effects"
can also refer to printing output and the like, but we can ignore
all that here.)
Two of the four expressions above do in fact have side effects,
namely, "p++" and "(something) = (something)".
In C, side effects that cause modifications to objects are allowed
to take place anywhere between "sequence points". If you wish to
avoid undefined behavior, you -- the programmer -- must make sure
that when you make such modifications, you do not "re-access" the
value of the object later, unless the "next" sequence point has
already happened, so that the new value has had time to settle in
to its object.
Think of the new values as raw eggs being tossed in in the air by
a juggler. He is allowed to get as many eggs (values) in the air
as he likes, as long as he catches them all by the next sequence
point, and packs them carefully into their egg-holders (objects)
by then.
What happens if you throw *two* raw-egg-values into the air, aiming
both of them at the same egg-holder? One likely answer is: they
collide and break, making a big mess. So "don't do that".
Now, in this case, we have two new values that have to happen:
"value of p + 1" goes into "p", and "value of c" goes into "*p".
This is where the most subtle point comes in.
The expression "*p = c" modifies *p. Of course, this needs the
value of "p". So "*p++" modifies *p++ ... which needs the value
of ... what?
The obvious (and correct) answer is "the value of p++", but what
exactly *is* the "value of p++"? The answer is *not* "go back and
look at p again", and this is a crucial point! The value of
"p++" is the value p had before the new p+1 value goes into p,
but this value has been "captured", in a sense, by tossing it
up into the air, as one of those raw eggs.
In other words, when you have:
*p++ = c;
the compiler can toss "the value of c" into the air; it does toss
"the old value of p, before incrementing" into the air; and it can
now toss "the new value of p, after incrementing" into the air.
At this point, it has everything it needs to handle the ordinary
assignment expression: it grabs "the old value of p" out of the
air and feeds that to the unary "*" operator. This finds whatever
object is at "*(old value of p)". Since the point of finding that
object is to change it -- to set it to "the value of c" -- the
compiler now aims "the value of c" (either getting it and tossing
it in the air now, if needed, or using the one that is already
there) at this object. The "value of c" egg is now aimed at the
"object at *old_p" egg-cup.
(At this point, there are only two values in the air: "value of c"
aimed towards *old_p and "new value for p" aimed at p. What happened
to "four expressions"? Well, one of them -- *(something) -- was
used to locate an object, so it is not "in the air" anymore. The
last one is the value of the entire assignment, i.e., the value
once *(something) is set. This value is aimed at the trash-can,
so if the compiler is any good, it does not even bother throwing
it up into the air in the first place.)
Now we come to the sequence point. Here, all the action has to
stop for a moment: the new value for p has to get stored safely
into p, and the new value for *(old value of p) has to get stored
safely there. All the "eggs" land in their targets -- with the
value of the overall assignment going into the trash -- and the
sequence point is satisfied; now the compiler can move on to the
next statement.
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: forget about it
http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.