468,294 Members | 1,907 Online

What is a sequence point?

I know the basic definition of a sequence point (point where all side
statement:

"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 accessed only to determine the
value to be stored."

Can someone give me examples of expressions that "barely" break this
rule (the type a newcomer might trip over)?

If you are between sequence points, then what does that second sentence
mean "prior value"?

Nov 14 '05 #1
53 3303
de*********@gmail.com says...
I know the basic definition of a sequence point (point where all side
statement:

"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 accessed only to determine the
value to be stored."

Can someone give me examples of expressions that "barely" break this
rule (the type a newcomer might trip over)?

If you are between sequence points, then what does that second sentence
mean "prior value"?

Specifically,
http://www.eskimo.com/~scs/C-faq/q3.8.html

Also related:
http://www.eskimo.com/~scs/C-faq/q3.1.html
http://www.eskimo.com/~scs/C-faq/q3.2.html
http://www.eskimo.com/~scs/C-faq/q3.3.html
http://www.eskimo.com/~scs/C-faq/q3.9.html

Then read all of it again. :-)

--
"Making it hard to do stupid things often makes it hard
to do smart ones too." -- Andrew Koenig
Nov 14 '05 #2

Randy Howard wrote:

If you are between sequence points, then what does that second sentence mean "prior value"?

Specifically,
http://www.eskimo.com/~scs/C-faq/q3.8.html

Also related:
http://www.eskimo.com/~scs/C-faq/q3.1.html
http://www.eskimo.com/~scs/C-faq/q3.2.html
http://www.eskimo.com/~scs/C-faq/q3.3.html
http://www.eskimo.com/~scs/C-faq/q3.9.html

Then read all of it again. :-)

I read the FAQ before my original post. It was the FAQ that actually
tickled my mind and made me wonder if I was just lucky all this time
when I was using side effects in expressions.

This sentence from the FAQ really confused me:

"The second sentence can be difficult to understand. It says that if an
object is written to within a full expression, any and all accesses to
it within the same expression must be for the purposes of computing the
value to be written. This rule effectively constrains legal expressions
to those in which the accesses demonstrably precede the modification."

More confused about the FAQ's explanation of the sentence from the
standard than the standard itself. I'm all around confused about side
effect and sequence points :_(

Nov 14 '05 #3
de*********@gmail.com says...

Randy Howard wrote:

If you are between sequence points, then what does that second sentence mean "prior value"?

Specifically,
http://www.eskimo.com/~scs/C-faq/q3.8.html

Also related:
http://www.eskimo.com/~scs/C-faq/q3.1.html
http://www.eskimo.com/~scs/C-faq/q3.2.html
http://www.eskimo.com/~scs/C-faq/q3.3.html
http://www.eskimo.com/~scs/C-faq/q3.9.html

Then read all of it again. :-)

I read the FAQ before my original post. It was the FAQ that actually
tickled my mind and made me wonder if I was just lucky all this time
when I was using side effects in expressions.

Ok, well, the links after the first one contain specific examples. I
was hoping that one or more of them would turn the lightbulb on for
you because I usually find that examples can be helpful when the legalese
gets overwhelming.

In particular, 3.2 has a more lengthy explanation, and a common example
as well. It seems to explain it pretty well to me, so if that doesn't do
it for you, maybe somebody else can provide a better explanation.

--
"Making it hard to do stupid things often makes it hard
to do smart ones too." -- Andrew Koenig
Nov 14 '05 #4
Deniz Bahar wrote:

I know the basic definition of a sequence point (point where all side
statement:

"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 accessed only to determine the
value to be stored."

Can someone give me examples of expressions that "barely" break this
rule (the type a newcomer might trip over)?

If you are between sequence points,
then what does that second sentence
mean "prior value"?

(p = p -> next = q)

The above doesn't specify the order of assignment and is undefined.
It could be either (p = q, p -> next = q) or (p -> next = q, p = q).

(i = x[i]++)
The above is being debated on comp.std.c
The i in x[i]++ is evaluated only once, but for two reasons.
It's evaluated to determine the value of the right operand of
the assignment operator, but also to determine the address of
the lvalue of the increment operator.

--
pete
Nov 14 '05 #5
"Deniz Bahar" <de*********@gmail.com> wrote:
I know the basic definition of a sequence point (point where all side
statement:

"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 accessed only to determine the
value to be stored."

Can someone give me examples of expressions that "barely" break this
rule (the type a newcomer might trip over)?

If you are between sequence points, then what does that second sentence
mean "prior value"?

A sequence point is a point in the execution of a program at which all
side effects (that is, all assignments, file output and volatile
accesses) must have been completed. Examples are at the end of a
complete expression, just before a function call, and at the logical,
comma and conditional operators.
The compiler is allowed to "save up" or mix side effects between two
adjacent sequence points, for example if that results in more efficient
code. However, _at_ a sequence point, all side effects must be complete.
This is useful; it allows both

int a,b;
char x[10],y[10];

...
x[a++]=y[b++];

to be compiled efficiently (possibly with both increments being done in
parallel), _and_

if (valid_index(a) && array[a]>cutoff)

to be compiled safely, that is, without the compiler trying to be
efficient by computing array[a] before we're sure a is valid.

It also means (and here we come to your example) that

a[i]=i++;

is invalid code, because i++ is both incremented, _and_ read for another
purpose than the increment. In the sub-expression i++, i is given a new
value; but in the sub-expression a[i], the _prior value_ of i, that is,
its value before the increment, is used to determine which array member
to assign to.

Richard
Nov 14 '05 #6

Deniz Bahar wrote:
I know the basic definition of a sequence point (point where all side
statement:

"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 accessed only to determine the
value to be stored."

I don't know where you got that passage from, but this one from the
last C89 draft is a lot more clear:

"[If] an object is modified more than once, or is modified and accessed
other than to determine the new value, between two sequence points [,
undefined behaviour reusults]"

There are two parts to this:

1) An object can't be modified more than once between sequence points:
examples

j = i-- * i--; /* more obvious fault */

j = ++i * ++i; /* less obvious, but still breaches rule */

You might think that the pre-increments are okay because side effects
happen immediately, but it doesn't matter because the literature says
you can't modify an object more than once.
2) Between sequence points, an object can't be modified and accessed
other than to determine it's new value (trickier of the two parts of
the rule).

examples

i = i + 3; this is okay because i is modified (i = ____) and accessed
(rvalue the the left of =) only to determine the new value for the i

