Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

function composition, sequence point, and unsuspected side effects

204 views
Skip to first unread message

regis

unread,
Nov 15, 2013, 12:17:14 AM11/15/13
to

Greetings,

my question relates to function calls with hidden side effects,
that are used as arguments of another function, particularly
in regard to the two following sections found in the draft

[3.3.2.2 Function calls] "The order of evaluation of the function
designator, the arguments, and subexpressions within the arguments is
unspecified."

[2.1.2.3 Program execution] "... at sequence points, all side effects of
previous evaluations shall be complete and no side effects of subsequent
evaluations shall have taken place."



For the sake of the exposition, let assume the type Pair,
and a function MakePair() returning a Pair:

typedef struct { int first, second; } Pair;

Pair MakePair (int first, int second) {
Pair pair;
pair.first= first;
pair.second= second;
return pair;
}

Assume 666 and 777 are the next two integers returned by rand(),
and consider the call: Pair rand_pair= MakePair (rand(), rand());

rand() probably does some side-effects on some global variables
to prepare for its next call, which leads me to wonder what to
think about the behavior or the call:

1) side-effects of both rand() calls may interleave, behavior is
undefined and program may break ?

2) rand_pair is either (Pair){ 666, 777 } or (Pair){ 777, 666 }
at the discretion of the implementation, i.e., the order of evaluation
of both rand() calls is unspecified, but we end with a random pair,
and all run smoothly in all rand() calls ?

3) any other scenario ?

Similar scenarios could be made with functions unsuspectingly touching
errno through other functions deeper in their code, or with functions
unsuspectingly using static data through deeper calls to, say, ctime().

--
regis



Robert Wessel

unread,
Nov 15, 2013, 2:20:02 AM11/15/13
to
On Fri, 15 Nov 2013 06:17:14 +0100, regis <regis.ba...@free.fr>
wrote:
#2 is correct in this case. All of the parameters are evaluated
before the call to the function, but the order in which they're
evaluated is not defined. In any event he cannot interleave function
calls, since there are sequence points before and after function calls
too.

IOW, if you had "MakePair(f(), g())" he could call either f() or g()
first, but whichever one got called first would have to complete and
return before the second one could return.

As for touching a global like errno, it leads to errno being undefined
if both f() and g() use it. If both set it, you won't know which
value will be left at the end, if one sets it and the other checks it,
you won't know if the checking routine will see the value from before
or after the setting routine.

glen herrmannsfeldt

unread,
Nov 15, 2013, 2:23:21 AM11/15/13
to
regis <regis.ba...@free.fr> wrote:

> my question relates to function calls with hidden side effects,
> that are used as arguments of another function, particularly
> in regard to the two following sections found in the draft

> [3.3.2.2 Function calls] "The order of evaluation of the function
> designator, the arguments, and subexpressions within the arguments is
> unspecified."

> [2.1.2.3 Program execution] "... at sequence points, all side effects of
> previous evaluations shall be complete and no side effects of subsequent
> evaluations shall have taken place."

(snip)

> Pair MakePair (int first, int second) {

(snip)

> Assume 666 and 777 are the next two integers returned by rand(),
> and consider the call: Pair rand_pair= MakePair (rand(), rand());

> rand() probably does some side-effects on some global variables
> to prepare for its next call, which leads me to wonder what to
> think about the behavior or the call:

> 1) side-effects of both rand() calls may interleave, behavior is
> undefined and program may break ?

In actual use, the most likely result is that the two random numbers
come out in unspecified order. Still, the general rule on undefined
behavior is that anything can happen.

> 2) rand_pair is either (Pair){ 666, 777 } or (Pair){ 777, 666 }
> at the discretion of the implementation, i.e., the order of evaluation
> of both rand() calls is unspecified, but we end with a random pair,
> and all run smoothly in all rand() calls ?

> 3) any other scenario ?

> Similar scenarios could be made with functions unsuspectingly touching
> errno through other functions deeper in their code, or with functions
> unsuspectingly using static data through deeper calls to, say, ctime().

The usual random number generators keep their state in static variables,
so, yes, it is a similar problem.

On a multithreaded system, you could imagine both calls to rand() at the
same time, such that the internal state was not self consistent.
I don't know that any systems do that, though.

-- glen

Philip Lantz

unread,
Nov 15, 2013, 4:00:36 AM11/15/13
to
glen herrmannsfeldt wrote:
> regis wrote:
> > my question relates to function calls with hidden side effects,
> > that are used as arguments of another function, particularly
> > in regard to the two following sections found in the draft
>
> > [3.3.2.2 Function calls] "The order of evaluation of the function
> > designator, the arguments, and subexpressions within the arguments is
> > unspecified."
>
> > [2.1.2.3 Program execution] "... at sequence points, all side effects of
> > previous evaluations shall be complete and no side effects of subsequent
> > evaluations shall have taken place."
>
> (snip)
>
> > Pair MakePair (int first, int second) {
>
> (snip)
>
> > Assume 666 and 777 are the next two integers returned by rand(),
> > and consider the call: Pair rand_pair= MakePair (rand(), rand());
>
> > rand() probably does some side-effects on some global variables
> > to prepare for its next call, which leads me to wonder what to
> > think about the behavior or the call:
>
> > 1) side-effects of both rand() calls may interleave, behavior is
> > undefined and program may break ?
>
> In actual use, the most likely result is that the two random numbers
> come out in unspecified order. Still, the general rule on undefined
> behavior is that anything can happen.

Why do you think the behavior is undefined?

Malcolm McLean

unread,
Nov 15, 2013, 4:29:52 AM11/15/13
to
On Friday, November 15, 2013 9:00:36 AM UTC, Philip Lantz wrote:
> glen herrmannsfeldt wrote:
>
> > In actual use, the most likely result is that the two random numbers
> > come out in unspecified order. Still, the general rule on undefined
> > behavior is that anything can happen.
>
> Why do you think the behavior is undefined?
>
The C standard doesn't attempt to define what happens when two threads contest
for a resource. Assuming the state is 8 bytes, and words are four bytes, the
compiler could emit load high byte, load low byte, store high byte store low
byte, or the reverse, leading to different behaviour when a store interleaves
between the two loads. Or the operating system could detect this situation and
terminate the program.
So it's classic undefined behaviour.

Tim Rentsch

unread,
Nov 15, 2013, 5:37:45 AM11/15/13
to
regis <regis.ba...@free.fr> writes:

> my question relates to function calls with hidden side effects,
> that are used as arguments of another function, particularly
> in regard to the two following sections found in the draft
>
> [3.3.2.2 Function calls] "The order of evaluation of the function
> designator, the arguments, and subexpressions within the arguments
> is unspecified."
>
> [2.1.2.3 Program execution] "... at sequence points, all side
> effects of previous evaluations shall be complete and no side
> effects of subsequent evaluations shall have taken place."

Based on the numbering it looks like you are using an ANSI (ie,
pre-ISO) document. For reference here are some pointers to
drafts of later versions of the ISO C standard:

Pre-C99
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n843.pdf
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n843.htm
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n869/n869.pdf.gz
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n869/n869.txt.gz
Post-C99
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf
Pre-C11
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf

Sometimes it is useful to compare different drafts to see how
the text has evolved over time.


> For the sake of the exposition, let assume the type Pair,
> and a function MakePair() returning a Pair:
>
> typedef struct { int first, second; } Pair;
>
> Pair MakePair (int first, int second) {
> Pair pair;
> pair.first= first;
> pair.second= second;
> return pair;
> }
>
> Assume 666 and 777 are the next two integers returned by rand(),
> and consider the call: Pair rand_pair = MakePair (rand(), rand());
>
> rand() probably does some side-effects on some global variables
> to prepare for its next call, which leads me to wonder what to
> think about the behavior or the call:
>
> 1) side-effects of both rand() calls may interleave, behavior is
> undefined and program may break ?
>
> 2) rand_pair is either (Pair){ 666, 777 } or (Pair){ 777, 666 } at
> the discretion of the implementation, i.e., the order of evaluation
> of both rand() calls is unspecified, but we end with a random pair,
> and all run smoothly in all rand() calls ?
>
> 3) any other scenario ?
>
> Similar scenarios could be made with functions unsuspectingly
> touching errno through other functions deeper in their code, or
> with functions unsuspectingly using static data through deeper
> calls to, say, ctime().

This is an excellent question. Let me rephrase it slightly
to simplify the discussion. With this function definition:

int
stateful(){
static int t;
t = 1 - t;
return t;
}

will the result of evaluating

stateful() - stateful()

be (a) undefined, (b) unspecified but always either 1 or -1, or
(c) something else? For what you're asking about the simpler
question is the same as the original - the outer function call to
MakePair doesn't change anything, and using stateful() rather
than a library function avoids the question of whether library
functions are somehow special in this regard (which they are not,
in case you were wondering, but it still helps to start with the
simpler formulation).

In C11, the answer is clearly (b), because C11 changed the
wording for how expression sequencing is done. Looking in n1570,
section 6.5.2.2, paragraph 10, we see that evaluation of function
bodies is /indeterminately sequenced/ with respect to other
evaluations in the calling function, which means they are done
either entirely before or entirely after, but never interleaved.
Speaking informally, calling a function is done "atomically"
relative to any other evaluation, including in particular another
function call.

