What is a sequence point?

L

Luke Wu

Jack said:
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;
========

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
 
L

Luke Wu

Mark said:
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).
 
I

infobahn

Luke said:
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.
 
L

Luke Wu

infobahn said:
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
 
I

infobahn

Luke said:
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.
 
L

Luke Wu

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

So what about:

a + b + c;

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

Chris Torek

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
discussion about this.
 
I

infobahn

Luke said:
So what about:

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
 
C

CBFalconer

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


It might be.


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

infobahn

CBFalconer said:
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.
 
M

Mark McIntyre

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

Mark McIntyre

So what about:

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
the C Standard. Why not read it instead of guessing?



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

Mark McIntyre

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

Luke Wu

Chris said:
Yes, except where it is specified (e.g., in the "," and "&&"
operators, for instance).


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

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
discussion about this.
--

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

Deniz Bahar

infobahn said:
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.
 
I

infobahn

Deniz said:
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.
 
C

CBFalconer

infobahn said:
Deniz Bahar wrote:
.... snip ...

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>
 
J

Jack Klein

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

Luke Wu

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

Michael Mair

Luke Wu wrote:
[manual reformatting due to wrongly broken lines]
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
make your newsreader break the lines correctly.


Cheers
Michael
 

Ask a Question

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

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

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top