(i += 3) * j; this is not okay, because i is modified (i += _-__) and
accessed (rvalue of i later used to multiply with j) for a reason that
isn't to determine the new value of i.

i = (i += 3) * j; this is okay, because i is modified (i += ____) and
is accessed to determine the new value of i (left side i = _____).

a[i++]; this is not okay, because i is modified [i++] then accessed to
determine something other than the new value of i.

I hope this helps.

Nov 14 '05 #7

pete wrote:
Deniz Bahar wrote:

I know the basic definition of a sequence point (point where all side effects guaranteed to be finished), but I am confused about this
statement:

"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 accessed only to determine the value to be stored."

Can someone give me examples of expressions that "barely" break this rule (the type a newcomer might trip over)?

If you are between sequence points,
then what does that second sentence
mean "prior value"?
(p = p -> next = q)

The above doesn't specify the order of assignment and is undefined.
It could be either (p = q, p -> next = q) or (p -> next = q, p = q).

but =assignment operators associate left to right, so it would be:

(p -> next = q, p = q)

(i = x[i]++)
The above is being debated on comp.std.c
The i in x[i]++ is evaluated only once, but for two reasons.
It's evaluated to determine the value of the right operand of
the assignment operator, but also to determine the address of
the lvalue of the increment operator.

this would seem to break the rule, because the postincrement is not an

Nov 14 '05 #8
Luke Wu wrote:

pete wrote:
(p = p -> next = q)

The above doesn't specify the order of assignment and is undefined.
It could be either (p = q, p -> next = q) or (p -> next = q, p = q).

but =assignment operators associate left to right, so it would be:

(p -> next = q, p = q)

Wrong. Because there's no sequence point, the compiler is free to do
the evaluations in any order it likes. Associativity does not
determine the order of evaluation.
Nov 14 '05 #9
On Fri, 11 Feb 2005 08:08:44 -0800, Luke Wu wrote:

Deniz Bahar wrote:
I know the basic definition of a sequence point (point where all side
statement:

"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 accessed only to determine the
value to be stored."

I don't know where you got that passage from, but this one from the
last C89 draft is a lot more clear:

The text is that in the current standard.
"[If] an object is modified more than once, or is modified and accessed
other than to determine the new value, between two sequence points [,
undefined behaviour reusults]"

There are two parts to this:

1) An object can't be modified more than once between sequence points:
examples

j = i-- * i--; /* more obvious fault */

j = ++i * ++i; /* less obvious, but still breaches rule */

You might think that the pre-increments are okay because side effects
happen immediately, but it doesn't matter because the literature says
you can't modify an object more than once.
2) Between sequence points, an object can't be modified and accessed
other than to determine it's new value (trickier of the two parts of
the rule).

examples

i = i + 3; this is okay because i is modified (i = ____) and accessed
(rvalue the the left of =) only to determine the new value for the i

(i += 3) * j; this is not okay, because i is modified (i += _-__) and
accessed (rvalue of i later used to multiply with j) for a reason that
isn't to determine the new value of i.
This is fine. By your logic you couldn't use the result of an assignment
operator for anything. Perhaps this is one reason why the draft text
you cite was corrected?
i = (i += 3) * j; this is okay, because i is modified (i += ____) and
is accessed to determine the new value of i (left side i = _____).
i is modified twice between sequence points so this is undefined.
a[i++]; this is not okay, because i is modified [i++] then accessed to
determine something other than the new value of i.

Again, this is perfectly fine.

Note that neither assignment nor increment/decrement operators reread the
value of the object after it has been modified.

Lawrence

Nov 14 '05 #10

infobahn wrote:
Luke Wu wrote:

pete wrote:
(p = p -> next = q)

The above doesn't specify the order of assignment and is undefined. It could be either (p = q, p -> next = q) or (p -> next = q, p =
q).
but =assignment operators associate left to right, so it would be:
meant to say right to left

(p -> next = q, p = q)

Wrong. Because there's no sequence point, the compiler is free to do
the evaluations in any order it likes. Associativity does not
determine the order of evaluation.

Yes, but = is a binary operator, and works on 2 operands.

So you can either have p = p->next, p->next =q OR p->next = q, p =
p->next

How can you have p = q ?? never is a single = operating on those two
operands

Nov 14 '05 #11