Now what about C99 and C90? Here the answer is also (b), but
this is not evident looking just at what is written in the
two Standard documents (or the various drafts). This question
was addressed in an early Defect Report

http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_087.html

for C90. Apparently either this was forgotten, or the committee
thought the answer given in DR 87 was sufficient, because C99
doesn't make any changes regarding this issue. The question came
up again, in a slightly different form, after C99 in this DR:

http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_287.htm

which contains this sentence in its offcial response:

As noted in the response to DR 087, function calls in the
same expression do not overlap. This has not changed for
C99.

So this should answer your question, and also explain why
the answer isn't evident from what is said in the C90 or
C99 standards.

Tim Rentsch

unread,
Nov 15, 2013, 5:53:01 AM11/15/13
to
The conclusion is right but the reasoning isn't. If f() and g()
modify the same global variable x, then the expression

(10, f(), 20) + (10, g(), 20)

is okay, but

(10, x = 1, 20) + (10, x = 2, 20)

isn't, even though in both cases there are sequence points before
and after the modification of x. (Two sequence points together
have the same effect as one.) Function calls are special, as
explained elsethread.

> IOW, if you had "MakePair(f(), g())" he could call either f() or g()
> first, but whichever one got called first would have to complete and
> return before the second one could return.
>
> As for touching a global like errno, it leads to errno being undefined
> if both f() and g() use it. If both set it, you won't know which
> value will be left at the end, if one sets it and the other checks it,
> you won't know if the checking routine will see the value from before
> or after the setting routine.

What I think you mean is that the value of errno is unspecified
(which is indeed the case) rather than undefined.

Tim Rentsch

unread,
Nov 15, 2013, 6:03:00 AM11/15/13
to
glen herrmannsfeldt <g...@ugcs.caltech.edu> writes:

> regis <regis.ba...@free.fr> wrote:
>
>> [can execution of two function bodies interleave?]
>
> On a multithreaded system, you could imagine both calls to rand()
> at the same time, such that the internal state was not self
> consistent.

This kind of reasoning is sometimes a good way to understand why
something /should/ be undefined behavior, but it isn't a good
way to decide if something /is/ undefined behavior. The result
of an an expression like f() + g() is unspecified, not undefined.

Fuseblower

unread,
Nov 15, 2013, 6:22:31 AM11/15/13
to
On Fri, 15 Nov 2013 01:29:52 -0800 (PST), Malcolm McLean
<malcolm...@btinternet.com> wrote:

>On Friday, November 15, 2013 9:00:36 AM UTC, Philip Lantz wrote:
>> glen herrmannsfeldt wrote:
>>
>> > In actual use, the most likely result is that the two random numbers
>> > come out in unspecified order. Still, the general rule on undefined
>> > behavior is that anything can happen.
>>
>> Why do you think the behavior is undefined?
>>
>The C standard doesn't attempt to define what happens when two threads contest
>for a resource.

(snip)

>So it's classic undefined behaviour.

I don't think so. The Standard defines what it considers undefined
behaviour.

It also defines the order in which arguments of a function are
evaluated as "unspecified".

Here's the snipped part again :

>Assuming the state is 8 bytes, and words are four bytes, the
>compiler could emit load high byte, load low byte, store high byte store low
>byte, or the reverse, leading to different behaviour when a store interleaves
>between the two loads.

The actual code that the compiler emits isn't defined in the Standard.
It depends on the target CPU. A simple integer division would be 1
machine code instruction on an x86 but might be a couple of
instructions on a Z80 (which doesn't have a division instruction).

Even a simple assignment might result in several instructions, like in
your example.

And all these "strings of machine code instructions" can be
"interrupted" by another thread in such a way that the result is
undefined.

But it's something else to suggest the Standard defines C operations
as "undefined behaviour" just because they can be interrupted by
another thread.

Unless the Standard states somewhere that when one uses
multi-threading, all objects become volatile. In such a case sequence
points become meaningless and we no longer have an abstract machine.

In that case, every single operation involving operands would invoke
undefined behaviour.

The case of the OP, IMO, is a clear case of "unspecified behaviour".

>Or the operating system could detect this situation and
>terminate the program.

Possible, but very unlikely :)

Tim Rentsch

unread,
Nov 15, 2013, 9:01:26 AM11/15/13
to
Malcolm McLean <malcolm...@btinternet.com> writes:

> On Friday, November 15, 2013 9:00:36 AM UTC, Philip Lantz wrote:
>> glen herrmannsfeldt wrote:
>>
>> > In actual use, the most likely result is that the two random numbers
>> > come out in unspecified order. Still, the general rule on undefined
>> > behavior is that anything can happen.
>>
>> Why do you think the behavior is undefined?
>
> The C standard doesn't attempt to define what happens when two
> threads contest for a resource.

Threads have nothing to do with the question here.

> Assuming the state is 8 bytes, and words are four bytes, the
> compiler could emit load high byte, load low byte, store high byte
> store low byte, or the reverse, leading to different behaviour
> when a store interleaves between the two loads. Or the operating
> system could detect this situation and terminate the program.
> So it's classic undefined behaviour.

Irrelevant reasoning, and wrong conclusion. The result is
unspecified, not undefined, behavior.

regis

unread,
Nov 15, 2013, 10:36:50 AM11/15/13
to

regis

unread,
Nov 15, 2013, 11:19:17 AM11/15/13
to
On 11/15/13 11:37, Tim Rentsch wrote:
> In C11, the answer is clearly (b), because C11 changed the
> wording for how expression sequencing is done. Looking in n1570,
> section 6.5.2.2, paragraph 10, we see that evaluation of function
> bodies is/indeterminately sequenced/ with respect to other
> evaluations in the calling function, which means they are done
> either entirely before or entirely after, but never interleaved.
> Speaking informally, calling a function is done "atomically"
> relative to any other evaluation, including in particular another
> function call.
>
> Now what about C99 and C90? Here the answer is also (b), but
> this is not evident looking just at what is written in the
> two Standard documents (or the various drafts). This question
> was addressed in an early Defect Report
>
> http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_087.html
>
> for C90. Apparently either this was forgotten, or the committee
> thought the answer given in DR 87 was sufficient, because C99
> doesn't make any changes regarding this issue. The question came
> up again, in a slightly different form, after C99 in this DR:
>
> http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_287.htm
>
> which contains this sentence in its offcial response:
>
> As noted in the response to DR 087, function calls in the
> same expression do not overlap. This has not changed for
> C99.
>
> So this should answer your question, and also explain why
> the answer isn't evident from what is said in the C90 or
> C99 standards.

(So the wording in C90 and C99 was tricky enough to require a
clarification by the committee in an answer to a defect report...)

Thank you for your expertize and your in-depth answer :o)

--
regis


Keith Thompson

unread,
Nov 15, 2013, 11:42:08 AM11/15/13
to
Malcolm McLean <malcolm...@btinternet.com> writes:
> On Friday, November 15, 2013 9:00:36 AM UTC, Philip Lantz wrote:
>> glen herrmannsfeldt wrote:
>>
>> > In actual use, the most likely result is that the two random numbers
>> > come out in unspecified order. Still, the general rule on undefined
>> > behavior is that anything can happen.
>>
>> Why do you think the behavior is undefined?
>>
> The C standard doesn't attempt to define what happens when two threads
> contest for a resource.
[...]

Threads? What threads?

--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

Keith Thompson

unread,
Nov 15, 2013, 11:45:35 AM11/15/13
to
Why?

That's a preliminary draft of a C standard that's a couple of decades
out of date.

Fuseblower

unread,
Nov 15, 2013, 12:50:35 PM11/15/13
to
On Fri, 15 Nov 2013 08:45:35 -0800, Keith Thompson <ks...@mib.org>
wrote:

>That's a preliminary draft of a C standard that's a couple of decades
>out of date.

OT : Hehe, over a decade ago I bought the hardcopy of The Standard
from the Ansi organization itself. It cost me over 200 bucks but I
figured it would make a fine addition to all those Microsoft tomes
that spell "authorative and definitive" by their sheer size and
hardcovers.

This is what I got :

http://www.paulmesken.net/images/TheStandard.jpg

I swear it's the truth, no matter how unlikely it seems...

It was bloody photo copied on a fax machine and the pages were bound
together by a mere piece of tape (probably put there by an intern or
something).

You can't hit someone over the head with something like that!

Still, it does fit the rest of the development environment nicely :)

Keith Thompson

unread,
Nov 15, 2013, 1:52:17 PM11/15/13
to
Fuseblower <gnomefir...@xs4all.nl> writes:
> On Fri, 15 Nov 2013 08:45:35 -0800, Keith Thompson <ks...@mib.org>
> wrote:
>>That's a preliminary draft of a C standard that's a couple of decades
>>out of date.
>
> OT : Hehe, over a decade ago I bought the hardcopy of The Standard
> from the Ansi organization itself. It cost me over 200 bucks but I
> figured it would make a fine addition to all those Microsoft tomes
> that spell "authorative and definitive" by their sheer size and
> hardcovers.
>
> This is what I got :
>
> http://www.paulmesken.net/images/TheStandard.jpg
>
> I swear it's the truth, no matter how unlikely it seems...

