complier problem

N

neha

Hi

My question is : Would the expression *p++=c be disallowed by the
complier.


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.


so can anyone explain this answer to me detail
 
E

Eric Sosman

neha wrote On 07/28/06 10:52,:
Hi

My question is : Would the expression *p++=c be disallowed by the
complier.


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.


so can anyone explain this answer to me detail

Before:

c = 42

p --> [ 3 ]
[ 1 ]
[ 4 ]

After:

c = 42

[ 42 ]
p --> [ 1 ]
[ 3 ]

Two things have changed: The value of c has been copied
to the object p formerly pointed to, and p has been
incremented to point to the next object. The first was
caused by *(something) = c, the second by the p++.
 
A

Andrey Tarasevich

neha said:
...
My question is : Would the expression *p++=c be disallowed by the
complier.
...
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.
...

The question does not provide enough information for an answer. And the given
answer does not make sense at all. What book is it? If it says what you say it
says, then you better get another book.

Note, that the question is specifically about whether the expression is allowed
or disallowed by the compiler or, in other words, whether the expression is
well-formed or ill-formed. In order to answer the question we need to know the
types of 'p' and 'c'. For example, if 'p' is of type 'void*', then it is illegal
to apply the '++' operator to it and the compiler will disallow it. If 'p' is of
type 'double*' and 'c' is of type 'struct Foo', then we have completely
unrelated types in assignment, which will also cause a diagnostic from the
compiler. And so on. Without having this extra information, there's no way to
say whether there's something there that must cause the compiler to complain.

The proposed answer, on the other hand, seems to address the well-known issue of
violating the object-access-and-sequence-points rule (don't know a better name
for it). Even if this rule is violated in some code, it does not make the code
ill-formed, i.e. no diagnostic is required from the compiler. It won't be
"disallowed" by the compiler even if there's a problem there. Instead, the code
will produce undefined behavior. There many are other ways, BTW, how the above
expression might produce undefined behavior. But this is irrelevant, since it
has nothing to do with being "disallowed by the compiler".
 
F

Frederick Gotham

neha posted:
My question is : Would the expression *p++=c be disallowed by the
complier.


I presume that that's a typo for:

*p++ = p;


Which means:

Take the object pointed at by "p", and store the value "p" in it. Then
increment "p". Quite like:

*p = p;

++p;

Which, in full context, would have to be something like:

int main()
{
void *ptr[2] = 0;

void **p = ptr;

*p++ = p;
}
 
S

spibou

Eric said:
neha wrote On 07/28/06 10:52,:
Hi

My question is : Would the expression *p++=c be disallowed by the
complier.


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.


so can anyone explain this answer to me detail

Before:

c = 42

p --> [ 3 ]
[ 1 ]
[ 4 ]

After:

c = 42

[ 42 ]
p --> [ 1 ]
[ 3 ]

Two things have changed: The value of c has been copied
to the object p formerly pointed to, and p has been
incremented to point to the next object. The first was
caused by *(something) = c, the second by the p++.

Actually three things have changed but I take it that one
of those changes is a typo.


Frederick said:
neha posted:



I presume that that's a typo for:

*p++ = p;

No , I think that the opening poster wrote what he intended.
He says "the value of p is accessed twice" which seems to
exclude *p++ = p

Spiros Bousbouras
 
E

Eric Sosman

Frederick Gotham wrote On 07/28/06 12:56,:
neha posted:





I presume that that's a typo for:

*p++ = p;


Which means:

Take the object pointed at by "p", and store the value "p" in it. Then
increment "p". Quite like:

*p = p;

++p;

`*p+ = p' exhibits undefined behavior that the two-line
rewrite does not. 6.5, paragraph 2:

"[...] Furthermore, the prior value shall be read
only to determine the value to be stored."

In `*p++ = p' there are two reads of p's value: the permitted
read on the l.h.s. as part of the ++, and another on the r.h.s.
(Some may think there's a third read to determine the value
that * dereferences, but not so: that value is not the value
of p, but the value of the subexpression p++.)

To put it another way: Why in the two-line rewrite did you
choose to put the assignment first and the increment second?
You could just as well have written

++p;
*p = p;