Deniz Bahar wrote:
I know the basic definition of a sequence point (point where all side
statement:

"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 accessed only to determine the
value to be stored."

Can someone give me examples of expressions that "barely" break this
rule (the type a newcomer might trip over)?

If you are between sequence points, then what does that second sentence mean "prior value"?

Dont forget about function calls (the commas in them are not sequence
points).
A call such as this:

f(n++, n);

has undefined behavior because it violates the rule you quoted cited.
There's no sequence point between the argument expressions. The first
(left) argument modifies n. It also reads the value of n, but only to
determine the new value to be stored in n. So far, so good. However,
the second (right) argument expression reads the value of n between the
same pair of sequence points as the first argument, but not to
determine the value to be stored in n. This additional attempt to read
the value of n has undefined behavior.

Nov 14 '05 #12
Luke Wu wrote:

infobahn wrote:

pete wrote:
> (p = p -> next = q)
Because there's no sequence point, the compiler is free to do
the evaluations in any order it likes. Associativity does not
determine the order of evaluation.

Yes, but = is a binary operator, and works on 2 operands.

So what? The compiler can view it this way:

"We have an expression of the form a = b = c; fine, I'll make them
all equal, so we'll set a equal to c and we'll set b equal to c",
so it could set p equal to q and then set p->next equal to q, with
the result that q->next = q;

or this way: it could set p->next to be equal to q, and then set p
to be equal to p->next, which would not affect q->next at all.
Either behaviour (and any other behaviour!) is legitimate.
Nov 14 '05 #13

infobahn wrote:
Luke Wu wrote:

infobahn wrote:
>
> pete wrote:
> > (p = p -> next = q) Because there's no sequence point, the compiler is free to do
the evaluations in any order it likes. Associativity does not
determine the order of evaluation.

Yes, but = is a binary operator, and works on 2 operands.

So what? The compiler can view it this way:

"We have an expression of the form a = b = c; fine, I'll make them
all equal, so we'll set a equal to c and we'll set b equal to c",
so it could set p equal to q and then set p->next equal to q, with
the result that q->next = q;

No, the compiler views it according to the associativity rules(right to
left) as this:

(a = (b = c)) <=== right to left associativity

Consider also (all ints):

a + b + c

the value in a is equal to -b, the value in both b and c is almost
INT_MAX, so will over flow occur?

Based on your argument, overflow 'ca'n occur, because the compiler is
free to do (b + c) before adding the result to a.

In reality, overflow never occurs because we know the compiler will
consider that expression as:

((a + b) + c) <=== left to right associativity

the result will be rvalue(c), never an overflow in an intermediate step

Nov 14 '05 #14

G Fernandes wrote:
Deniz Bahar wrote:
I know the basic definition of a sequence point (point where all side effects guaranteed to be finished), but I am confused about this
statement:

"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 accessed only to determine the value to be stored."

Can someone give me examples of expressions that "barely" break this rule (the type a newcomer might trip over)?

If you are between sequence points, then what does that second

sentence
mean "prior value"?

Dont forget about function calls (the commas in them are not sequence
points).
A call such as this:

f(n++, n);

has undefined behavior because it violates the rule you quoted cited.
There's no sequence point between the argument expressions. The first
(left) argument modifies n. It also reads the value of n, but only to
determine the new value to be stored in n. So far, so good. However,
the second (right) argument expression reads the value of n between

the same pair of sequence points as the first argument, but not to
determine the value to be stored in n. This additional attempt to read the value of n has undefined behavior.

"It also reads the value of n, but only to determine the new value to
be stored in n." No it doesn't, the first argument reads the value of n
to be sent to the function, not to be stored in n.

Nov 14 '05 #15
On 11 Feb 2005 12:55:34 -0800, in comp.lang.c , "Luke Wu"
<Lo***********@gmail.com> wrote:

infobahn wrote:

So what? The compiler can view it this way:

"We have an expression of the form a = b = c; fine, I'll make them
all equal, so we'll set a equal to c and we'll set b equal to c",
so it could set p equal to q and then set p->next equal to q, with
the result that q->next = q;

No, the compiler views it according to the associativity rules(right to
left) as this:

(a = (b = c)) <=== right to left associativity

The standard discusses this type of problem. In 5.1.2.3, it shows examples
and notes that in general you cannot assume the normal mathematical laws of
associativity to be applicable.
--
Mark McIntyre
CLC FAQ <http://www.eskimo.com/~scs/C-faq/top.html>
Nov 14 '05 #16
On 11 Feb 2005 13:00:23 -0800, in comp.lang.c , "Luke Wu"
<Lo***********@gmail.com> wrote:

f(n++, n);

has undefined behavior because it violates the rule you quoted cited.
There's no sequence point between the argument expressions. The first
(left) argument modifies n. It also reads the value of n, but only to
determine the new value to be stored in n. So far, so good. However,
the second (right) argument expression reads the value of n between the
same pair of sequence points as the first argument, but not to
determine the value to be stored in n. This additional attempt to read
the value of n has undefined behavior.

"It also reads the value of n, but only to determine the new value to
be stored in n." No it doesn't, the first argument reads the value of n
to be sent to the function, not to be stored in n.

It does both.

For the first argument n is evaluated, and stored for passing to f. n is
also incremented, and stored back into n.

The second argument evaluates n and stores it for passing to f.

The order in which the two arguments are evaluated is undefined. The timing
of the increment-and-store-back is undefined. Its possible for the
increment to happen before or after the 2nd argument is evaluated. Or
simultaneously.

--
Mark McIntyre
CLC FAQ <http://www.eskimo.com/~scs/C-faq/top.html>
Nov 14 '05 #17
Luke Wu <Lo***********@gmail.com> wrote:
No, the compiler views it according to the associativity rules(right to
left) as this:

(a = (b = c)) <=== right to left associativity
Yes, that's how it's parsed.
No, that doesn't impose requirements on the order things are actually
done. All that's required is that these happen:
- c is evaluated to get a value
- The value of c, converted appropriately, is stored in b
- The value stored in b, converted appropriately, is stored in a.
Which order they happen in depends only on dependencies between them,
and the store to a only depends on the value stored in b being calculated
(and even there the compiler may be able to determine what it is without
actually calculating it - see my example below), not on it actually
being stored in b.

If they all have the same types, any self-respecting optimizing compiler
will load the value once and store it twice without doing anything else
between the stores. In the simple case it will assign to b first, but
there's no reason why it needs to, and (especially if they're not both
local variables) there might be good reasons not to.
If different types (and, therefore, conversions) are involved, it's even
easier to come up with a perfectly reasonable way for the store to a to
happen first, or for both stores to happen at the same time.
Consider, f'rexample, a word-addressed machine where char * and void *
(as "byte pointers") have a different representation than other pointers.
(Such machines do exist, and as far as I know have C implementations;
Chris Torek likes using one of them as an example.)
If a and c are, say, int * and b is a void *, then the most sensible
way to execute "a=b=c" is:
--------
store r1,a1 ;Store word pointer
shl r1,2 ;Convert word pointer to byte pointer
store r1,a1 ;Store byte pointer
--------
Since the word pointer -> byte pointer -> word pointer conversion ends
up being a no-op, this reordering gives the exact same results as if
the double conversion had been done, and is therefore just as valid.

For the case where they're all the same type, consider
Consider also (all ints):

a + b + c

the value in a is equal to -b, the value in both b and c is almost
INT_MAX, so will over flow occur?

If the value of a+b can be added to c without overflowing, the generated
code has to act as if no overflow occurred, since the value being added
to c is the value of a+b.
If the compiler knows that, f'rexample, the machine uses 2s-complement
with silent wraparound on overflow, it's free to generate code that
computes b+c and then adds a to that, since it knows that the result
will be the same whether something overflows or not.

The only requirement is that the correct values get stored in the
correct places before the next sequence point. Operations without
dependencies, or operations that can commute with each other, can be
(and, with optimizing compilers, usually are) done in any order the
compiler likes, no matter what the code says.
dave

--
Dave Vandervies dj******@csclub.uwaterloo.ca
I can't pick up the phone and ask for "Farsley double one double three"
whenever I fancy a pizza. Well, I could, but I'd stay hungry.
--Peter Corlett in the scary devil monastery
Nov 14 '05 #18

Dave Vandervies wrote:
Luke Wu <Lo***********@gmail.com> wrote:
No, the compiler views it according to the associativity rules(right toleft) as this:

