Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > tricky assignment statemenent

Reply
Thread Tools

tricky assignment statemenent

 
 
Ike Naar
Guest
Posts: n/a
 
      12-31-2004
Consider the following code:

#include <stdlib.h>

struct s
{
/* ... */
struct s * next;
};

void f(struct s * q)
{
struct s * p = malloc(sizeof *p);
if (p != 0)
{
p = p->next = q; /* is this safe? */
}
}

In the context given above, is the statement "p = p->next = q;"
safe and equivalent to "p->next = q; p = q;"
or does it invoke undefined behaviour because p is modified and
dereferenced at the same time?

Kind regards,
Ike

--
mail to ike at iae dot nl
 
Reply With Quote
 
 
 
 
Joona I Palaste
Guest
Posts: n/a
 
      12-31-2004
Ike Naar <(E-Mail Removed)> scribbled the following:
> Consider the following code:


> #include <stdlib.h>


> struct s
> {
> /* ... */
> struct s * next;
> };


> void f(struct s * q)
> {
> struct s * p = malloc(sizeof *p);
> if (p != 0)
> {
> p = p->next = q; /* is this safe? */
> }
> }


> In the context given above, is the statement "p = p->next = q;"
> safe and equivalent to "p->next = q; p = q;"
> or does it invoke undefined behaviour because p is modified and
> dereferenced at the same time?


I would consider it undefined behaviour, as there is no sequence point
between the operations "p=p->next" and "p->next=q" in the code.

--
/-- Joona Palaste ((E-Mail Removed)) ------------- Finland --------\
\-------------------------------------------------------- rules! --------/
"Insanity is to be shared."
- Tailgunner
 
Reply With Quote
 
 
 
 
Ben Pfaff
Guest
Posts: n/a
 
      12-31-2004
Ike Naar <(E-Mail Removed)> writes:

> In the context given above, is the statement "p = p->next = q;"
> safe and equivalent to "p->next = q; p = q;"
> or does it invoke undefined behaviour because p is modified and
> dereferenced at the same time?


We had a long, long thread about this years ago. I should know,
I started it You could probably find it with Google. For
what it's worth, the conclusion was that everyone disagreed about
the actual answer, but almost everyone concluded that it was
better to stay on the safe side by writing it as two statements.
--
Ben Pfaff
email: http://www.velocityreviews.com/forums/(E-Mail Removed)
web: http://benpfaff.org
 
Reply With Quote
 
Andrey Tarasevich
Guest
Posts: n/a
 
      12-31-2004
Ike Naar wrote:
> Consider the following code:
>
> #include <stdlib.h>
>
> struct s
> {
> /* ... */
> struct s * next;
> };
>
> void f(struct s * q)
> {
> struct s * p = malloc(sizeof *p);
> if (p != 0)
> {
> p = p->next = q; /* is this safe? */
> }
> }
>
> In the context given above, is the statement "p = p->next = q;"
> safe and equivalent to "p->next = q; p = q;"
> or does it invoke undefined behaviour because p is modified and
> dereferenced at the same time?
> ...


The real question here is whether the old value of 'p' is accessed for
the sole purpose of determining its new value. For example, assignment

p = p->next;

is perfectly legal even though "p is modified and dereferenced at the
same time".

I think it's pretty clear that assignment

p = p->next = q;

(which associates from right to left) does not satisfy this requirement.
Therefore, this assignment produces undefined behavior.

--
Best regards,
Andrey Tarasevich

 
Reply With Quote
 
Emmanuel Delahaye
Guest
Posts: n/a
 
      12-31-2004
Ike Naar wrote on 31/12/04 :
> struct s * p = malloc(sizeof *p);
> if (p != 0)


Style preference:

if (p != NULL)


