473,836 Members | 1,438 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Multiple Assignment Evaluation Debate

Hi All,

A coworker and I have been debating the 'correct' expectation of
evaluation for the phrase a = b = c. Two different versions of GCC
ended up compiling this as b = c; a = b and the other ended up
compiling it as a = c; b = c. (Both with no optimizations enabled).
How did we notice you may ask? Well, in our case 'b' was a memory
mapped register that only has a select number of writable bits. He
claims it has been a 'C Standard' that it will be evaluted as a = c; b
= c. I personally believe that it would make more sense for it to be
evaluated as b = c; a = b, although I would never write code that has a
questionable operation. Can anyone settle this debate?

Mar 10 '06
77 4773

Ben Pfaff wrote:
en******@yahoo. com writes:
Any compiler that compiles p = p->next = q as storing into p
before fetching p->next is simply broken. Operands are
evaluated before the operator they're operands for, and
operators are evaluated before they produce a value--that's
just how C semantics work (in a C abstract machine of course).


Do you have any citations to back up your assertions?


"Evaluation of an expression may _produce_ side effects."

"[At sequence points,] all side effects of _previous_
evaluations shall be complete [...]."

"In the abstract machine, all expressions are evaluated
_as specified by the semantics_."

"[An operator] may specify an operation to be performed
(which _in turn_ may _yield a value_ [...], _produce_ a
side effect, or some combination thereof) [...]."

"The syntax specifies the precedence of operators _in the
evaluation of_ an expression [...]."

"[Except ...], _the order of evaluation of subexpressions_
and _the order in which side effects take place_ are both
unspecified." (Not "order of A and B" but "order of A" and
"order of B".)

Keeping these in mind, read through section 6.5, noting
which paragraphs are marked "Semantics" . Expressions
are evaluated "as specified by the semantics".

Mar 14 '06 #31

pete wrote:
Ben Pfaff wrote:

en******@yahoo. com writes:
Any compiler that compiles p = p->next = q as storing into p
before fetching p->next is simply broken. Operands are
evaluated before the operator they're operands for, and
operators are evaluated before they produce a value--that's
just how C semantics work (in a C abstract machine of course).
Do you have any citations to back up your assertions?


I think he's confused about sequence points.


I assure you I understand sequence points quite well.
Some of the standard's function operator descriptions
seem to order the sequence of events between sequence points,
without proper respect to the concept of sequence points.