or (abusing C's syntax to emphasize the U.B.)

++...
*p = p;
...p;
 
C

Chris Torek

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
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.
 
A

Andrey Tarasevich

Chris said:
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.
...

Well, while it is true that what we need is the old value of 'p', what you are
saying seems to imply that the only way to obtain that old value of 'p' is to
preserve it ("capture it", "toss it into the air") when it is still available
(before 'p' is incremented). In reality, it is not required to be that way. The
compiler is free to perform the increment first without making any attempts to
"capture" the old value of 'p' and later, when it actually needs the old value
of 'p', it can obtain it by subtracting 1 from the current (already incremented)
value of 'p'.
 
C

Chris Torek

[in a situation in which the value of "p++" is used...]

Well, while it is true that what we need is the old value of 'p',
what you are saying seems to imply that the only way to obtain that
old value of 'p' is to preserve it ("capture it", "toss it into
the air") when it is still available (before 'p' is incremented).
In reality, it is not required to be that way. The compiler is free
to perform the increment first without making any attempts to
"capture" the old value of 'p' and later, when it actually needs
the old value of 'p', it can obtain it by subtracting 1 from the
current (already incremented) value of 'p'.

This is a valid implementation-specific technique (via the
"as-if" rule), provided p itself is not marked volatile.

That is, regardless whether the compiler turns:

save = p++;

into:

/* effective C code; and machine instructions */
save = p; /* add r1, 0, r2 */
p = p + 1; /* add r1, 1, r1 */

or:

p = p + 1; /* add r1, 1, r1 */
save = p - 1; /* add r1, -1, r2 */

the outcome is the same, as long as p is not "volatile". (And the
code runs just as fast either way, since both sequences use two
"add" instructions with embedded immediate constants; the only
difference is the value of those constants.)

If p *is* marked "volatile", however, the compiler cannot use the
second sequence, since that causes two reads from the object. (As
yet another wrinkle, "what constitutes an access" to one of these
things marked volatile "is implementation-defined", so if the
implementation can provide a particularly interesting interpretation,
it may actually be able to do the "add one, then re-read, then
subtract one" after all.)
 
A

Ark

Chris said:
[in a situation in which the value of "p++" is used...]

Well, while it is true that what we need is the old value of 'p',
what you are saying seems to imply that the only way to obtain that
old value of 'p' is to preserve it ("capture it", "toss it into
the air") when it is still available (before 'p' is incremented).
In reality, it is not required to be that way. The compiler is free
to perform the increment first without making any attempts to
"capture" the old value of 'p' and later, when it actually needs
the old value of 'p', it can obtain it by subtracting 1 from the
current (already incremented) value of 'p'.

This is a valid implementation-specific technique (via the
"as-if" rule), provided p itself is not marked volatile.

That is, regardless whether the compiler turns:

save = p++;

into:

/* effective C code; and machine instructions */
save = p; /* add r1, 0, r2 */
p = p + 1; /* add r1, 1, r1 */

or:

p = p + 1; /* add r1, 1, r1 */
save = p - 1; /* add r1, -1, r2 */

the outcome is the same, as long as p is not "volatile". (And the
code runs just as fast either way, since both sequences use two
"add" instructions with embedded immediate constants; the only
difference is the value of those constants.)

If p *is* marked "volatile", however, the compiler cannot use the
second sequence, since that causes two reads from the object. (As
yet another wrinkle, "what constitutes an access" to one of these
things marked volatile "is implementation-defined", so if the
implementation can provide a particularly interesting interpretation,
it may actually be able to do the "add one, then re-read, then
subtract one" after all.)

Looks like a storm in a teacup. ['save' doesn't have to exist at all,
depending on the instruction set, and *p++=c; may execute in a single
instruction]
p++ has a value (old p) and a side effect (p = old p + 1).
*p++ dereferences the value of p++ (old p).
I'd care about implementation only when shopping for a compiler (or
squeezing out that last optimization)
Sorry, fail to see any mystery or flying eggs :)
Regards,
Ark
 
C

Chris Torek

Chris said:
[the compiler can produce varying machine code for the C source:]
save = p++;

Looks like a storm in a teacup. ['save' doesn't have to exist at all,
depending on the instruction set,

The intent of this later example -- which I admit is not clear if
you follow the whole thread and think that it is *not* a separate
example -- is that the "save = p++;" is a complete line of C source
code. Hence "save" *does* exist, right there in the source code. :)
and *p++=c; may execute in a single instruction]

On some architectures (try it on a SPARC or MIPS!); but this is a
different line of source code anyway. :) And of course, while a
number of architectures have a single instruction that implements
"*p++ = expr" and "*--p = expr", many of those do not have a single
instruction that implements "*++p = expr" or "*p-- = expr". (But
some do.)
p++ has a value (old p) and a side effect (p = old p + 1).
*p++ dereferences the value of p++ (old p).

(Correct -- but can you say that in no fewer than 1000 words? :) )
I'd care about implementation only when shopping for a compiler (or
squeezing out that last optimization)

Indeed, this is an appropriate attitude.
 
D

Dann Corbit

neha said:
Hi

My question is : Would the expression *p++=c be disallowed by the
complier.


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.


so can anyone explain this answer to me detail

Your question is similar to a FAQ, did you read it before posting?:

----------------------------------------------------
4.3: Does *p++ increment p, or what it points to?

A: Postfix ++ essentially has higher precedence than the prefix
unary operators. Therefore, *p++ is equivalent to *(p++); it
increments p, and returns the value which p pointed to before p
was incremented. To increment the value pointed to by p, use
(*p)++ (or perhaps ++*p, if the order of the side effect doesn't
matter).

References: K&R1 Sec. 5.1 p. 91; K&R2 Sec. 5.1 p. 95; ISO
Sec. 6.3.2, Sec. 6.3.3; H&S Sec. 7.4.4 pp. 192-3, Sec. 7.5 p.
193, Secs. 7.5.7,7.5.8 pp. 199-200.
----------------------------------------------------
A side point:
I would never rely on the compiler to reject a nonsense code fragment.
There are some types of problems that the compiler MUST issue a diagnostic
message for, but there are other things that are totally wrong but which the
compiler does not have to diagnose.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,774
Messages
2,569,598
Members
45,151
Latest member
JaclynMarl
Top