(a = (b = c)) <=== right to left associativity
Yes, that's how it's parsed.
No, that doesn't impose requirements on the order things are actually
done. All that's required is that these happen:
- c is evaluated to get a value
- The value of c, converted appropriately, is stored in b
- The value stored in b, converted appropriately, is stored in a.
Which order they happen in depends only on dependencies between them,
and the store to a only depends on the value stored in b being

calculated (and even there the compiler may be able to determine what it is without actually calculating it - see my example below), not on it actually
being stored in b.

The compiler can do anything it wants at the machine level, but the
result must not differ from what we would get if we did b=c , a=b at
the abstract machine level

if in (a = (b = c)) , the order really does matter (if a, b, c are sub
expressions), then the order implied by the brackets must be taken, if
order doesn't matter, the compiler can do anything

the result of anything the compiler does, must equal the result of:
b=c,a=b

I am absolutely certain of this.

Nov 14 '05 #19
On 11 Feb 2005 16:12:57 -0800, "Luke Wu" <Lo***********@gmail.com>
wrote in comp.lang.c:

Dave Vandervies wrote:
Luke Wu <Lo***********@gmail.com> wrote:
No, the compiler views it according to the associativity rules(right toleft) as this:

(a = (b = c)) <=== right to left associativity

Yes, that's how it's parsed.
No, that doesn't impose requirements on the order things are actually
done. All that's required is that these happen:
- c is evaluated to get a value
- The value of c, converted appropriately, is stored in b
- The value stored in b, converted appropriately, is stored in a.
Which order they happen in depends only on dependencies between them,
and the store to a only depends on the value stored in b being

calculated
(and even there the compiler may be able to determine what it is

without
actually calculating it - see my example below), not on it actually
being stored in b.

The compiler can do anything it wants at the machine level, but the
result must not differ from what we would get if we did b=c , a=b at
the abstract machine level

if in (a = (b = c)) , the order really does matter (if a, b, c are sub
expressions), then the order implied by the brackets must be taken, if
order doesn't matter, the compiler can do anything

the result of anything the compiler does, must equal the result of:
b=c,a=b

I am absolutely certain of this.

And I am absolutely certain that you are wrong. The difference is
that I can cite the reference to the C language standard to back up my
certainty, and you cannot.

The C standard specifically states, in paragraph 2 of section 6.5:

========
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.70)
========

That "70" at the end is a reference to a footnote, subscripts don't
look special in plain text. But here is the context of footnote 70:

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

So you may be certain, but you are certainly wrong.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.contrib.andrew.cmu.edu/~a...FAQ-acllc.html
Nov 14 '05 #20

Jack Klein wrote:

And I am absolutely certain that you are wrong. The difference is
that I can cite the reference to the C language standard to back up my certainty, and you cannot.

Your reference has nothing to do with a = b = c;

The C standard specifically states, in paragraph 2 of section 6.5:

========
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.70)
========

Yes, and?

That "70" at the end is a reference to a footnote, subscripts don't
look special in plain text. But here is the context of footnote 70:

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

So you may be certain, but you are certainly wrong.

Am I?

How does a = b = c; invoked undefined behaviour?

c is stored into b (so b is modified)
the NEW value of b is read to store into a.

Never is b modified more than one, and never is the OLD value of b read
for any purpose whatsoever.

a = b = c = d = e = f = g = ... etc is a classic idiom
dont' tell me it's UB

Nov 14 '05 #21

Mark McIntyre wrote:

The order in which the two arguments are evaluated is undefined. The timing of the increment-and-store-back is undefined. Its possible for the
increment to happen before or after the 2nd argument is evaluated. Or
simultaneously.

You probably mean unspecified (or imp. defined).

Nov 14 '05 #22
Luke Wu wrote:

Mark McIntyre wrote:

The order in which the two arguments are evaluated is undefined. The

timing
of the increment-and-store-back is undefined. Its possible for the
increment to happen before or after the 2nd argument is evaluated. Or
simultaneously.

You probably mean unspecified (or imp. defined).

The order of evaluation is indeed unspecified, which is why you're
wrong to think p = p->next = q is well-defined.
Nov 14 '05 #23

infobahn wrote:
Luke Wu wrote:

Mark McIntyre wrote:

The order in which the two arguments are evaluated is undefined. The
timing
of the increment-and-store-back is undefined. Its possible for

the increment to happen before or after the 2nd argument is evaluated. Or simultaneously.

You probably mean unspecified (or imp. defined).

The order of evaluation is indeed unspecified, which is why you're
wrong to think p = p->next = q is well-defined.

The order of evaluation is unspecified for OPERANDS of an operator.
Order of evaluation between OPERATORS is defined by the rules of
precedence and associativity.

you have two =s in your expression statement. Same precedence,but
associativity is right to left. So the right = is dealt with first.

(p = (p->next = q))

Now, whether p->next or q evaluates first is unspecified. But it
doesn't matter, p->next = q happens before p = p->next

Nov 14 '05 #24
Luke Wu wrote:

infobahn wrote:
The order of evaluation is indeed unspecified, which is why you're
wrong to think p = p->next = q is well-defined.
The order of evaluation is unspecified for OPERANDS of an operator.

Yup.
Order of evaluation between OPERATORS is defined by the rules of
precedence and associativity.
Nope. C has no precedence rules. What you call precedence is merely
a consequence of the grammar. And in fact the truth is that order
of evaluation between *sequence points* is unspecified.
you have two =s in your expression statement. Same precedence,but
associativity is right to left. So the right = is dealt with first.
It might be.

(p = (p->next = q))

Now, whether p->next or q evaluates first is unspecified. But it
doesn't matter, p->next = q happens before p = p->next

The evaluations of p, p->next, and q can happen in any order.
Nov 14 '05 #25

infobahn wrote:
Luke Wu wrote:

infobahn wrote:
The order of evaluation is indeed unspecified, which is why you're wrong to think p = p->next = q is well-defined.

The order of evaluation is unspecified for OPERANDS of an operator.

Yup.
Order of evaluation between OPERATORS is defined by the rules of
precedence and associativity.

Nope. C has no precedence rules. What you call precedence is merely
a consequence of the grammar. And in fact the truth is that order
of evaluation between *sequence points* is unspecified.

a + b + c;

b+c can happen first? is that what you're saying? (I always "assumed"
(a+b) will always happen first)

Nov 14 '05 #26
Luke Wu wrote:

infobahn wrote:
Order of evaluation between OPERATORS is defined by the rules of
precedence and associativity.

Nope. C has no precedence rules. What you call precedence is merely
a consequence of the grammar. And in fact the truth is that order
of evaluation between *sequence points* is unspecified.

