What is a sequence point?

I

infobahn

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

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

#define temp buff

15-all. Falconer to serve.
 
B

BRG

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

15-all. Falconer to serve.


But who is umpiring? :)

Brian Gladman
 
C

Chris Croughton

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>

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

Kenneth Bull

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

Tim Rentsch

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

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.

Please do read the example, 5.1.2.3 Example 6.

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

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

Mark McIntyre

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

Tim Rentsch

Chris Torek said:
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.
 
J

Jirka Klaue

Mark said:
Tim Rentsch wrote: ....

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
 
T

Tim Rentsch

Mark McIntyre said:
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.
 
T

Tim Rentsch

CBFalconer said:
infobahn said:
Luke said:
infobahn wrote: [redundant quoting snipped]

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

It's true that a compiler need not emit any code specifically for the
expression 'p->next'. But in the abstract machine 'p->next' is
evaluated. Looking at the semantics of the '->' operator in section
6.5.2.3, p4:

A postfix expression followed by the -> operator and an identifier
designates a member of a structure or union object. The value is
that of the named member of the object to which the first
expression points, and is an lvalue.

In the abstract machine, it is evaluating the expression that yields
its value. Also, expressions that are lvalue's are evaluated:
section 6.3.2.1, p1, says

An lvalue is an expression with an object type or an incomplete
type other than void; if an lvalue does not designate an object
when it is evaluated, the behavior is undefined.

Evaluating an lvalue does not mean getting the stored value; that
is discussed separately, in 6.3.2.1, p2:

Except when it is the operand of the sizeof operator, the unary &
operator, the ++ operator, the --operator, or the left operand of
the . operator or an assignment operator, an lvalue that does not
have array type is converted to the value stored in the designated
object (and is no longer an lvalue).

The
thing that is evaluated is (p->next = q).

The expression '(p->next = q)' is also evaluated. That evaluation
both yields the value and produces the side effect. The side effect
might not complete until later, but it is produced by evaluating the
expression - section 5.1.3.2.

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.

Of the various people responding to the 'p = p->next = q;' question,
only Chris Torek was conscientious enough to note the discussions in
comp.std.c on this and related topics. Rather than get into those
arguments again here, anyone interested is invited to see the
discussions in comp.std.c, which are still going on.

Incidentally, even if one admits the possibility that the evaluation
of the containing expression 'p = p->next = q' might precede the
evaluation of the contained expression 'p->next = q' (which question
is still being debated), evaluation order is only "unspecified", not
"undefined".
 
M

Mark McIntyre

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

it explicitly allows it:

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

Mark McIntyre

Actually it does mean unspecified behavior.

I'm unconvinced. I take the word "provides" to mean "provides" rather than
"allows". The two words are different and I suspect you're thinking of
"provides for".
If we look in Annex J

Annex J is informative and thus not gospel.
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).

Euh, in that sentence, the word means "gives". Not "permits you to have".

But this is a discussion for comp.std.c methinks. Either way, the behaviour
isn't specified by the standard....
 
T

Tim Rentsch

Mark McIntyre said:
Actually it [referring to 6.5.2.2 p10] does mean
unspecified behavior.

I'm unconvinced.

Hmmm. 6.5.2.2 p10 says

The order of evaluation of the function designator, the actual
arguments, and subexpressions within the actual arguments is
unspecified, but there is a sequence point before the actual
call.

Nothing else in 6.5.2.2 says anything using the word "unspecified".
Earlier, 3.4.4 p1-2, defining "unspecified behavior", says

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.

Those two together don't convince you that the word "unspecified" in
6.5.2.2 p10 is supposed to mean "unspecified behavior"? I'm at a loss
to see how a reasonable person could reach any other conclusion.

Annex J is informative and thus not gospel.

That's true, Annex J is informative rather than normative. But all
the evidence I'm aware of - 3.4.4, 6.5.2.2, J.1 - all support the
conclusion that use of the term "unspecified" is meant to be
synonymous with "unspecified behavior". Is there even a scintilla of
evidence that the word "unspecified" is ever used in the standard to
mean something other than "unspecified behavior"?


[repeated, rearranged for context]
I take the word "provides" to mean "provides" rather than
"allows". The two words are different and I suspect you're thinking of
"provides for".

No, I wasn't. The "provides for" phrasing is the intransitive use of
provides, and clearly the transitive is what's meant here. See for
example the online Merriam-Webster's dictionary, which gives AFFORDS
as one of the principal meanings for the transitive verb "provide".
Actually the sense that probably comes closest is "2 a : to supply or
make available"; the point I was trying to make is that the providing
is usually done implicitly rather than by explicit listing of possible
behaviors.

Euh, in that sentence, the word means "gives". Not "permits you to have".

No, it doesn't. Blankets don't *give* warmth (unless someone was talking
about electric blankets, which I wasn't). Blankets do *afford* warmth.

But this is a discussion for comp.std.c methinks.

Yes, it does seem like it's reached the point where it could
benefit from that audience.

Either way, the behaviour isn't specified by the standard....

Behavior that is "unspecified behavior" still isn't totally
unconstrained; the unspecified behavior that occurs must still be one
of the behaviors that the standard otherwise provides (ignoring for
the moment whether "provides" is meant in the sense of "explicitly
states" or "implicitly supplies"). So if, as I contend, this is a
case of unspecified behavior, that still doesn't mean any behavior is
possible - it must be a behavior that is provided by the standard.

If the meaning is not "unspecified behavior" but simply "unspecified"
(whatever that might mean), and there is no explicit definition of
behavior, then by section 4 p2, it is "undefined behavior". There is
no middle ground where the behavior is completely not specified yet
isn't undefined.
 

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,764
Messages
2,569,564
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top