Here, we see the standard claim that the postfix increment
takes place after the result is obtained:
N869
6.5.2.4 Postfix increment and decrement operators
[#2] The result of the postfix ++ operator is the value of
the operand. After the result is obtained, the value of the
operand is incremented.

But, if we look at
N869
5.1.2.3 Program execution
[#16] EXAMPLE 7
"sum = (((sum * 10) - '0') + ((*(p++)) = (getchar())));
but the actual increment of p can occur at any time between
the previous sequence point and the next sequence point "

... we see the standard deny any ordering of events
between sequence points.


You're not reading carefully enough. The order in which
_side effects_ take place is unspecified, but _some_
evaluation must precede _any_ side effect, because it is
evaluations that produce side effects. The "at any time"
in the comment above means relative to other side effects.

Mar 14 '06 #32

Micah Cowan wrote:
Michael Mair <Mi**********@i nvalid.invalid> writes:
Richard G. Riley schrieb:
On 2006-03-12, pete <pf*****@mindsp ring.com> wrote:

> I'm coming around to thinking that 6.5 [#2] is relevant, and that
> p = p->next = q
>is undefined, and not just unspecified.
but
p=(p->next=q);
is fine? I hope .


No. Whether you write
a = b = c;
or
a = (b = c);
does not change anything -- apart from clarifying your intent.
You still are modifying p twice between sequence points


Well, no he's not. But he's reading its prior value for purposes other
than to determine the value stored, so same deal.


Look again. Reading p is necessary to evaluate p->q = next,
and evaluating p->q = next is necessary to get the value
that's assigned to p.

Mar 14 '06 #33
en******@yahoo. com wrote:
The order in which
_side effects_ take place is unspecified, but _some_
evaluation must precede _any_ side effect, because it is
evaluations that produce side effects.
It's possible for all evaluations to be completed
before any side effects take place.
The "at any time"
in the comment above means relative to other side effects.


Hence we get OP's situation:
"A coworker and I have been debating the 'correct' expectation
of evaluation for the phrase a = b = c.
Two different versions of GCC ended up compiling this as
b = c; a = b
and the other ended up compiling it as
a = c; b = c. "

--
pete
Mar 14 '06 #34
en******@yahoo. com wrote:

Micah Cowan wrote:
Michael Mair <Mi**********@i nvalid.invalid> writes:
Richard G. Riley schrieb:
> On 2006-03-12, pete <pf*****@mindsp ring.com> wrote:
>
>> I'm coming around to thinking that 6.5 [#2] is relevant, and that
>> p = p->next = q
>>is undefined, and not just unspecified.
> but
> p=(p->next=q);
> is fine? I hope .

No. Whether you write
a = b = c;
or
a = (b = c);
does not change anything -- apart from clarifying your intent.
You still are modifying p twice between sequence points
Well, no he's not.
But he's reading its prior value for purposes other
than to determine the value stored, so same deal.


Look again. Reading p is necessary to evaluate p->q = next,


No, it isn't.
The value of (p->q = next) is (next)
and evaluating p->q = next is necessary to get the value
that's assigned to p.


--
pete
Mar 14 '06 #35
en******@yahoo. com writes:
Ben Pfaff wrote:
en******@yahoo. com writes:
> Any compiler that compiles p = p->next = q as storing into p
> before fetching p->next is simply broken. Operands are
> evaluated before the operator they're operands for, and
> operators are evaluated before they produce a value--that's
> just how C semantics work (in a C abstract machine of course).
Do you have any citations to back up your assertions?


"Evaluation of an expression may _produce_ side effects."

"[At sequence points,] all side effects of _previous_
evaluations shall be complete [...]."

"In the abstract machine, all expressions are evaluated
_as specified by the semantics_."


OK.
"[An operator] may specify an operation to be performed
(which _in turn_ may _yield a value_ [...], _produce_ a
side effect, or some combination thereof) [...]."
I don't see anything about ordering there.
"The syntax specifies the precedence of operators _in the
evaluation of_ an expression [...]."
Precedence != ordering.
"[Except ...], _the order of evaluation of subexpressions_
and _the order in which side effects take place_ are both
unspecified." (Not "order of A and B" but "order of A" and
"order of B".)
I don't think that typographical distinction is a good one to
hinge an argument on. "the order of" could be repeated just for
clarity of exposition. Even if the typography is significant
here, I don't think it gets you where you want to go.
Keeping these in mind, read through section 6.5, noting
which paragraphs are marked "Semantics" . Expressions
are evaluated "as specified by the semantics".


OK. Let's look at section 6.15.6 "Assignment operators":

3 ... The side effect of updating the stored value of the
left operand shall occur between the previous and the
next sequence point.

4 The order of evaluation of the operands is
unspecified. ...

It seems pretty clear that both the order of evaluation of the
operands and the time at which the result is stored are both
rather unconstrained.
--
"The lusers I know are so clueless, that if they were dipped in clue
musk and dropped in the middle of pack of horny clues, on clue prom
night during clue happy hour, they still couldn't get a clue."
--Michael Girdwood, in the monastery
Mar 14 '06 #36

"Ben Pfaff" <bl*@cs.stanfor d.edu> wrote in message
news:87******** ****@benpfaff.o rg...
en******@yahoo. com writes:
Ben Pfaff wrote:
en******@yahoo. com writes:

> Any compiler that compiles p = p->next = q as storing into p
> before fetching p->next is simply broken. Operands are
> evaluated before the operator they're operands for, and
> operators are evaluated before they produce a value--that's
> just how C semantics work (in a C abstract machine of course).

Do you have any citations to back up your assertions?
"Evaluation of an expression may _produce_ side effects."

"[At sequence points,] all side effects of _previous_
evaluations shall be complete [...]."

"In the abstract machine, all expressions are evaluated
_as specified by the semantics_."


OK.
"[An operator] may specify an operation to be performed
(which _in turn_ may _yield a value_ [...], _produce_ a
side effect, or some combination thereof) [...]."


I don't see anything about ordering there.
"The syntax specifies the precedence of operators _in the
evaluation of_ an expression [...]."


Precedence != ordering.

Precedence puts unavoidable contraints on ordering, because it establishes
which subexpressions are the operands of other expressions.

"[Except ...], _the order of evaluation of subexpressions_
and _the order in which side effects take place_ are both
unspecified." (Not "order of A and B" but "order of A" and
"order of B".)


I don't think that typographical distinction is a good one to
hinge an argument on. "the order of" could be repeated just for
clarity of exposition. Even if the typography is significant
here, I don't think it gets you where you want to go.
Keeping these in mind, read through section 6.5, noting
which paragraphs are marked "Semantics" . Expressions
are evaluated "as specified by the semantics".