It doesn't seem unlikely at all.

I bought a PDF copy of the 1990 ISO C standard (which is equivalent to
the 1989 ANSI standard, with the addition of some front matter resulting
in the renumbering of the sections). It cost me $18 from ANSI. I don't
know whether you can buy from ANSI from outside the US; if not, check
your national standards body.

Before that, I bought a copy of Schildt's "The Annotated ANSI C
Standard" (which was actually an annotation of the 1990 ISO C standard).
It was much cheaper at the time than an copy of the standard itself.
The price difference reflects the value of the annotations.
<http://www.davros.org/c/schildt.html>

Typically each new standard comes out in a grossly overpriced version,
followed a few months later by something more reasonable; I think it's
up to $30 now.

My copy of the C90 standard appears to have been scanned from a paper
copy. The C99 and C11 standard are purely digital, which makes them
easier to search and to copy-and-paste.

N1256 is the best freely available draft of the C99 standard; it
includes the official standard itself with the three Technical
Corrigenda (offical updates) merged into it.

N1570 is a version of the C11 standard from just before it was
officially published; there are a few minor differences.

Unless you really need an *official* standard (you probably don't),
N1256 and N1570 are probably more than good enough.

Stephen Sprunk

unread,
Nov 15, 2013, 4:49:21 PM11/15/13
to
On 14-Nov-13 23:17, regis wrote:
> Assume 666 and 777 are the next two integers returned by rand(),
> and consider the call: Pair rand_pair= MakePair (rand(), rand());
>
> rand() probably does some side-effects on some global variables
> to prepare for its next call, which leads me to wonder what to
> think about the behavior or the call:
>
> 1) side-effects of both rand() calls may interleave, behavior is
> undefined and program may break ?

There are sequence points before and after each call to rand(),
regardless of which order they're called in, so there is no danger of
them interleaving.

> 2) rand_pair is either (Pair){ 666, 777 } or (Pair){ 777, 666 }
> at the discretion of the implementation, i.e., the order of evaluation
> of both rand() calls is unspecified, but we end with a random pair,
> and all run smoothly in all rand() calls ?

Correct. It is unspecified which happens, but those are the only two
possibilities. If you care about the order, you need to introduce your
own sequence point between the calls to rand():

int r1 = rand();
int r2 = rand();
Pair p = MakePair(r1, r2);

Note that both right-to-left and left-to-right evaluation can be
observed on common implementations, for various reasons. Some
implementations even vary depending on the function called!

S

--
Stephen Sprunk "God does not play dice." --Albert Einstein
CCIE #3723 "God is an inveterate gambler, and He throws the
K5SSS dice at every possible opportunity." --Stephen Hawking

Tim Rentsch

unread,
Nov 16, 2013, 8:12:06 PM11/16/13
to
This looks like it has the same content as the C89 draft
that I sometimes use, which is at

http://flash-gordon.me.uk/ansi.c.txt

The link you posted looks like a good resource, having
in-document links in a .html setting. I do still recommend
getting the .pdf's for the later drafts. Also the ansi.c.txt
version of C89 is good if one wants to do searching.

Tim Rentsch

unread,
Nov 16, 2013, 8:28:08 PM11/16/13
to
Stephen Sprunk <ste...@sprunk.org> writes:

> On 14-Nov-13 23:17, regis wrote:
>> Assume 666 and 777 are the next two integers returned by rand(),
>> and consider the call: Pair rand_pair= MakePair (rand(), rand());
>>
>> rand() probably does some side-effects on some global variables
>> to prepare for its next call, which leads me to wonder what to
>> think about the behavior or the call:
>>
>> 1) side-effects of both rand() calls may interleave, behavior is
>> undefined and program may break ?
>
> There are sequence points before and after each call to rand(),
> regardless of which order they're called in, so there is no danger of
> them interleaving.

Having a sequence point before and after each common access does
not prevent undefined behavior if there is no other ordering
between those sequence points. For example

int x;
...

(10, x = 1, 30) + (20, x = 2, 40)

is still undefined behavior, despite there being a sequence point
both before and after each access to x.

What matters here is not the sequence points but the accesses
taking place inside a called function body. Evaluation of
function bodies does not overlap with the evalution of other
expressions outside the called function (including expressions
in other called functions). So this

int x;

void set_x( int new_x; ){ x = new_x; }

...

(10, set_x( 1 ), 30) + (20, set_x( 2 ), 40)

is defined (unspecified) behavior rather than undefined behavior,
despite the accesses to x being the same as in the assignment
example as far as sequence points go. This rule is spelled out
more precisely in C11, with evaluation of function bodies being
'indeterminately sequenced' with respect to all other expressions
in the calling function. The same "non-overlap" rule for function
calls holds in C90 and C99, as detailed in the Defect Reports
mentioned elsethread.

Stephen Sprunk

unread,
Nov 16, 2013, 9:21:37 PM11/16/13
to
On 16-Nov-13 19:28, Tim Rentsch wrote:
> Stephen Sprunk <ste...@sprunk.org> writes:
>> There are sequence points before and after each call to rand(),
>> regardless of which order they're called in, so there is no danger of
>> them interleaving.
>
> Having a sequence point before and after each common access does
> not prevent undefined behavior if there is no other ordering
> between those sequence points. For example
>
> int x;
> ...
>
> (10, x = 1, 30) + (20, x = 2, 40)
>
> is still undefined behavior, despite there being a sequence point
> both before and after each access to x.

I trust that you are correct, but I don't understand why. I thought
those sequence points would establish that x=1 and x=2 were ordered,
even if that order is unspecified.

> What matters here is not the sequence points but the accesses
> taking place inside a called function body. Evaluation of
> function bodies does not overlap with the evalution of other
> expressions outside the called function (including expressions
> in other called functions).
>
> So this
>
> int x;
>
> void set_x( int new_x; ){ x = new_x; }
>
> ...
>
> (10, set_x( 1 ), 30) + (20, set_x( 2 ), 40)
>
> is defined (unspecified) behavior rather than undefined behavior,
> despite the accesses to x being the same as in the assignment
> example as far as sequence points go.

That seems strange to me. I thought the sequence points before and
after the function calls here:

set_x(1) + set_x(2)

would be enough to establish that one call must finish before the other
could start, even if it's unspecified which order they happen in. To
interpret it that way doesn't require extra rules about non-overlap of
function bodies.

Richard Damon

unread,
Nov 16, 2013, 10:37:38 PM11/16/13
to
On 11/16/13, 9:21 PM, Stephen Sprunk wrote:
> On 16-Nov-13 19:28, Tim Rentsch wrote:
>> Stephen Sprunk <ste...@sprunk.org> writes:
>>> There are sequence points before and after each call to rand(),
>>> regardless of which order they're called in, so there is no danger of
>>> them interleaving.
>>
>> Having a sequence point before and after each common access does
>> not prevent undefined behavior if there is no other ordering
>> between those sequence points. For example
>>
>> int x;
>> ...
>>
>> (10, x = 1, 30) + (20, x = 2, 40)
>>
>> is still undefined behavior, despite there being a sequence point
>> both before and after each access to x.
>
> I trust that you are correct, but I don't understand why. I thought
> those sequence points would establish that x=1 and x=2 were ordered,
> even if that order is unspecified.

The key is to notice that the sequence points establish that
10 is before x = 1 is before 30 and that
20 is before x = 2 is before 40
but that there is NO sequence points between the first set and the
second, and no rule to prevent interleaving, so on possible execution
sequence is:

10, 20; x=1, x=2; 30, 40
where here , separates items without a sequence point, and the ; mark
sequence points. Thus the x=1 and the x=2 are not separated by a
sequence point.
>
>> What matters here is not the sequence points but the accesses
>> taking place inside a called function body. Evaluation of
>> function bodies does not overlap with the evalution of other
>> expressions outside the called function (including expressions
>> in other called functions).
>>
>> So this
>>
>> int x;
>>
>> void set_x( int new_x; ){ x = new_x; }
>>
>> ...
>>
>> (10, set_x( 1 ), 30) + (20, set_x( 2 ), 40)
>>
>> is defined (unspecified) behavior rather than undefined behavior,
>> despite the accesses to x being the same as in the assignment
>> example as far as sequence points go.
>
> That seems strange to me. I thought the sequence points before and
> after the function calls here:
>
> set_x(1) + set_x(2)
>
> would be enough to establish that one call must finish before the other
> could start, even if it's unspecified which order they happen in. To
> interpret it that way doesn't require extra rules about non-overlap of
> function bodies.
>
> S
>

Again, the points could occur in the order:
Sequence point before calling set_x(1)
Sequence point before calling set_x(2)
calling set_x(1) and set_x(2)
Sequence point after calling set_x(1)
Sequence point after calling set_x(2)

You need some further specification to make sure that the functions
don't overlap in execution.

Tim Rentsch