a + b + c;

b+c can happen first? is that what you're saying? (I always "assumed"
(a+b) will always happen first)

The compiler is free to evaluate a, b, and c in any order it likes.
It can do the additions in any order it likes, provided a strictly
conforming program can't tell the difference between its choice and
(a + b) + c
Nov 14 '05 #27
Luke Wu <Lo***********@gmail.com> wrote:
The order of evaluation is unspecified for OPERANDS of an operator.
Yes, except where it is specified (e.g., in the "," and "&&"
operators, for instance).
Order of evaluation between OPERATORS is defined by the rules of
precedence and associativity.
No. While this would make sense, this is not the case. Technically
speaking, C does not even *have* precedence and associativity --
these are just conveniences for humans trying to parse expressions.
The C standards use fully-factored grammars that can produce only
a single parse tree. We (by which I mean "people") use "precedence"
to disambiguate between two possible parse trees when using different
operators, and then when that fails, we use "associativity" to
disambiguate between two possible parse trees, so that we will come
up with the same tree the C compiler used.

(Of course, as long as it gets the right tree, any given C compiler
is *allowed* to use an operator-precedence grammar. As long as
the fully-factored grammar and the unfactored-but-decorated-with-
precedence-and-associativity grammar produce the same results, who
cares? :-) )
you have two =s in your expression statement. Same precedence,but
associativity is right to left. So the right = is dealt with first.

(p = (p->next = q))

Now, whether p->next or q evaluates first is unspecified. But it
doesn't matter, p->next = q happens before p = p->next

It might be nice if that were the case, but according to those who
specialize in interpretation of the C Standard, it is not the case.

You may want to peruse the DejaGoogle archives of comp.std.c to see
what was said in the last two or three extremely large rounds of
--
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
Reading email is like searching for food in the garbage, thanks to spammers.
Nov 14 '05 #28
infobahn wrote:
Luke Wu wrote:
infobahn wrote:
The order of evaluation is indeed unspecified, which is why
you're wrong to think p = p->next = q is well-defined.

The order of evaluation is unspecified for OPERANDS of an operator.

Yup.
Order of evaluation between OPERATORS is defined by the rules of
precedence and associativity.

Nope. C has no precedence rules. What you call precedence is merely
a consequence of the grammar. And in fact the truth is that order
of evaluation between *sequence points* is unspecified.
you have two =s in your expression statement. Same precedence,but
associativity is right to left. So the right = is dealt with first.

It might be.

(p = (p->next = q))

Now, whether p->next or q evaluates first is unspecified. But it
doesn't matter, p->next = q happens before p = p->next

The evaluations of p, p->next, and q can happen in any order.

However p->next is never evaluated, it is simply stored into. The
thing that is evaluated is (p->next = q). I don't think this makes
any difference to the argument, because there is no argument about
what gets stored in p. The argument would be about where the
storage into (p->next) goes, and that is what is undefined here.

There should be no essential end result difference between:

a = b = c;
and
b = a = c;
but
p = p->next = q;
and
p->next = p = q;

are obviously not able to produce the same result.

--
"If you want to post a followup via groups.google.com, 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
Nov 14 '05 #29
CBFalconer wrote:

infobahn wrote:

The evaluations of p, p->next, and q can happen in any order.

However p->next is never evaluated, it is simply stored into.

Sorry, you're right. I meant, of course, p, p, and q.

p has to be evaluated so that p->next can be located, so it gets
evaluated twice.
Nov 14 '05 #30
On 12 Feb 2005 07:53:50 -0800, in comp.lang.c , "Luke Wu"
<Lo***********@gmail.com> wrote:

Mark McIntyre wrote:

The order in which the two arguments are evaluated is undefined.

You probably mean unspecified (or imp. defined).

The standard doesn't specify two or more alternatives. This is required for
formal unspecified behaviour. 6.5.2.2 (10) says unspecified, but curiously
this doesn't mean its unspecified behhaviour.
--
Mark McIntyre
CLC FAQ <http://www.eskimo.com/~scs/C-faq/top.html>
Nov 14 '05 #31
On 12 Feb 2005 08:56:21 -0800, in comp.lang.c , "Luke Wu"
<Lo***********@gmail.com> wrote:

a + b + c;

b+c can happen first? is that what you're saying? (I always "assumed"
(a+b) will always happen first)

Adding several things together is an actual example in section 5.1.2.3 of

Summary: the compiler can re-order operations in any way it likes, if it
can do so without side-effects.

Thus for integers, provided neither a+b nor b+c overflows* it can evaluate
a+ (b+c) ot (a+b) +c, exactly as you're allowed to in "real life". If its
even slightly possible that a+b or b+c overflows, the compiler is obligated
to follow associativity and precedence rules and evaluate as (a+b) +c

*or the implementation is able to cleverly handle overflows eg it uses
wider objects to do the maths.
--
Mark McIntyre
CLC FAQ <http://www.eskimo.com/~scs/C-faq/top.html>

----== Posted via Newsfeeds.Com - Unlimited-Uncensored-Secure Usenet News==----
http://www.newsfeeds.com The #1 Newsgroup Service in the World! 120,000+ Newsgroups
----= East and West-Coast Server Farms - Total Privacy via Encryption =----
Nov 14 '05 #32
On 12 Feb 2005 07:53:50 -0800, in comp.lang.c , "Luke Wu"
<Lo***********@gmail.com> wrote:

Mark McIntyre wrote:

The order in which the two arguments are evaluated is undefined.

You probably mean unspecified (or imp. defined).

The standard doesn't specify two or more alternatives. This is required for
formal unspecified behaviour. 6.5.2.2 (10) says unspecified, but curiously
this doesn't mean its unspecified behhaviour.
--
Mark McIntyre
CLC FAQ <http://www.eskimo.com/~scs/C-faq/top.html>

----== Posted via Newsfeeds.Com - Unlimited-Uncensored-Secure Usenet News==----
http://www.newsfeeds.com The #1 Newsgroup Service in the World! 120,000+ Newsgroups
----= East and West-Coast Server Farms - Total Privacy via Encryption =----
Nov 14 '05 #33
Chris Torek wrote:
Luke Wu <Lo***********@gmail.com> wrote:
The order of evaluation is unspecified for OPERANDS of an operator.
Yes, except where it is specified (e.g., in the "," and "&&"
operators, for instance).
Order of evaluation between OPERATORS is defined by the rules of
precedence and associativity.