OK. Let's look at section 6.15.6 "Assignment operators":

3 ... The side effect of updating the stored value of the
left operand shall occur between the previous and the
next sequence point.

4 The order of evaluation of the operands is
unspecified. ...

In p = p->next = q there are two assignments, so 6.15.6 has to be applied to
each of them separately.

4 The order of evaluation of the operands is
unspecified. ...

Well each assignment has left and right operands, and in each case it makes
no difference whether we evaluate the left before the right or vice versa.
Nobody is claiming otherwise. It's a non-issue.

But nothing here contradicts the fact that one assignment must be evaluated
before the other, for the simple reason that the value of one is an operand
of the other. "Evaluated" here means fully evaluated according to the
semantics -- the abstract machine has no optimiser and no licence to
simplify, so just doing enough to ascertain the value doesn't qualify.
3 ... The side effect of updating the stored value of the
left operand shall occur between the previous and the
next sequence point.

The wording is very specific. "The side effect of updating the stored
value" (of an object referenced by an lvalue) doesn't extend to computing
the lvalue in question. The evaluation of the left operand of an assignment
is not part of the side effect.

The timing of the side effect is only an issue for the outer assignment.
And the quote above only puts outer limits on when the side effect occurs.
It doesn't say that the side effect may occur at any time between the
sequence points -- of course that would be silly, because the new value has
to be computed before it can be stored, so the side effect cannot occur
before the evaluation of the expression which provides the new value.
Another constraint on ordering that is too obvious to be stated, but is not
overridden by any wording.

It seems pretty clear that both the order of evaluation of the
operands and the time at which the result is stored are both
rather unconstrained.

That's an interesting approach: two insignificant details are left
unspecified, so it must be saying "anything goes"

Mar 14 '06 #37
You (Robin Haigh) believe that
p = p->next = q;
is well defined. Many other people share this opinion. But when
it was brought up in comp.std.c a long time ago, there were also
many people with the opposite opinion. If I recall correctly,
some of these folks were actually committee members. I'm not
going to waste a lot more of my time resuming the debate. It's
not productive. Go back and read the old thread.

--
"Am I missing something?"
--Dan Pop
Mar 14 '06 #38

"Ben Pfaff" <bl*@cs.stanfor d.edu> wrote in message
news:87******** ****@benpfaff.o rg...
You (Robin Haigh) believe that
p = p->next = q;
is well defined. Many other people share this opinion. But when
it was brought up in comp.std.c a long time ago, there were also
many people with the opposite opinion. If I recall correctly,
some of these folks were actually committee members. I'm not
going to waste a lot more of my time resuming the debate. It's
not productive. Go back and read the old thread.

Well I wouldn't claim that the standard is crystal clear on the subject.

However, if this expression produces undefined behaviour, it can only be
because of 6.5/2. Trouble is, an enthusiastic reading of 6.5/2 can make all
sorts of things UB, including *p++. A line has to be drawn, and it might
seem important to know where it is.

One way to draw a line is to say that the old value (of the modified object)
can be used within the subexpression that computes the new value. This is
rational, workable, creates no difficulties or ambiguities, conflicts with
nothing in the standard, and finds support in the Rationale.

Many people have claimed that the line is somewhere else, but nobody seems
to be offering a workable detailed exposition of where they think it is.
The other approach (with p = p->next = q) is to argue that maybe 6.5/2
doesn't apply, but the outcome is still unspecified as a result of the
"reordering " licence given to the abstract machine.

Well it seems to me that 6.5/2 is intended to cover, and does cover, _at
least_ all the cases where the reordering licence creates an ambiguity, as
regards the value of an expression or as regards the final stored value of
any object (at the next sequence point). This doesn't leave any room for
outcomes to be unspecified as to these aspects. So the effect of the
reordering licence on any expression that isn't UB is invisible, unless
there are external consequences of side effects, such as file output or
asynchronous access to objects.

--
RSH

Mar 15 '06 #39
Robin Haigh wrote:
Trouble is, an enthusiastic reading of 6.5/2 can make all
sorts of things UB, including *p++.


An enthusiastic reading of 6.5/2 should find the footnote:

70)
This paragraph renders undefined statement expressions such as
i = ++i + 1;
a[i++] = i;
while allowing
i = i + 1;
a[i] = i;

--
pete
Mar 15 '06 #40

This thread has been closed and replies have been disabled. Please start a new discussion.

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.