unread,
Nov 17, 2013, 5:07:47 AM11/17/13
to
Stephen Sprunk <ste...@sprunk.org> writes:

> On 16-Nov-13 19:28, Tim Rentsch wrote:
>> Stephen Sprunk <ste...@sprunk.org> writes:
>>> There are sequence points before and after each call to rand(),
>>> regardless of which order they're called in, so there is no danger of
>>> them interleaving.
>>
>> Having a sequence point before and after each common access does
>> not prevent undefined behavior if there is no other ordering
>> between those sequence points. For example
>>
>> int x;
>> ...
>>
>> (10, x = 1, 30) + (20, x = 2, 40)
>>
>> is still undefined behavior, despite there being a sequence point
>> both before and after each access to x.
>
> I trust that you are correct, but I don't understand why. I thought
> those sequence points would establish that x=1 and x=2 were ordered,
> even if that order is unspecified.

The rules for evaluation sequencing in C90 suffer from an
unfortunate choice of phrasing, which turned out to be misleading
in some cases. This confusion was noted fairly soon after C90 was
published, the Defect Report asking about it being submitted in
1993. Around the time C99 was being done, several attempts were
made to define a formal model for sequencing rules, to remove
ambiguities and define the rules more precisely. However these
efforts did not result in any changes in C99. Finally when C11
was being done a suitable formal model was arrived at, and these
changes were incorporated into C11. It is worth reading what C11
says about sequencing relationships, expecially if one is familar
with how those rules are expressed in C90/C99 - even though it
seems like the new rules are different, reportedly the semantics
of the C11 rules is what was intended all along for C90 and C99.

To look at the particular case, here is a diagrammatic view. The
"at sign" in '@x' means lvalue as opposed to rvalue. Statement
boundaries are semicolons.

-- @x --
/ \
- 10 --> , -- = --> , -- 30 --
/ \ / \
/ --- 1 -- \
--> ; -- + --> ; --
\ -- @x -- /
\ / \ /
- 20 --> , -- = --> , -- 40 --
\ /
--- 2 --

Here the lines show ordering for value computations, and the
arrows -> show sequence points, which also order storing of values
for operations going backwards from the ->. The undefined
behavior for this expression can be seen by the absence of a line
connecting the two assignment operators, both of which modify x.
In fact, since both are modifications, not only would there need
to be a line between the two assignments, but there would need to
be an arrow -> between them on that line, to prevent destructive
interference (ie, undefined behavior). But there is no such line.
A sequence point imposes an ordering between two modifications
only when one modification is somewhere on the "forward line" and
the other is somewhere on the "backward line". (The rule for one
modification and one read access is a little more complicated,
involving operators as well as sequence points.)