No. While this would make sense, this is not the case. Technically
speaking, C does not even *have* precedence and associativity --
these are just conveniences for humans trying to parse expressions.
The C standards use fully-factored grammars that can produce only
a single parse tree. We (by which I mean "people") use "precedence"
to disambiguate between two possible parse trees when using different
operators, and then when that fails, we use "associativity" to
disambiguate between two possible parse trees, so that we will come
up with the same tree the C compiler used.

Yeah. I've never argued that compilers MUST FOLLOW PREC/ASSOC. My
claim has always been that following precedence and associativity (and
avoiding UB) will produce the "expected" results (doesn't matter if the
compiler actually did something totally unrelated and different to get
that result).

(Of course, as long as it gets the right tree, any given C compiler
is *allowed* to use an operator-precedence grammar. As long as
the fully-factored grammar and the unfactored-but-decorated-with-
precedence-and-associativity grammar produce the same results, who
cares? :-) )

Yes. Exactly. My point always has been that a compiler can do anything
it wants, but it will always get there result we get if we properly
follow the precedence and associativity rules (and don't invoke UB).

My commends from before:
-----
The compiler can do anything it wants at the machine level, but the
result must not differ from what we would get if we did b=c , a=b at
the abstract machine level

if in (a = (b = c)) , the order really does matter (if a, b, c are sub
expressions), then the order implied by the brackets must be taken, if
order doesn't matter, the compiler can do anything

the result of anything the compiler does, must equal the result of:
b=c,a=b
-----

you have two =s in your expression statement. Same precedence,but
associativity is right to left. So the right = is dealt with first.

(p = (p->next = q))

Now, whether p->next or q evaluates first is unspecified. But it
doesn't matter, p->next = q happens before p = p->next
It might be nice if that were the case, but according to those who
specialize in interpretation of the C Standard, it is not the case.

Well that's a weakness in the standard (people who formulated it). As
far as I know, using the precedence and associativity rules (and never
invoking UB) will produce the result that any good compiler will (even
if the compiler at times takes back door routes to produce the
"expected" results).

You may want to peruse the DejaGoogle archives of comp.std.c to see
what was said in the last two or three extremely large rounds of
--

I could, but I've noticed these long drawn out discussions (in csc)
never end. People are so worked up on finding tiny "loop holes" in the
literature that can allow conforming implementations to do weird
unexpected things. Seriously, how many compiler writers are combing
through the standard to find ways to toy with users(programmers)?
I know many of you are thinking "this guy should read the standard, the
standard is the language, if there are loopholes, those loopholes are
part of C!!!" Correct, but showcasing loopholes without stating that
following the prec/ass. rules in introductory books is perfectly safe
for programmers, will only cause confusion in many readers.

Nov 14 '05 #34

infobahn wrote:
Luke Wu wrote:

Mark McIntyre wrote:

The order in which the two arguments are evaluated is undefined. The
timing
of the increment-and-store-back is undefined. Its possible for

the increment to happen before or after the 2nd argument is evaluated. Or simultaneously.

You probably mean unspecified (or imp. defined).

The order of evaluation is indeed unspecified, which is why you're
wrong to think p = p->next = q is well-defined.

Hi,

So is it better to not include many expressions on one line?

Should someone do:

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

?
I am new to C just wondering what is best to not mess up these
associtivity traps.

Nov 14 '05 #35
Deniz Bahar wrote:

infobahn wrote:

The order of evaluation is indeed unspecified, which is why you're
wrong to think p = p->next = q is well-defined.

Hi,

So is it better to not include many expressions on one line?

Should someone do:

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

No need to go quite that far.

p->next = q;
p = q;

is fine.
Nov 14 '05 #36
infobahn wrote:
Deniz Bahar wrote:

.... snip ...

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

No need to go quite that far.

p->next = q;
p = q;

is fine.

#define q buff[i++]

and yours flunks, while the OPs flies right along. </nit>

--
"If you want to post a followup via groups.google.com, 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

Nov 14 '05 #37
On 12 Feb 2005 08:27:46 -0800, "Luke Wu" <Lo***********@gmail.com>
wrote in comp.lang.c:

infobahn wrote:
Luke Wu wrote:

Mark McIntyre wrote:

>
> The order in which the two arguments are evaluated is undefined. The timing
> of the increment-and-store-back is undefined. Its possible for the > increment to happen before or after the 2nd argument is evaluated. Or > simultaneously.
>

You probably mean unspecified (or imp. defined).

The order of evaluation is indeed unspecified, which is why you're
wrong to think p = p->next = q is well-defined.

The order of evaluation is unspecified for OPERANDS of an operator.
Order of evaluation between OPERATORS is defined by the rules of
precedence and associativity.

you have two =s in your expression statement. Same precedence,but
associativity is right to left. So the right = is dealt with first.

(p = (p->next = q))

Now, whether p->next or q evaluates first is unspecified. But it
doesn't matter, p->next = q happens before p = p->next

No, you are assuming that precedence and associativity forces order of
operation again, and even more importantly that it forces order of
modification of objects.

The expression p = p->next = q; could be executed as:

1. Evaluate the value of 'q'.

2. If an automatic conversion is available and necessarily, convert
'q' to the type of 'p->next', and this value, after the conversion if
any, is the value that will be assigned to p->next.

3. Store the value unchanged or converted from step 2 into 'p', with
a possible further conversion if the type of 'p' is different from the
type of 'p->next'.

4. Evaluate 'p' (the new value, equal to 'q') and dereference it to
store the value unchanged or converted from step to into 'p->next'.

To put it more concretely, let's assume a type:

struct obj { struct obj *next };

And two objects of this type:

struct obj o1, o2;

And two pointers to objects of this type:

struct obj *p = &o1, *q = &o2;

You are making assertions about results after the sequence point at
the end of the statement:

p = p->next = q;

....namely:

1. 'p' and 'q' will be identical, both will point to 'o2', neither
will point to 'o1'. This is true.

2. 'o1.next' will point to 'o2' and 'o2.next' will still be
uninitialized if it has automatic storage duration, or NULL if it has
static storage duration, and this is NOT guaranteed by the C standard.

The compiler could follow the four steps I outlined above:

1. Evaluate the lvalue 'q' to its contents, an address of type
'pointer to struct obj' (actual value &02).

2. Evaluate the TYPE of p->next, and since it is also 'pointer to
struct obj', leave the value from step 1 unchanged.

3. Store this value (&o2) into 'p', where again it needs no
conversion.

4. Evaluate 'p' (now containing &o2 instead of the original &o1) and
store the value from step 1, which needed no conversions, into
'p->next'.

I am not saying that any given compiler would actually execute that
statement in that sequence, but THERE IS NOTHING IN THE C STANDARD
THAT PREVENTS IT.

You are expecting o1.next to contain the address of o2, but a compiler
that did not modify o1.next and instead assigned o2.next with the
address of o2 WOULD BE COMPLETELY CONFORMING.

And that is because the evaluation of 'p->next' is NOT required to
compute the new value to be assigned to 'p'. The assignment operators
in C most specifically do NOT store a value into an object, then read
that value back, to yield the value of the expression.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.contrib.andrew.cmu.edu/~a...FAQ-acllc.html
Nov 14 '05 #38

Jack Klein wrote:
On 11 Feb 2005 16:12:57 -0800, "Luke Wu" <Lo***********@gmail.com>
wrote in comp.lang.c:

Dave Vandervies wrote:
In article <11**********************@f14g2000cwb.googlegroups .com>, Luke Wu <Lo***********@gmail.com> wrote:

>No, the compiler views it according to the associativity rules(right
to
>left) as this:
>
>(a = (b = c)) <=== right to left associativity

Yes, that's how it's parsed.
No, that doesn't impose requirements on the order things are

actually done. All that's required is that these happen:
- c is evaluated to get a value
- The value of c, converted appropriately, is stored in b
- The value stored in b, converted appropriately, is stored in a. Which order they happen in depends only on dependencies between them, and the store to a only depends on the value stored in b being

calculated
(and even there the compiler may be able to determine what it is

without
actually calculating it - see my example below), not on it actually being stored in b.

So you're telling me that a = b = c = 0; will no necessarily set the
three variables to 0?

Nov 14 '05 #39
Luke Wu wrote:
[manual reformatting due to wrongly broken lines]
Dave Vandervies wrote:

Luke Wu <Lo***********@gmail.com> wrote:

>No, the compiler views it according to the associativity
>rules(right to
>left) as this:
>
>(a = (b = c)) <=== right to left associativity

Yes, that's how it's parsed.
No, that doesn't impose requirements on the order things are
actually
done. All that's required is that these happen:
- c is evaluated to get a value
- The value of c, converted appropriately, is stored in b
- The value stored in b, converted appropriately, is stored in
a.
Which order they happen in depends only on dependencies between
them, and the store to a only depends on the value stored in b
being calculated
(and even there the compiler may be able to determine what it is
without actually calculating it - see my example below), not on it
actually being stored in b.

So you're telling me that a = b = c = 0; will no necessarily set the
three variables to 0?

No, he is not. Parse again.
And if you are at it: Please respond to the message written by the
one you are responding to, not something further downthread; and
Cheers
Michael
--
E-Mail: Mine is an /at/ gmx /dot/ de address.
Nov 14 '05 #40
CBFalconer wrote:

infobahn wrote:
Deniz Bahar wrote:

... snip ...

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

No need to go quite that far.

p->next = q;
p = q;

is fine.

#define q buff[i++]

and yours flunks, while the OPs flies right along. </nit>

But you forgot that, before that #define, there was another one:

#define temp buff[i]

15-all. Falconer to serve.
Nov 14 '05 #41
infobahn wrote:
CBFalconer wrote:
infobahn wrote:
Deniz Bahar wrote:

... snip ...
temp = q;
p->next=temp;
p = temp;

No need to go quite that far.

p->next = q;
p = q;

is fine.

#define q buff[i++]

and yours flunks, while the OPs flies right along. </nit>

But you forgot that, before that #define, there was another one:

#define temp buff[i]

15-all. Falconer to serve.

But who is umpiring? :-)

