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 4770
On 2006-03-19, Vladimir S. Oka <no****@btopenw orld.com> wrote: la************@ ugs.com opined:
Vladimir S. Oka <no****@btopenw orld.com> wrote: la************@ ugs.com opined:
It's a bit more complicated than that. First, the top-level assignment expression (p = p->next = q) is evaluated. That requires evaluating the left side (p) to determine the location to store into and evaluating the right side (p->next = q) to determine the value to store. Those evaluations can occur in either order.
I admit that the normative text of the standard does allow (but not mandate) such behaviour. However, as I have pointed earlier, the Example 2 (non-normative, as examples are) suggests that the order of evaluation is right-to-left.
No, it most certainly does not. Example 2 is primarily concerned with the result type of assignment expressions, not order of evaluation. About the only thing that it implies about order of evaluation is that an operator's operands must be evaluated before the final result of the operation is known, but it says nothing whatsoever about the order in which the operands are evaluated.
So, according to what you just said, in order to execute:
p = p->next = q;
the right assignment has to be done first. Otherwise, the final result is not known and the left assignment cannot happen. Hence, the right one must be executed first, QED. Executing the right assignment does not modify `p`, so there's no multiple modifications between sequence points, and no UB (`p` is modified once, and `p->next` is modified once -- that's it).
That sounds right.
[No-one has claimed that "p = p->next" alone is UB, and I don't see what
this adds that would be]
Vladimir S. Oka wrote: en******@yahoo. com opined:
Vladimir S. Oka wrote: en******@yahoo. com opined:
> > Vladimir S. Oka wrote: >> On Friday 17 March 2006 15:27, en******@yahoo. com opined (in >> <11************ **********@j52g 2000cwj.googleg roups.com>): >> >> > >> > Vladimir S. Oka wrote: >> >> On Friday 17 March 2006 04:48, en******@yahoo. com opined >> >> (in >> >> <11************ *********@i39g2 000cwa.googlegr oups.com>): >> >> >> >> > >> >> > pete wrote: >> >> >> 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) >> >> > >> >> > The value of p->q = next is next. But in order to get >> >> > the value, the assignment p->q = next must _have been >> >> > evaluated_, which needed both operands. Evaluation >> >> > includes all the actions in the Semantics paragraph, >> >> > which includes starting the side effect of storing the >> >> > value. >> >> >> >> I think pete is right. The compiler _knows_ that the >> >> result of (p->q = next) _will_be_ next before it even >> >> produces any code, and can use that knowledge once it does >> >> get to producing some. >> > >> > You've fallen into the trap of arguing based on what a >> > compiler might be capable of. Regardless of what the >> > compiler knows, it's still obliged to produce code that >> > behaves according to how the Semantics paragraphs and >> > everything else in the standard says it must. >> >> But it still does not evaluate p->q in order to assign next >> to it. What is evaluated is next, and its value is /assigned/ >> to p->q. Next, the assignment is evaluated and assigned to p. >> The assignment evaluates to next, but I don't think that >> compiler is obliged to do that in any particular way. > > Sorry, the compiler _is_ obliged to do the evaluation as per > the Semantics description. The Semantics paragraphs in 6.5.16 > specify _both_ that the store happens and what the value is. > (Yes, the side effect of the store may happen later.) Where > in the standard is there any statement that says an expression > (_any_ expression) may yield a value _before_ it has been > evaluated?
Ok, now my head starts to spin a bit, but bear with me...
Is it right that the original problem statement was:
p = p->next = q;
Yes.
Assuming it was: `p` is not modified twice, it is read once to get `next`, and modified once to assign value to it in the leftmost assignment. Assigning value to `next` does not modify `p`, right?
Now, I re-read what 6.5.16 has to say, and yes, I know examples are not normative, but to me Example 2 (6.5.16.1p5) seems to support the following interpretation of the above statement:
1) `q` is evaluated 2) its value is assigned to `p->next` 3) that value becomes the value of the rightmost assignment 4) that value is assigned to `p`
I read that it happens in the order I give above. Also, `p` is modified only once (4), and read only once (2).
Yes. Also the side effect of updating the value of p->next can happen any time after (2), and the side effect of updating the value of p can happen any time after (4).
I agree.
However, what is the side effect of updating `p->next`? It's just plain storing a value into a variable. Again, `p` is not modified, AFAICT. It's the struct member `next` that is, and `p` is just read to get its address (`next`'s address).
Whether there's any side effect of updating `p` after (4) is, IMO, irrelevant to the discussion (again, I can't see any side effect of storing a value into variable).
I still think the operation in this case has to be equivalent to:
p->next = q; p = p->next;
Right. Except the last part is better written as
t = q;
p->next = t;
p = t; la************@ ugs.com wrote: Vladimir S. Oka <no****@btopenw orld.com> wrote: Now, I re-read what 6.5.16 has to say, and yes, I know examples are not normative, but to me Example 2 (6.5.16.1p5) seems to support the following interpretation of the above statement:
1) `q` is evaluated 2) its value is assigned to `p->next` 3) that value becomes the value of the rightmost assignment 4) that value is assigned to `p`
It's a bit more complicated than that. First, the top-level assignment expression (p = p->next = q) is evaluated. That requires evaluating the left side (p) to determine the location to store into and evaluating the right side (p->next = q) to determine the value to store. Those evaluations can occur in either order. ...
Why do you say the top level expression is evaluated
first? It makes more sense to say that subexpressions
(including simple operands) are evaluated first, and a
larger containing expression is evaluated only after
its operand subexpressions have been evaluated. la************@ ugs.com wrote: Vladimir S. Oka <no****@btopenw orld.com> wrote: la************@ ugs.com opined: It's a bit more complicated than that. First, the top-level assignment expression (p = p->next = q) is evaluated. That requires evaluating the left side (p) to determine the location to store into and evaluating the right side (p->next = q) to determine the value to store. Those evaluations can occur in either order.
I admit that the normative text of the standard does allow (but not mandate) such behaviour. However, as I have pointed earlier, the Example 2 (non-normative, as examples are) suggests that the order of evaluation is right-to-left.
No, it most certainly does not. Example 2 is primarily concerned with the result type of assignment expressions, not order of evaluation. About the only thing that it implies about order of evaluation is that an operator's operands must be evaluated before the final result of the operation is known, but it says nothing whatsoever about the order in which the operands are evaluated. Compiler can, and do, evaluate operands in various orders. In fact, a common strategy is to evaluate the most complex operand first.
When asked about evaluation order by a beginner,
try saying this:
Precedence _does_ determine order of evaluation
for operATORS, but _doesn't_ determine order of
evaluation for operANDS.
Of course, it isn't completely accurate (because
of &&, etc), but usually after hearing that one
sentence the lights will go on.
Jordan Abel wrote: On 2006-03-19, Vladimir S. Oka <no****@btopenw orld.com> wrote: la************@ ugs.com opined:
Vladimir S. Oka <no****@btopenw orld.com> wrote: la************@ ugs.com opined: > It's a bit more complicated than that. First, the top-level > assignment expression (p = p->next = q) is evaluated. That > requires evaluating the left side (p) to determine the location to > store into and evaluating the right side (p->next = q) to determine > the value to > store. Those evaluations can occur in either order. I admit that the normative text of the standard does allow (but not mandate) such behaviour. However, as I have pointed earlier, the Example 2 (non-normative, as examples are) suggests that the order of evaluation is right-to-left. No, it most certainly does not. Example 2 is primarily concerned with the result type of assignment expressions, not order of evaluation. About the only thing that it implies about order of evaluation is that an operator's operands must be evaluated before the final result of the operation is known, but it says nothing whatsoever about the order in which the operands are evaluated. So, according to what you just said, in order to execute:
p = p->next = q;
the right assignment has to be done first. Otherwise, the final result is not known and the left assignment cannot happen. Hence, the right one must be executed first, QED. Executing the right assignment does not modify `p`, so there's no multiple modifications between sequence points, and no UB (`p` is modified once, and `p->next` is modified once -- that's it).
That sounds right.
[No-one has claimed that "p = p->next" alone is UB, and I don't see what this adds that would be]
The problem with "p = p->next = q" is that wither the new or the old
value of p could be used to decide where to write q to. Regardless of
whether that is a real problem, there is also section 6.5 para 2 which says:
| Between the previous and next sequence point an object shall have its
| stored value modified at most once by the evaluation of an expression.
| Furthermore, the prior value shall be read only to determine the value
| to be stored.
Note the second sentence. In "p = p->next = q" p is read to determine
where to store q, and this violates the shall in the second sentence.
Unless, of course, evaluating p->next is part of determining the value
to be assigned to p...
Personally I don't like the line even if it is defined behaviour.
--
Flash Gordon, living in interesting times.
Web site - http://home.flash-gordon.me.uk/
comp.lang.c posting guidelines and intro: http://clc-wiki.net/wiki/Intro_to_clc
Flash Gordon opined: Jordan Abel wrote: On 2006-03-19, Vladimir S. Oka <no****@btopenw orld.com> wrote: la************@ ugs.com opined:
Vladimir S. Oka <no****@btopenw orld.com> wrote: > la************@ ugs.com opined: >> It's a bit more complicated than that. First, the top-level >> assignment expression (p = p->next = q) is evaluated. That >> requires evaluating the left side (p) to determine the location >> to store into and evaluating the right side (p->next = q) to >> determine the value to >> store. Those evaluations can occur in either order. > I admit that the normative text of the standard does allow (but > not mandate) such behaviour. However, as I have pointed earlier, > the Example 2 (non-normative, as examples are) suggests that the > order of evaluation is right-to-left. No, it most certainly does not. Example 2 is primarily concerned with the result type of assignment expressions, not order of evaluation. About the only thing that it implies about order of evaluation is that an operator's operands must be evaluated before the final result of the operation is known, but it says nothing whatsoever about the order in which the operands are evaluated. So, according to what you just said, in order to execute:
p = p->next = q;
the right assignment has to be done first. Otherwise, the final result is not known and the left assignment cannot happen. Hence, the right one must be executed first, QED. Executing the right assignment does not modify `p`, so there's no multiple modifications between sequence points, and no UB (`p` is modified once, and `p->next` is modified once -- that's it). That sounds right.
[No-one has claimed that "p = p->next" alone is UB, and I don't see [what this adds that would be]
The problem with "p = p->next = q" is that wither the new or the old value of p could be used to decide where to write q to. Regardless of whether that is a real problem, there is also section 6.5 para 2 which says: | Between the previous and next sequence point an object shall have | its stored value modified at most once by the evaluation of an | expression. Furthermore, the prior value shall be read only to | determine the value to be stored.
Note the second sentence. In "p = p->next = q" p is read to determine where to store q, and this violates the shall in the second sentence. Unless, of course, evaluating p->next is part of determining the value to be assigned to p...
Which was my point all along, and why I think it's OK.
Personally I don't like the line even if it is defined behaviour.
I do heartily agree with this. It's just that I think it is.
--
BR, Vladimir
Fourth Law of Revision:
It is usually impractical to worry beforehand about
interferences -- if you have none, someone will make one for you.
Vladimir S. Oka <no****@btopenw orld.com> wrote: Executing the right assignment does not modify `p`, so there's no multiple modifications between sequence points, and no UB (`p` is modified once, and `p->next` is modified once -- that's it).
That's not correct. Since p is modified, it can be read "only to
determine the value to be stored". If it is read for any other reason,
the behavior is undefined. So, the crux of the matter is whether the
read of p in the embedded assignment (p->next = q) is "only to determine
the value to be stored" in p by the outer assignment or not. Many
people have argued that it is not (it's being read to determine where to
store q) and that's where the undefined behavior comes from.
For what it's worth, I believe the standard can be read either way. I
also believe that all of the formal sequence point models that have been
proposed make it well defined. I also believe there is at least one
implementation that gets it wrong.
-Larry Jones
I don't see why some people even HAVE cars. -- Calvin en******@yahoo. com wrote: Why do you say the top level expression is evaluated first?
Because it is.
It makes more sense to say that subexpressions (including simple operands) are evaluated first, and a larger containing expression is evaluated only after its operand subexpressions have been evaluated.
It's the evaluation of the top-level expression that causes the operand
subexpressions to be evaluated. This is particularly important for
cases where certain operands are *not* evaluated, such as in &&, ||, and
?: expressions.
-Larry Jones
I sure wish I could get my hands on some REAL dynamite. -- Calvin la************@ ugs.com opined: Vladimir S. Oka <no****@btopenw orld.com> wrote: Executing the right assignment does not modify `p`, so there's no multiple modifications between sequence points, and no UB (`p` is modified once, and `p->next` is modified once -- that's it). That's not correct. Since p is modified, it can be read "only to determine the value to be stored". If it is read for any other reason, the behavior is undefined. So, the crux of the matter is whether the read of p in the embedded assignment (p->next = q) is "only to determine the value to be stored" in p by the outer assignment or not. Many people have argued that it is not (it's being read to determine where to store q) and that's where the undefined behavior comes from.
That sounds reasonable enough. I'd still argue that `p` is read only to
determine the value to be stored. For me, the fact that reading it is
first used to figure out where to store `q` is just part of
determining what to finally store in `p`.
Count me in the (not so many?) other people, and let's agree to
disagree. Especially since you won't see me using such a construct
anyway.
:-)
For what it's worth, I believe the standard can be read either way. I also believe that all of the formal sequence point models that have been proposed make it well defined. I also believe there is at least one implementation that gets it wrong.
I actually haven't tried this on any implementations . It's entirely
possible that some behave differently to the others, given differences
in opinions on this one.
--
BR, Vladimir
If you are over 80 years old and accompanied
by your parents, we will cash your check.
"Vladimir S. Oka" wrote: la************@ ugs.com opined:
.... snip ... For what it's worth, I believe the standard can be read either way. I also believe that all of the formal sequence point models that have been proposed make it well defined. I also believe there is at least one implementation that gets it wrong.
I actually haven't tried this on any implementations . It's entirely possible that some behave differently to the others, given differences in opinions on this one.
I think you are talking about "p = p->next = q;" constructs. The
"p = p->next" part is a very standard way of advancing down a
linked list, and the whole statement is a very normal way of
extending the list and advancing.
--
"If you want to post a followup via groups.google.c om, don't use
the broken "Reply" link at the bottom of the article. Click on
"show options" at the top of the article, then click on the
"Reply" at the bottom of the article headers." - Keith Thompson
More details at: <http://cfaj.freeshell. org/google/>
Also see <http://www.safalra.com/special/googlegroupsrep ly/> This thread has been closed and replies have been disabled. Please start a new discussion. |