Unfortunately the Standard expresses these rules by talking about
an interval "Between the previous and next sequence point". This
sounds like such intervals are uniquely determined (which they
aren't), or perhaps like there is an unspecified total ordering of
sequence points (which there isn't). What's intended is a partial
ordering determined by the operators in an expression (and in fact
two partial ordering relationships, one for 'value computations'
that involve only reading, and another for operations that store
into objects). How C90 and C99 express this is misleading at
times; it's more reliable (meaning more consistent with the C11
wording) to ask the question in terms of what the diagram looks
like.


>> What matters here is not the sequence points but the accesses
>> taking place inside a called function body. Evaluation of
>> function bodies does not overlap with the evalution of other
>> expressions outside the called function (including expressions
>> in other called functions).
>>
>> So this
>>
>> int x;
>>
>> void set_x( int new_x; ){ x = new_x; }
>>
>> ...
>>
>> (10, set_x( 1 ), 30) + (20, set_x( 2 ), 40)
>>
>> is defined (unspecified) behavior rather than undefined behavior,
>> despite the accesses to x being the same as in the assignment
>> example as far as sequence points go. [ADDED: probably I should
>> have mentioned that 'set_x(1) + set_x(2)' is also defined
>> (unspecifed) behavior and not undefined behavior.]
>
> That seems strange to me. I thought the sequence points before and
> after the function calls here:
>
> set_x(1) + set_x(2)
>
> would be enough to establish that one call must finish before the other
> could start, even if it's unspecified which order they happen in. To
> interpret it that way doesn't require extra rules about non-overlap of
> function bodies.

The diagram for 'set_x(1) + set_x(2)' looks like this (simplifying
a bit):

--> [set_x(1)] -- x = 1 --> ; --> [return] --
/ \
--> ; -- + --> ; --
\ /
--> [set_x(2)] -- x = 2 --> ; --> [return] --

There are plenty of sequence points, but still no ordering line
between the two assignments. If all we have is the same rule for
sequence points that is used within expressions, this case is
still undefined behavior. I agree with what Richard Damon says,
that the more stringent rule for how function calls work cannot
be derived from what C90 or C99 says about sequencing (or at
least if it can I don't know how). Obviously it's what people
expect, but it doesn't really follow from the phrasing used in
C90 or C99.

(Unfortunately there is no simple way to explain the rule for
function calls in terms of the diagrams. This difficulty may help
explain why the earlier attempts at defining formal models weren't
used in C99.)

I realize this answer may not be completely satisfactory, because it
doesn't directly respond to your intuition about how sequence points
work. The best I can think of to say is that other people have had
reactions much like yours, and it's taken the better part of 20
years to figure out how to say how evaluation sequencing is meant to
work (or not, in the cases where there is undefined behavior). The
good news is that now there is new phrasing in C11, and it is much
better at delineating the different cases unambiguously. So I hope
that either the explanation above or the new C11 description will
help bridge the gap.

christ...@cbau.wanadoo.co.uk

unread,
Nov 17, 2013, 5:50:47 AM11/17/13
to
On Friday, November 15, 2013 6:52:17 PM UTC, Keith Thompson wrote:

> Unless you really need an *official* standard (you probably don't),
> N1256 and N1570 are probably more than good enough.

For 99.99% of all C programmers, the difference between their understanding of the C standard and the contents of N1256 or N1570 is much bigger than the difference between N1256 / N1570 and the actual standard, so the drafts will give them huge benefits at no cost, while the actual standard gives them not much more benefit at possibly high cost.

Of course if you write compilers for a living then I would expect you to have the actual standards.

Malcolm McLean

unread,
Nov 17, 2013, 6:03:53 AM11/17/13
to
On Friday, November 15, 2013 11:22:31 AM UTC, Fuseblower wrote:
> On Fri, 15 Nov 2013 01:29:52 -0800 (PST), Malcolm McLean
>
> Unless the Standard states somewhere that when one uses
> multi-threading, all objects become volatile. In such a case sequence
> points become meaningless and we no longer have an abstract machine.
>
> In that case, every single operation involving operands would invoke
> undefined behaviour.
>
If you implement threads, you've got to suppose that there is some mechanism
which prevents them stepping over each other's memory accesses. If this
isn't provided, then indeed you can't say anything about the behaviour of
the program.
In the case of overlapping rand() calls, there are two main possibilities,
a read of the high byte, then a write to the low byte by the other thread,
followed a read of the written low byte, or the reverse, leading to two
different results. But the reconstituted word could conceivably form a
trap representation, terminating the program with an error message. So it's
exactly the same as undefined behaviour.

88888 Dihedral

unread,
Nov 17, 2013, 1:49:11 PM11/17/13
to
On Friday, November 15, 2013 1:17:14 PM UTC+8, regis wrote:
> Greetings,
>
>
>
> my question relates to function calls with hidden side effects,
>
> that are used as arguments of another function, particularly
>
> in regard to the two following sections found in the draft
>
>
>
> [3.3.2.2 Function calls] "The order of evaluation of the function
>
> designator, the arguments, and subexpressions within the arguments is
>
> unspecified."
>
>
>
> [2.1.2.3 Program execution] "... at sequence points, all side effects of
>
> previous evaluations shall be complete and no side effects of subsequent
>
> evaluations shall have taken place."
>
>
>
>
>
>
>
> For the sake of the exposition, let assume the type Pair,
>
> and a function MakePair() returning a Pair:
>
>
>
> typedef struct { int first, second; } Pair;
>
>
>
> Pair MakePair (int first, int second) {
>
> Pair pair;
>
// THIS IS AN VAR IN THE PROGRAM
// STACK SPACE!!!
> pair.first= first;
>
> pair.second= second;
>
> return pair;
>
BAD STYLE! DO YOU MEAN RETURN IN
VAL FOR NON-BUILTIN TYPES?

> }
>

James Kuyper

unread,
Nov 17, 2013, 2:14:57 PM11/17/13
to
On 11/17/2013 01:49 PM, 88888 Dihedral wrote:
> On Friday, November 15, 2013 1:17:14 PM UTC+8, regis wrote:
...
>> typedef struct { int first, second; } Pair;
>>
>> Pair MakePair (int first, int second) {
>> Pair pair;
>>
> // THIS IS AN VAR IN THE PROGRAM
> // STACK SPACE!!!

'pair' has automatic storage duration, and what the C standard says
about the lifetime of objects with automatic storage durations makes
allocating them from the stack a popular implementation strategy, though
that is not mandatory.
However, I'm wondering why you're getting so excited about that fact.
Were you under the impression that it violates some constraint, or
renders the behavior undefined for some reason? If so, could you explain
why you think it's a problem?

>> pair.first= first;
>> pair.second= second;
>>
>> return pair;
>>
> BAD STYLE! DO YOU MEAN RETURN IN
> VAL FOR NON-BUILTIN TYPES?
>
>> }

The relevant constraint is: "A function declarator shall not specify a
return type that is a function type or an array type." Pair is neither a
function type nor an array type; and is therefore does not violate that
constraint - I'm not aware of any other relevant constraint - do you
know of any?
Returning values of struct type has been allowed since at least C89,
nearly a quarter-century ago, though I vaguely remember being surprised
at the time when I first heard about it, so it was probably not allowed
in K&R C. It's a fairly commonplace technique nowadays.
--
James Kuyper

regis

unread,
Nov 17, 2013, 2:36:29 PM11/17/13
to
On 11/17/13 19:49, 88888 Dihedral wrote:

>> typedef struct { int first, second; } Pair;

>> Pair MakePair (int first, int second) {
>> Pair pair;
>> pair.first= first;
>> pair.second= second;
>> return pair;
>> }

> BAD STYLE! DO YOU MEAN RETURN IN
> VAL FOR NON-BUILTIN TYPES?

C99 introduced compound literals that seem to encourage this style.

Pair
MakePair (int first, int second)
{
return (Pair) { .first= first, .second= second };
}

88888 Dihedral

unread,
Nov 17, 2013, 3:47:30 PM11/17/13
to
To write C programs in manny platforms
are basic trainings in the
IT industry.

Ian Collins

unread,
Nov 17, 2013, 4:15:42 PM11/17/13
to
88888 Dihedral wrote:
>
> To write C programs in manny platforms
> are basic trainings in the
> IT industry.

What was wrong with the code you criticised?

Will you ever learn how to post?

--
Ian Collins

Seebs

unread,
Nov 17, 2013, 4:19:09 PM11/17/13
to
On 2013-11-17, Malcolm McLean <malcolm...@btinternet.com> wrote:
> If you implement threads, you've got to suppose that there is some mechanism
> which prevents them stepping over each other's memory accesses. If this
> isn't provided, then indeed you can't say anything about the behaviour of
> the program.

In practice, the general answer is "actually, there's no such mechanism,
but you're given the tools to make one if you want to."

> In the case of overlapping rand() calls, there are two main possibilities,
> a read of the high byte, then a write to the low byte by the other thread,
> followed a read of the written low byte, or the reverse, leading to two
> different results. But the reconstituted word could conceivably form a
> trap representation, terminating the program with an error message. So it's
> exactly the same as undefined behaviour.

I think this ignores a large number of possible cases in which internal
structures used by the RNG, but not visible to the user, end up in
inconsistent states. (Although most rand() implementations are pretty
simple.)

-s
--
Copyright 2013, all wrongs reversed. Peter Seebach / usenet...@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
Autism Speaks does not speak for me. http://autisticadvocacy.org/
I am not speaking for my employer, although they do rent some of my opinions.

Seebs

unread,
Nov 17, 2013, 4:24:05 PM11/17/13
to
On 2013-11-17, 88888 Dihedral <dihedr...@gmail.com> wrote:
> To write C programs in manny platforms
> are basic trainings in the
> IT industry.

So what?

There's nothing wrong with returning an automatic variable of struct
type.

88888 Dihedral

unread,
Nov 18, 2013, 8:50:31 AM11/18/13
to
I did lousy writing in English when
I saw lousy source codes in C.

nick.keig...@gmail.com

unread,
Nov 18, 2013, 10:41:31 AM11/18/13
to
On Friday, 15 November 2013 10:37:45 UTC, Tim Rentsch wrote:


> Based on the numbering it looks like you are using an ANSI (ie,
> pre-ISO) document. For reference here are some pointers to
> drafts of later versions of the ISO C standard:
>
> Pre-C99
> http://www.open-std.org/jtc1/sc22/wg14/www/docs/n843.pdf
> http://www.open-std.org/jtc1/sc22/wg14/www/docs/n843.htm
> http://www.open-std.org/jtc1/sc22/wg14/www/docs/n869/n869.pdf.gz
> http://www.open-std.org/jtc1/sc22/wg14/www/docs/n869/n869.txt.gz
>
> Post-C99
> http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf
> http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf
>
> Pre-C11
> http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
>

you don't have a link to a C89 definition/draft (I suppose you'd call it C90) do you?. PDF would be nice...

nick.keig...@gmail.com

unread,
Nov 18, 2013, 10:48:41 AM11/18/13
to
On Monday, 18 November 2013 13:50:31 UTC, 88888 Dihedral wrote:


> I did lousy writing in English when
> I saw lousy source codes in C.

what was wrong with the C?

Keith Thompson

unread,
Nov 18, 2013, 11:27:19 AM11/18/13
to
http://flash-gordon.me.uk/ansi.c.txt is a pre-ANSI draft (as the name
implies, it's plain text). I think it's already been mentioned in this
thread.

James Kuyper

unread,
Nov 18, 2013, 11:35:59 AM11/18/13
to
On 11/18/2013 08:50 AM, 88888 Dihedral wrote:
> On Monday, November 18, 2013 5:15:42 AM UTC+8, Ian Collins wrote:
>> 88888 Dihedral wrote:
>>>
>>> To write C programs in manny platforms
>>> are basic trainings in the
>>> IT industry.

Unless I've missed something (which is entirely possible) the code you
object to is perfectly fine C90 code (the feature that you object to
doesn't disprove that, because its a feature fully supported by C90).
C90 is one of the most widely implemented computer languages in the
world - so targeting C90 meets the goal you specify: to "write C
programs [for many] platforms".

>> What was wrong with the code you criticised?
>>
>> Will you ever learn how to post?
>>
>> --
>> Ian Collins
>
> I did lousy writing in English when
> I saw lousy source codes in C.

I've been having trouble with my news server for a couple of days now,
or I would have responded to your earlier message.

What makes you think this is lousy source code? The feature you seem to
be objecting to has been a part of standard C for as long as there has
been a C standard, and appears to be used correctly, in a reasonably
idiomatic fashion.

Noob

unread,
Nov 18, 2013, 11:42:24 AM11/18/13
to
James Kuyper wrote:

> I've been having trouble with my news server for a couple of days now,

I haven't been able to post via eternal-september for a while.
It returned NNTP error 220. Does it work better now?

Keith Thompson

unread,
Nov 18, 2013, 11:48:14 AM11/18/13
to
I had a problem the other day. If you see this, it's working now.

James Harris

unread,
Nov 18, 2013, 12:03:59 PM11/18/13
to
"Noob" <ro...@127.0.0.1> wrote in message news:l6dg01$1a0$1...@dont-email.me...
ES had a problem. All OK now. Incidentally, if it's the same as I had I
think the error code was 441 (which doesn't really tell us anything). Code
220 is just for indicating the message.

http://tools.ietf.org/html/rfc3977

James


James Kuyper

unread,
Nov 18, 2013, 12:04:07 PM11/18/13
to
I had been using the port 80 server; switching to the port 119 server
was my work-around. If you got it work without having to switch servers,
then my "success" may have simply been a matter of changing something
irrelevant at the same time that the real problem got solved.

Seebs

unread,
Nov 18, 2013, 1:21:28 PM11/18/13
to
On 2013-11-18, 88888 Dihedral <dihedr...@gmail.com> wrote:
> I did lousy writing in English when
> I saw lousy source codes in C.

It's poetry, but you have not yet offered any example of something wrong
with the source in question. You've made a vague assertion about
portability, but there's nothing obviously non-portable about returning
a struct from a function. Returning the *address of* an automatic variable
is bad style; returning the automatic variable itself is fine.

regis

unread,
Nov 18, 2013, 2:10:37 PM11/18/13
to
On 11/18/13 19:21, Seebs wrote:
> On 2013-11-18, 88888 Dihedral <dihedr...@gmail.com> wrote:
>> I did lousy writing in English when
>> I saw lousy source codes in C.
>
> It's poetry, but you have not yet offered any example of something wrong
> with the source in question. You've made a vague assertion about
> portability, but there's nothing obviously non-portable about returning
> a struct from a function. Returning the *address of* an automatic variable
> is bad style; returning the automatic variable itself is fine.


Broken code with undefined behevior is a bit beyond style issues ;o)



Seebs

unread,
Nov 18, 2013, 2:37:57 PM11/18/13
to
I think strictly speaking, it's not undefined behavior to return the
address of an automatic variable, it's just undefined behavior to do
anything at all with that return value, including storing it or comparing
it to anything.

Stephen Sprunk

unread,
Nov 18, 2013, 6:12:06 PM11/18/13
to
On 16-Nov-13 21:37, Richard Damon wrote:
> On 11/16/13, 9:21 PM, Stephen Sprunk wrote:
>> On 16-Nov-13 19:28, Tim Rentsch wrote:
>>> (10, x = 1, 30) + (20, x = 2, 40)
>>>
>>> is still undefined behavior, despite there being a sequence point
>>> both before and after each access to x.
>>
>> I trust that you are correct, but I don't understand why. I thought
>> those sequence points would establish that x=1 and x=2 were ordered,
>> even if that order is unspecified.
>
> The key is to notice that the sequence points establish that
> 10 is before x = 1 is before 30 and that
> 20 is before x = 2 is before 40
> but that there is NO sequence points between the first set and the
> second, and no rule to prevent interleaving, so on possible execution
> sequence is:
>
> 10, 20; x=1, x=2; 30, 40
> where here , separates items without a sequence point, and the ; mark
> sequence points. Thus the x=1 and the x=2 are not separated by a
> sequence point.

Ah. I had assumed that the unspecified ordering between (10,x=1,30) and
(20,x=2,40) meant that one had to be evaluated and then the other, i.e.
they couldn't be interleaved like you show, even if we don't know which
of the two will be evaluated first.

>> That seems strange to me. I thought the sequence points before and
>> after the function calls here:
>>
>> set_x(1) + set_x(2)
>>
>> would be enough to establish that one call must finish before the
>> other could start, even if it's unspecified which order they happen
>> in. To interpret it that way doesn't require extra rules about
>> non-overlap of function bodies.
>
> Again, the points could occur in the order:
> Sequence point before calling set_x(1)
> Sequence point before calling set_x(2)
> calling set_x(1) and set_x(2)
> Sequence point after calling set_x(1)
> Sequence point after calling set_x(2)
>
> You need some further specification to make sure that the functions
> don't overlap in execution.

Similarly, I had assumed that evaluating either function, including its
sequence points, was indivisible. In fact, I thought that was the
entire purpose of specifying there were sequence points before/after a
function call!

(All of the above being subject to the as-if rule, of course.)

Stephen Sprunk

unread,
Nov 18, 2013, 6:12:09 PM11/18/13
to
On 17-Nov-13 04:07, Tim Rentsch wrote:
> Stephen Sprunk <ste...@sprunk.org> writes:
>> On 16-Nov-13 19:28, Tim Rentsch wrote:
>>> Having a sequence point before and after each common access does
>>> not prevent undefined behavior if there is no other ordering
>>> between those sequence points. For example
>>>
>>> (10, x = 1, 30) + (20, x = 2, 40)
>>>
>>> is still undefined behavior, despite there being a sequence point
>>> both before and after each access to x.
>>
>> I trust that you are correct, but I don't understand why. I thought
>> those sequence points would establish that x=1 and x=2 were ordered,
>> even if that order is unspecified.
>
> [ very helpful explanation ]
>
> Unfortunately the Standard expresses these rules by talking about
> an interval "Between the previous and next sequence point". This
> sounds like such intervals are uniquely determined (which they
> aren't), or perhaps like there is an unspecified total ordering of
> sequence points (which there isn't). What's intended is a partial
> ordering determined by the operators in an expression (and in fact
> two partial ordering relationships, one for 'value computations'
> that involve only reading, and another for operations that store
> into objects). How C90 and C99 express this is misleading at
> times; it's more reliable (meaning more consistent with the C11
> wording) to ask the question in terms of what the diagram looks
> like.

I think this is where I got turned around. I thought there _was_ a
total ordering, at least to the extent that anything separated by
sequence points happens in the specified order and anything between
sequence points happens in an unspecified order. (All subject to the
as-if rule, of course.)

Following that (apparently flawed) logic, I concluded that since there
was a sequence point before and after a function call, nothing outside
the function call could overlap with it because that would violate the
function's own internal ordering, even if the ordering of its parent
expression was unspecified.

> I realize this answer may not be completely satisfactory, because it
> doesn't directly respond to your intuition about how sequence points
> work. The best I can think of to say is that other people have had
> reactions much like yours, and it's taken the better part of 20
> years to figure out how to say how evaluation sequencing is meant to
> work (or not, in the cases where there is undefined behavior).

Well, at least I'm not alone in my confusion.

Could it be said that it was always _intended_ to work how I thought it
did, but the Standard didn't actually say so until C11?

Stephen Sprunk

unread,
Nov 18, 2013, 6:58:38 PM11/18/13
to
I always use the port 119 server; it seems to have been fixed sometime
in the last ~16 hours.

Stephen Sprunk

unread,
Nov 18, 2013, 11:53:50 PM11/18/13
to
In theory, NNTP response code 441 is supposed to be followed by a
human-readable explanation of what went wrong. However, ES's servers
respond with "441 220" when they're not accepting posts, which some
newsreaders then present as "NNTP error: 220". It has nothing to do
with NNTP response code 220.

Rosario1903

unread,
Nov 19, 2013, 3:35:09 AM11/19/13
to
On Sun, 17 Nov 2013 10:49:11 -0800 (PST), 88888 Dihedral
<dihedr...@gmail.com> wrote:

>On Friday, November 15, 2013 1:17:14 PM UTC+8, regis wrote:
>> Greetings,
>>
>>

>> Pair MakePair (int first, int second) {
>>
>> Pair pair;
>>
>// THIS IS AN VAR IN THE PROGRAM
>// STACK SPACE!!!
>> pair.first= first;
>>
>> pair.second= second;
>>
>> return pair;
>>
>BAD STYLE! DO YOU MEAN RETURN IN
>VAL FOR NON-BUILTIN TYPES?
>
>> }

i remember as you, but if one do some try
with code one think can be ok....

--------------------------
#include <stdio.h>

typedef struct { unsigned first, second; } Pair;

Pair MakePair(unsigned first, unsigned second)
{Pair pair;

pair.first = first;
pair.second= second;
return pair;
}

void printPair(Pair* a)
{if(a==0) {printf("Pair=null\n"); return ;}
printf("Pair=%x, %x\n", a->first, a->second);
}

int main(void)
{Pair vv;
vv=MakePair(0xFFFFFFFB, 0xFFFFFFFA);
printPair(&vv);
return 0;
}

--------------------------

_TEXT segment dword public use32 'CODE'
_MakePair proc near
;
; Pair MakePair(unsigned first, unsigned second)
;
push ebp
mov ebp,esp
add esp,-8
mov eax,dword ptr [ebp+8]
;
; {Pair pair;
;
; pair.first = first;
;
?live1@16: ; EAX = return
@1:
mov edx,dword ptr [ebp+12]
mov dword ptr [ebp-8],edx
;
; pair.second= second;
;
mov ecx,dword ptr [ebp+16]
mov dword ptr [ebp-4],ecx
;
; return pair;

; this write in the mem pointed from eax [&vv] the result

mov edx,dword ptr [ebp-8]
mov dword ptr [eax],edx
mov edx,dword ptr [ebp-4]
mov dword ptr [eax+4],edx
;
; }
@3:
@2:
pop ecx
pop ecx
pop ebp
ret
_MakePair endp
----------------------------

;
; int main(void)
;
push ebp
mov ebp,esp
add esp,-8

; {Pair vv;
; vv=MakePair(0xFFFFFFFB, 0xFFFFFFFA);
@9:
push -6
push -5
lea eax,dword ptr [ebp-8]
; here eax point to the space of mem of vv
push eax

; this call _MakePair with 3 arg
; 1: is the address that can contain the result [&vv]
; 2: is the argument 0xFFFFFFFB
; 3: is the arguemtn 0xFFFFFFFA
call _MakePair
add esp,12
; so this free the 3 dword stack [4*3=12]

Tim Rentsch

unread,
Nov 19, 2013, 11:07:52 AM11/19/13
to
I don't always distinguish, but when I do usually I use C89 to
mean the ANSI version and C90 to mean the ISO version. In any
case here are the links I have for (what I believe is) the
pre-ANSI draft, one a text document, the other html --

http://flash-gordon.me.uk/ansi.c.txt
http://web.archive.org/web/20050207005628/http://dev.unicals.com/papers/c89-draft.html

Note that these use different section numbering than the ISO
C90 document. Other than that I believe they are essentially
identical.

> PDF would be nice...

Yes, wouldn't it? :)

Tim Rentsch

unread,
Nov 19, 2013, 11:43:02 AM11/19/13
to
Stephen Sprunk <ste...@sprunk.org> writes:

> On 17-Nov-13 04:07, Tim Rentsch wrote:
>> Stephen Sprunk <ste...@sprunk.org> writes:
>>> On 16-Nov-13 19:28, Tim Rentsch wrote:
>.
>. [semantics of function call evaluation sequencing]
>.
>> I realize this answer may not be completely satisfactory, because
>> it doesn't directly respond to your intuition about how sequence
>> points work. The best I can think of to say is that other people
>> have had reactions much like yours, and it's taken the better part
>> of 20 years to figure out how to say how evaluation sequencing is
>> meant to work (or not, in the cases where there is undefined
>> behavior).
>
> Well, at least I'm not alone in my confusion.
>
> Could it be said that it was always _intended_ to work how I thought
> it did, but the Standard didn't actually say so until C11?

Assuming you're asking about function calls being "indivisible", I
believe the answer to the first part is Yes, that is always how the
standardization group(s) expected they would work, although I'm not
sure how conscious that expectation was.

The second part of the question, about when the Standard actually
said that, is harder to answer. As far as the committee is
concerned, the response to Defect Report 87 may constitute an
explicit statement that resolves the question. I think responses
to Defect Reports have some sort of official status but I'm not
sure just what. Furthermore, even in C90 there is the statement
that "Calling a function suspends but does not end execution of the
block containing the call." This statement could be taken to mean
that the sequence point before the function call acts as a sequence
point for all evaluations that have been started in the calling
function up to that point, in which case the call to some other
function (or any other expression for that matter) could not be "in
flight" while the called function body was being evaluated. This
reading of that sentence comes close to, or may even match exactly,
the behavior described more precisely in C11. So the people who
wrote the C90 standard may consider that document to have already
put forth the "indivisibleness" of function calls, just in a rather
indirect form.

One further note of possible historical interest - it wasn't until
C99 that the Standard specified that there is a sequence point
before the return of a library function (as opposed to a function
defined in regular source). I found it interesting to look through
Annex C (which summarizes sequence points) in each of C90, C99, and
C11, and see how the list has evolved over time.

regis

unread,
Nov 19, 2013, 12:22:45 PM11/19/13
to
by the way, starting from C99,
<stdlib.h> has some functions returning a structure, such as:
div_t div (int numer, int denom);
which has a behavior semantically equivalent to:

div_t
div (int numer, int denom)
{
return (div_t) { .quot= numer / denom, .rem= numer % denom };
}

Eric Sosman

unread,
Nov 19, 2013, 12:53:45 PM11/19/13
to
On 11/19/2013 12:22 PM, regis wrote:
>[...]
>
> by the way, starting from C99,
> <stdlib.h> has some functions returning a structure, such as:
> div_t div (int numer, int denom);

div() and ldiv() (and div_t and ldiv_t) were not C99
innovations; they are in the original ANSI Standard of 1989.

--
Eric Sosman
eso...@comcast-dot-net.invalid

Tim Rentsch

unread,
Nov 19, 2013, 1:06:31 PM11/19/13
to
Malcolm McLean <malcolm...@btinternet.com> writes:

> On Friday, November 15, 2013 11:22:31 AM UTC, Fuseblower wrote:
>> On Fri, 15 Nov 2013 01:29:52 -0800 (PST), Malcolm McLean
>>
>> Unless the Standard states somewhere that when one uses
>> multi-threading, all objects become volatile. In such a case
>> sequence points become meaningless and we no longer have an
>> abstract machine.
>>
>> In that case, every single operation involving operands would
>> invoke undefined behaviour.
>
> If you implement threads, you've got to suppose that there is some
> mechanism which prevents them stepping over each other's memory
> accesses. If this isn't provided, then indeed you can't say
> anything about the behaviour of the program. [snip example] So
> it's exactly the same as undefined behaviour.

Apparently you don't understand what question is being asked, or
what the term 'undefined behavior' means, or both.

The question under consideration (lost to unmarked snipping) is
about a code fragment like

foo( rand(), rand() )

and is asking what ISO C specifies for the behavior of such
an expression when run under a conforming implementation (the
only arena where the ISO C standard says anything at all).

The answer to this is that such expressions are specified to have
'unspecified behavior' (assuming foo() is declared appropriately,
etc), not 'undefined behavior'. This answer is not affected by
the presence or absence of threads, either in the implementation
or in the execution environment.

The term 'undefined behavior' refers to a specification. It does
not refer to actual behavior. The actual behavior of any program
execution is never 'undefined behavior', 'unspecified behavior",
'implementation-defined behavior', or 'defined behavior'; rather
it is some particular behavior, which might or might not meet the
specified requirements in each particular case.

If you mean to address the question originally posed, the
statement you make is wrong, because what is specified in such
cases is unspecified behavior, not undefined behavior.

If you mean to address a question outside the realm of what
the ISO C standard specifies, your statement is nonsensical,
because the term 'undefined behavior' has meaning only in the
context of what is specified by ISO C. It also would have no
bearing on the original question.

regis

unread,
Nov 19, 2013, 1:11:38 PM11/19/13
to
On 11/19/13 18:53, Eric Sosman wrote:
> On 11/19/2013 12:22 PM, regis wrote:
>> [...]
>>
>> by the way, starting from C99,
>> <stdlib.h> has some functions returning a structure, such as:
>> div_t div (int numer, int denom);
>
> div() and ldiv() (and div_t and ldiv_t) were not C99
> innovations; they are in the original ANSI Standard of 1989.

even better ;o)


Fuseblower

unread,
Nov 19, 2013, 1:37:36 PM11/19/13
to
On Tue, 19 Nov 2013 12:53:45 -0500, Eric Sosman
<eso...@comcast-dot-net.invalid> wrote:

>On 11/19/2013 12:22 PM, regis wrote:
>>[...]
>>
>> by the way, starting from C99,
>> <stdlib.h> has some functions returning a structure, such as:
>> div_t div (int numer, int denom);
>
> div() and ldiv() (and div_t and ldiv_t) were not C99
>innovations; they are in the original ANSI Standard of 1989.

And excellent functions they are (7.10.6.2 and 7.10.6.4 from C90,
BTW).

Any half decent compiler will replace the function call by the single
machine instruction that calculates both the quotient and remainder
(if the target machine has such an instruction, the x86 does : IDIV).

So, using div() might actually result in faster code than using
something like :

a = c / d;
b = c % d;

Now, all we need are new functions that use the carry flag and we're
all set to go ;)

glen herrmannsfeldt

unread,
Nov 19, 2013, 3:24:10 PM11/19/13
to
Tim Rentsch <t...@alumni.caltech.edu> wrote:
> Malcolm McLean <malcolm...@btinternet.com> writes:

(snip)

> Apparently you don't understand what question is being asked, or
> what the term 'undefined behavior' means, or both.

> The question under consideration (lost to unmarked snipping) is
> about a code fragment like

> foo( rand(), rand() )

> and is asking what ISO C specifies for the behavior of such
> an expression when run under a conforming implementation (the
> only arena where the ISO C standard says anything at all).

> The answer to this is that such expressions are specified to have
> 'unspecified behavior' (assuming foo() is declared appropriately,
> etc), not 'undefined behavior'. This answer is not affected by
> the presence or absence of threads, either in the implementation
> or in the execution environment.

Just to be sure, is it 'unspecified behavior' in all ISO C versions?

Not all of us have C11 compilers, and some might still need to compile
on C89/C90 compilers.

> The term 'undefined behavior' refers to a specification. It does
> not refer to actual behavior. The actual behavior of any program
> execution is never 'undefined behavior', 'unspecified behavior",
> 'implementation-defined behavior', or 'defined behavior'; rather
> it is some particular behavior, which might or might not meet the
> specified requirements in each particular case.

-- glen

Tim Rentsch

unread,
Nov 19, 2013, 8:45:47 PM11/19/13
to
glen herrmannsfeldt <g...@ugcs.caltech.edu> writes:

> Tim Rentsch <t...@alumni.caltech.edu> wrote:
>> Malcolm McLean <malcolm...@btinternet.com> writes:
>
> (snip)
>
>> Apparently you don't understand what question is being asked, or
>> what the term 'undefined behavior' means, or both.
>>
>> The question under consideration (lost to unmarked snipping) is
>> about a code fragment like
>>
>> foo( rand(), rand() )
>>
>> and is asking what ISO C specifies for the behavior of such
>> an expression when run under a conforming implementation (the
>> only arena where the ISO C standard says anything at all).
>>
>> The answer to this is that such expressions are specified to have
>> 'unspecified behavior' (assuming foo() is declared appropriately,
>> etc), not 'undefined behavior'. This answer is not affected by
>> the presence or absence of threads, either in the implementation
>> or in the execution environment.
>
> Just to be sure, is it 'unspecified behavior' in all ISO C versions?

Yes.

88888 Dihedral

unread,
Nov 20, 2013, 11:50:45 AM11/20/13
to
>> Pair MakePair (int first, int second) {
>>
>> Pair pair;
>>
>// THIS IS AN VAR IN THE PROGRAM
>// STACK SPACE!!!
>> pair.first= first;
>>
>> pair.second= second;
>>
>> return pair;
>>
>BAD STYLE! DO YOU MEAN RETURN IN
>VAL FOR NON-BUILTIN TYPES?
>
>> }

i remember as you, but if one do some try
with code one think can be ok....

--------------------------
#include <stdio.h>

typedef struct { unsigned first, second; } Pair;

Pair MakePair(unsigned first, unsigned second)
{Pair pair;

pair.first = first;
pair.second= second;
return pair;
}

I'll give my version of this kind
trival settings

#define MAKEPAIR(p, x, y) { p.first=x;p.second=y;}

christ...@cbau.wanadoo.co.uk

unread,
Nov 20, 2013, 12:26:24 PM11/20/13
to
On Monday, November 18, 2013 11:12:06 PM UTC, Stephen Sprunk wrote:

> Similarly, I had assumed that evaluating either function, including its
> sequence points, was indivisible. In fact, I thought that was the
> entire purpose of specifying there were sequence points before/after a
> function call!

It also means that when you call f (i++) the value of i is incremented before the call to f. Which would be relevant if i is a static variable that is accessible to f. So not only is the call itself undivided, but it doesn't divide any operator from its side effects. (Of course evaluating parameters does _not_ involve any sequence points).

88888 Dihedral

unread,
Nov 20, 2013, 12:35:19 PM11/20/13
to
Only idiots can't distingush
the call-by-name macro in CAPTALS
to supply with name arguments
such as i++, --i and etc. to
the right macro in the caller part.

Keith Thompson

unread,
Nov 20, 2013, 2:00:28 PM11/20/13
to
88888 Dihedral <dihedr...@gmail.com> writes:
> On Wednesday, November 20, 2013 12:43:02 AM UTC+8, Tim Rentsch wrote:
>> Stephen Sprunk <ste...@sprunk.org> writes:
[...]
>>> Pair MakePair (int first, int second) {
>>>
>>> Pair pair;
>>>
>>// THIS IS AN VAR IN THE PROGRAM
>>// STACK SPACE!!!
>>> pair.first= first;
>>>
>>> pair.second= second;
>>>
>>> return pair;
>>>
>>BAD STYLE! DO YOU MEAN RETURN IN
>>VAL FOR NON-BUILTIN TYPES?
>>
>>> }

Your quoting is messed up. Tim didn't write the text starting with "BAD
STYLE!", you did.

> i remember as you, but if one do some try
> with code one think can be ok....
>
> --------------------------
> #include <stdio.h>
>
> typedef struct { unsigned first, second; } Pair;
>
> Pair MakePair(unsigned first, unsigned second)
> {Pair pair;
>
> pair.first = first;
> pair.second= second;
> return pair;
> }

Yes, that's essentially the same as what Stephen (or whoever it was)
posted, and it's perfectly valid and safe. You asserted that it's bad
style, but you haven't explained why, after repeated attempts to coax
you to do so.

> I'll give my version of this kind
> trival settings
>
> #define MAKEPAIR(p, x, y) { p.first=x;p.second=y;}

That could work too, though I'd definitely make some changes in the way
the macro is defined:
#define MAKEPAIR(p, x, y) ( (p).first = (x), (p).second = (y) )
to allow it to be used in any context where an expression is permitted.

But how is
MAKEPAIR(p, x, y);
better than
p = MakePair(x, y);
?

Here's my answer: Returning a struct value from a function is
valid and safe, and is in no way poor style for a small struct.
(It makes sense to manipulate larger structs via pointers, to
avoid unnecessary copying.) Returning a struct value may be a
problem if you're using a non-conforming compiler, but *anything*
could be a problem if you're using a non-conforming compiler; in
particular, pre-C89 compilers are nearly irrelevant these days.
You've incorrectly asserted that it's "BAD STYLE" and refused to
admit that you were mistaken.

I invite you to explain what you meant.

88888 Dihedral

unread,
Nov 20, 2013, 2:28:14 PM11/20/13
to
Do you push so often in the C program
stack for such trivial things?

The caller is responsible
to allocate the structure either
in the heap or the program stack
in my version of C programs.

Of course, in C++ or Java
things are different.

Seebs

unread,
Nov 20, 2013, 2:23:29 PM11/20/13
to
On 2013-11-20, 88888 Dihedral <dihedr...@gmail.com> wrote:
> Do you push so often in the C program
> stack for such trivial things?

Sure, why not?

> The caller is responsible
> to allocate the structure either
> in the heap or the program stack
> in my version of C programs.

Why? As you said, it's a trivial thing. Do you also require the
caller to do allocation for long doubles? Maybe just plain old doubles?

You made a comment about "portability", but the more you talk about this,
the less it seems to me like you have a broad experience with different
architectures and how they do or don't handle this.

regis

unread,
Nov 20, 2013, 3:18:33 PM11/20/13
to
On 11/20/13 17:50, 88888 Dihedral wrote:
> #include <stdio.h>
>
> typedef struct { unsigned first, second; } Pair;
>
> Pair MakePair(unsigned first, unsigned second)
> {Pair pair;
>
> pair.first = first;
> pair.second= second;
> return pair;
> }
>
> I'll give my version of this kind
> trival settings
>
> #define MAKEPAIR(p, x, y) { p.first=x;p.second=y;}

It does not allow function composition
since you have to use a named variable.


Keith Thompson

unread,
Nov 20, 2013, 4:19:41 PM11/20/13
to
88888 Dihedral <dihedr...@gmail.com> writes:
> On Thursday, November 21, 2013 3:00:28 AM UTC+8, Keith Thompson wrote:
[...]
>> Here's my answer: Returning a struct value from a function is
>> valid and safe, and is in no way poor style for a small struct.
>> (It makes sense to manipulate larger structs via pointers, to
>> avoid unnecessary copying.) Returning a struct value may be a
>> problem if you're using a non-conforming compiler, but *anything*
>> could be a problem if you're using a non-conforming compiler; in
>> particular, pre-C89 compilers are nearly irrelevant these days.
>> You've incorrectly asserted that it's "BAD STYLE" and refused to
>> admit that you were mistaken.
>>
>> I invite you to explain what you meant.
>
> Do you push so often in the C program stack for such trivial things?

Finally, something that might *vaguely* be responsive to what I
was asking. Are you saying you're concerned about the expense
of pushing values onto the stack to pass them to the function,
and then returning a struct value (presumably also on the stack)?

If you're interested in having an actual discussion, it would have
beeen helpful if you'd stated your rationale when you first made
the remark -- or at least after being asked.

The C standard says nothing about a "stack". It's true that the
vast majority of C implementations do use an in-memory contiguous
stack. But if the expense of the MakePair() function is such a
concern, you can always inline it (making it conditional on the
__STDC_VERSION__ if you're concerned about older implementations)
and ask your compiler to optimize it.

I don't think I'd bother unless actual measurements showed that it
was a significant problem.

And it's very common for parameters and function return values to
be passed in registers. In this case, the structure is just twice
the size of an int.

Do you often resort to macros for such trivial things?

Incidentally, in this particular case C99's compound literals make the
function largely unecessary -- though not all compilers support them.

> The caller is responsible to allocate the structure either in the heap
> or the program stack in my version of C programs.

Yes. So what is the advantage of pushing that responsibility onto
the caller? It makes the code more difficult to read and write,
for vanishingly little benefit.

[...]

88888 Dihedral

unread,
Nov 20, 2013, 4:33:18 PM11/20/13
to
First the actions of the caller that calls a normal C function with variables are different from
the actions of the use of a macro.

The simple and fast way is better.

Because there's no GC in C,
and in manny embedded systems a small C compiler like LCC or PCC could be embedded but not a big one like
GCC for C and C++.









Keith Thompson

unread,
Nov 20, 2013, 5:40:22 PM11/20/13
to
88888 Dihedral <dihedr...@gmail.com> writes:
[...]
> First the actions of the caller that calls a normal C function with
> variables are different from the actions of the use of a macro.

Not necessarily. In this case, the behaviors are identical; the
operations performed by the abstract machine may differ, but we don't
run abstract machines.

> The simple and fast way is better.

And the question is, which way is simpler?

> Because there's no GC in C,
> and in manny embedded systems a small C compiler like LCC or PCC could
> be embedded but not a big one like GCC for C and C++.

Garbage collection is not required in this case.

Here's a concrete example:

#include <stdio.h>

typedef struct {
int first;
int second;
} Pair;

static Pair MakePair(int first, int second) {
Pair result;
result.first = first;
result.second = second;
return result;
}

#define MAKE_PAIR(p, first_, second_) \
( (p).first = (first_), \
(p).second = (second_) )

int main(void) {
Pair p;
#ifdef MACRO
puts("MACRO enabled");
MAKE_PAIR(p, 100, 200);
#else
puts("MACRO disabled");
p = MakePair(100, 200);
#endif
printf("p = {%d, %d}\n", p.first, p.second);
}

When I compile and generate assembly listings with:
gcc -O3 -S c.c
vs.
gcc -DMACRO -O3 -S c.c
the assembly listings are *identical* except for the message
"MACRO enabled" vs. "MACRO disabled".

Write clear code, and let the compiler worry about performance.

Well, that's not entirely true. Choosing a good algorithm in
particular can help performance significantly, and *sometimes*
tweaking low-level code can be helpful (if actual measurements have
indicated a significant problem, and if you're better at it in some
particular case than the compiler is).

But at least in this particular case, with the particular compiler and
options I used, using your macro vs. a function returning a struct made
exactly no difference in performance.

Seebs

unread,
Nov 20, 2013, 8:18:01 PM11/20/13
to
On 2013-11-20, 88888 Dihedral <dihedr...@gmail.com> wrote:
> First the actions of the caller that calls a normal C function with variables are different from
> the actions of the use of a macro.

So what? The function is *correct*. The macro often isn't.

> The simple and fast way is better.

Except that it can often introduce bugs, because it's not really
simpler.

> Because there's no GC in C,
> and in manny embedded systems a small C compiler like LCC or PCC could be embedded but not a big one like
> GCC for C and C++.

If you're targeting a small embedded system, you cross-compile.
0 new messages