Nov 14 '05 #42
On Sun, 13 Feb 2005 19:31:53 GMT, CBFalconer
<cb********@yahoo.com> wrote:
infobahn wrote:
Deniz Bahar wrote:

... snip ...

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

No need to go quite that far.

p->next = q;
p = q;

is fine.

#define q buff[i++]

and yours flunks, while the OPs flies right along. </nit>

#define p (*pp++)

and yours flunks as well. In other words, that's a strawman, it has
been assumed all along that p and q are atomic types with no
side-effects.

Is

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

well-defined? I hope so, otherwise there are a /lot/ of programs
(almost anything using linked lists!) which have UB...

Chris C
Nov 14 '05 #43
Chris Croughton wrote:

Is

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

well-defined?

Given all the usual caveats, and absence of malware #defines, it's fine.
Nov 14 '05 #44
Luke Wu wrote:

So you're telling me that a = b = c = 0; will no necessarily set the
three variables to 0?

a = b = c = 0; parses to:

=
/ \
a =
/ \
b =
/ \
c 0
As you can see, the prec&assoc of operators helps us get this tree.
In this case, there is pretty much a strong order of evaluation
existing only because a, b, c are not subexpressions, so the compiler
can't fill in any details of the tree except for the c = 0 branch
(it has only 1 operator in the tree at every stage with valid operands
to work on at that stage).

So a = b = c = 0 will set everything to 0.
If on the other hand we had something like this: y = (a + b) * (b = 4)
This parses to:

=
/ \
y *
/ \
+ =
/ \ / \
a b b 4
The compiler can not deal with the topmost = on the first pass, nor the
* because they don't yet have valid operands at this stage. But both +
and the bottom = have valid operands, so the compiler is free to do
either one first. This causes problems, because order matters a lot in
this case, as you can see.
So "order of evaluation" doesn't exist in its full form. The only
order that the compiler is forced to take is to pick a point in the
tree that has enough information to produce results for operators (if
there are 6 operators with operands at that point, the operator is free
to work on any of these sub-expressions).

Nov 14 '05 #45
Mark McIntyre <ma**********@spamcop.net> writes:
On 12 Feb 2005 08:56:21 -0800, in comp.lang.c , "Luke Wu"
<Lo***********@gmail.com> wrote:

a + b + c;

b+c can happen first? is that what you're saying? (I always "assumed"
(a+b) will always happen first)

Adding several things together is an actual example in section 5.1.2.3 of

Summary: the compiler can re-order operations in any way it likes, if it
can do so without side-effects.

Thus for integers, provided neither a+b nor b+c overflows* it can evaluate
a+ (b+c) ot (a+b) +c, exactly as you're allowed to in "real life". If its
even slightly possible that a+b or b+c overflows, the compiler is obligated
to follow associativity and precedence rules and evaluate as (a+b) +c