> {
> p = p->next = q; /* is this safe? */


Huh! Gurus know that. Simple programmers write simple things that show
clearly the intention:

p->next = q;
p = p->next;

(Well, I guess)

--
Emmanuel
The C-FAQ: http://www.eskimo.com/~scs/C-faq/faq.html
The C-library: http://www.dinkumware.com/refxc.html

"Mal nommer les choses c'est ajouter du malheur au
monde." -- Albert Camus.

 
Reply With Quote
 
Chris Croughton
Guest
Posts: n/a
 
      12-31-2004
On Thu, 30 Dec 2004 22:12:44 -0800, Ben Pfaff
<(E-Mail Removed)> wrote:

> Ike Naar <(E-Mail Removed)> writes:
>
>> In the context given above, is the statement "p = p->next = q;"
>> safe and equivalent to "p->next = q; p = q;"
>> or does it invoke undefined behaviour because p is modified and
>> dereferenced at the same time?

>
> We had a long, long thread about this years ago. I should know,
> I started it You could probably find it with Google. For
> what it's worth, the conclusion was that everyone disagreed about
> the actual answer, but almost everyone concluded that it was
> better to stay on the safe side by writing it as two statements.


If everyone disagrees, I'd call that "undefined" (as in the spec.
doesn't define it in an unambiguous way). It may, of course, then be a
defect in the specification which should be remedied at some point
(either to define the behaviour or to specifically state it as
'undefined').

It sounds like a job for comp.std.c.

Chris C
 
Reply With Quote
 
Ben Pfaff
Guest
Posts: n/a
 
      12-31-2004
Chris Croughton <(E-Mail Removed)> writes:

> If everyone disagrees, I'd call that "undefined" (as in the spec.
> doesn't define it in an unambiguous way). It may, of course, then be a
> defect in the specification which should be remedied at some point
> (either to define the behaviour or to specifically state it as
> 'undefined').
>
> It sounds like a job for comp.std.c.


We brought them into the picture and it didn't, if I recall
correctly, help much.
--
"I hope, some day, to learn to read.
It seems to be even harder than writing."
--Richard Heathfield
 
Reply With Quote
 
Ike Naar
Guest
Posts: n/a
 
      01-02-2005
Emmanuel Delahaye <(E-Mail Removed)> wrote:
: Ike Naar wrote on 31/12/04 :
:> struct s * p = malloc(sizeof *p);
:> if (p != 0)
: Style preference:
: if (p != NULL)

Not to mention "if (p)", "if( p!=0 )" or "if (NULL != p)" .
The discussion was not about style, though.

:> p = p->next = q; /* safe or UB ? */
: Huh! Gurus know that.

That's why I turned to this very group. To ask the gurus.

: Simple programmers write simple things that show
: clearly the intention:
: p->next = q;
: p = p->next;

And right they are. Do they also eat quiche ?

Kind regards,
Ike
 
Reply With Quote
 
Ike Naar
Guest
Posts: n/a
 
      01-02-2005
Ike Naar <(E-Mail Removed)> wrote:
: [...] is the statement "p = p->next = q;"
: safe and equivalent to "p->next = q; p = q;"
: or does it invoke undefined behaviour because p is modified and
: dereferenced at the same time?

Thanks to all who responded. The tricky part appeared in existing code
and at first sight it looked a bit suspicious to me; your responses
have confirmed those suspicions.
That said, the code seems to compile and run correctly on every
platform/compiler combination I tried.

I would have written it as two statements, just to be on the safe side.

Thanks again,
Ike
 
Reply With Quote
 
Lawrence Kirby
Guest
Posts: n/a
 
      01-04-2005
On Fri, 31 Dec 2004 05:50:38 +0000, Joona I Palaste wrote:

> Ike Naar <(E-Mail Removed)> scribbled the following:
>> Consider the following code:


....

>> void f(struct s * q)
>> {
>> struct s * p = malloc(sizeof *p);
>> if (p != 0)
>> {
>> p = p->next = q; /* is this safe? */
>> }
>> }

>
>> In the context given above, is the statement "p = p->next = q;"
>> safe and equivalent to "p->next = q; p = q;"
>> or does it invoke undefined behaviour because p is modified and
>> dereferenced at the same time?

>
> I would consider it undefined behaviour, as there is no sequence point
> between the operations "p=p->next" and "p->next=q" in the code.


However the standard says "Furthermore, the prior value shall be read only
to determine the value to be stored." In p = p->next = q the value
assigned to p is the result of the expression p->next = q. In the abstract
machine this expression must be evaluated fully in order to generate the
value to be assigned. The access to p in p->next is part of that
evaluation so it is very much being read in order to determine the new
value. Before you start formulting your objections to this consider
carefully the statements in the standard (5.1.2.3):

"The semantic descriptions in this International Standard describe the
behavior of an abstract machine in which issues of optimization are
irrelvant."

"In the abstract machine, all expressions are evaluated as specified by
the semantics."

Most counterarguments are based on the idea that you can determine the
value to be assigned to p without evaluating p->next explicitly. However
that is an argument that is based on optimization, not the application of
the semantics of assignment exactly as stated in the standard. The
standard explicitly forbids this line of reasoning.

Also consider that in the abstract machine operands must be evaluated
before the operation can be performed and a result generated.
Again, anything else would constitute an optimization and a departure
from stated semantics. (Noting short-circuit and conditional operators
whose stated semantics affect whether some operands are evaluated at all
depending on the value of another operand).

Lawrence

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Assignment operator self-assignment check Chris C++ 34 09-26-2006 04:26 AM
Augument assignment versus regular assignment nagy Python 36 07-20-2006 07:24 PM
a tricky if else(maybe not tricky but impossible) nirkheys@gmail.com C Programming 9 04-25-2006 06:13 PM
tricky assignment statemenent ccwork C Programming 3 02-03-2005 06:26 PM
tricky assignment statemenent ccwork C Programming 6 01-12-2005 03:59 AM



Advertisments