*or the implementation is able to cleverly handle overflows eg it uses
wider objects to do the maths.

Mark's summary is correct but I think it may be misleading.

Indeed the program must behave as if 'a + b' is evaluated first.

In the compiled code, 'b + c' might be computed first, and then added
to 'a'. Or, the value of 'a + b' might be left over after a previous
computation, and the old value simply reused and not recomputed;
similarly an old value of 'b + c'. Or, 'a + c' could be computed, and
then added to 'b'. Or, the compiler might deduce that the value of
the expression 'a + b + c' is never used, and never compute that value
at all. Or, if the values of the variables can be determined at
compile time, the code might arrange to avoid any run-time addition at
all and simply load the value; or, cause a trap indicating overflow.
All these scenarios are possible, in the compiled code.

But still: the program must *behave* as if 'a + b' is evaluated first.

================================================== ====================

EXAMPLE 6 To illustrate the grouping behavior of expressions, in the
following fragment

int a, b;
/* ... */
a = a + 32760 + b + 5;

the expression statement behaves exactly the same as

a = (((a + 32760) + b) + 5);

due to the associativity and precedence of these operators. Thus, the
result of the sum (a + 32760) is next added to b, and that result is
then added to 5 which results in the value assigned to a. On a machine
in which overflows produce an explicit trap and in which the range of
values representable by an int is [-32768, +32767], the implementation
cannot rewrite this expression as

a = ((a + b) + 32765);

since if the values for a and b were, respectively, -32754 and -15,
the sum a + b would produce a trap while the original expression would
not; nor can the expression be rewritten either as

a = ((a + 32765) + b);
or

a = (a + (b + 32765));

since the values for a and b might have been, respectively, 4 and -8
or -17 and 12. However, on a machine in which overflow silently
generates some value and where positive and negative overflows cancel,
the above expression statement can be rewritten by the implementation
in any of the above ways because the same result will occur.
Nov 14 '05 #46
On 16 Feb 2005 08:57:44 -0800, in comp.lang.c , Tim Rentsch
<tx*@alumnus.caltech.edu> wrote:
Mark McIntyre <ma**********@spamcop.net> writes:
On 12 Feb 2005 08:56:21 -0800, in comp.lang.c , "Luke Wu"
<Lo***********@gmail.com> wrote:
>
>a + b + c;
>
>b+c can happen first? is that what you're saying? (I always "assumed"
>(a+b) will always happen first)

the compiler can re-order operations in any way it likes, if it
can do so without side-effects.

Mark's summary is correct but I think it may be misleading.

Indeed the program must behave as if 'a + b' is evaluated first.

Indeed, but the point is, it can actually evaluate b+c first. The OP
thought it always did a+b, which is erroneous.

--
Mark McIntyre
CLC FAQ <http://www.eskimo.com/~scs/C-faq/top.html>
Nov 14 '05 #47
Chris Torek <no****@torek.net> writes:
Luke Wu <Lo***********@gmail.com> wrote:
Order of evaluation between OPERATORS is defined by the rules of
precedence and associativity.

No. While this would make sense, this is not the case. Technically
speaking, C does not even *have* precedence and associativity --
these are just conveniences for humans trying to parse expressions.
The C standards use fully-factored grammars that can produce only
a single parse tree. We (by which I mean "people") use "precedence"
to disambiguate between two possible parse trees when using different
operators, and then when that fails, we use "associativity" to
disambiguate between two possible parse trees, so that we will come
up with the same tree the C compiler used.

Just a minor comment here.

It's correct to say that the C standards define the language using
grammars rather than rules for precedence and associativity; but, the
notions of precedence and associativity are also used. The term
"precedence" is used in the primary text, in examples, and in notes;
the term "associativity" is used in examples and in notes. The
intention seems to be that "precedence" and "associativity" are used
in their normal senses, with the precedence and associativity of
operators defined not explicitly but rather implicitly by the grammar;
e.g., 6.5, Note 71:
71) The syntax specifies the precedence of operators in the
evaluation of an expression, which is the same as the order
of the major subclauses of this subclause, highest
precedence first. [...]

Within each major subclause, the operators have the same
precedence. Left-or right-associativity is indicated in each
subclause by the syntax for the expressions discussed
therein.

So I think it's fair to talk about the precedence and associativity of
operators, with the understanding that these attributes are derived
from the syntax of the various expressional forms rather than being
defined explicitly on operators.
Nov 14 '05 #48
Mark McIntyre wrote:
Tim Rentsch wrote:

....
Indeed the program must behave as if 'a + b' is evaluated first.

Indeed, but the point is, it can actually evaluate b+c first. The OP
thought it always did a+b, which is erroneous.

Is it? Doesn't 5.1.2.3#14 explicitly forbid this possibility?

Jirka
Nov 14 '05 #49
Mark McIntyre <ma**********@spamcop.net> writes:
On 12 Feb 2005 07:53:50 -0800, in comp.lang.c , "Luke Wu"
<Lo***********@gmail.com> wrote:

Mark McIntyre wrote:

The order in which the two arguments are evaluated is undefined.

You probably mean unspecified (or imp. defined).

The standard doesn't specify two or more alternatives. This is required for
formal unspecified behaviour. 6.5.2.2 (10) says unspecified, but curiously
this doesn't mean its unspecified behhaviour.

Actually it does mean unspecified behavior. The standard uses the
term "unspecified", by itself, in the text to mean "unspecified
behavior". If we look in Annex J

J.1 Unspecified behavior

The following are unspecified:

[very long list, including 6.5.2.2]

Also look at the definition of "unspecified behavior" in 3.4.4

3.4.4
unspecified behavior
behavior where this International Standard provides two or more
possibilities and imposes no further requirements on which is
chosen in any instance

EXAMPLE An example of unspecified behavior is the order in
which the arguments to a function are evaluated.

The example, by coincidence, names precisely the case of 6.5.2.2 as
being unspecifed behavior.

Furthermore, the word "provides" is used not in the sense of "states"
but in the sense of "allows" or "affords" (as is illustrated in the
sentence "A blanket provides warmth", for example). No orders of
evaluation for function arguments are explicitly stated in the text of
section 6.5.2.2 (or anywhere else for that matter); rather, it is the
evaluation orders allowed by the entire text of the standard that make
up the set of possibilities out of which a particular evaluation order
must be chosen.
Nov 14 '05 #50