C Quiz

F

flameice9

Does the following program invoke undefined behaviour?

int x;
int f(void) {
x = 1;
return 2;
}
int main(void) {
x = f();
}
 
V

Vincenzo Mercuri

Il 18/06/2010 11:28, flameice9 ha scritto:
Does the following program invoke undefined behaviour?

int x;
int f(void) {
x = 1;
return 2;
}
int main(void) {
x = f();
}

Its behaviour is pretty well-defined:

1) x==0 before calling f
2) x==1 before returning from f
3) x==2 before returning(?) from main
 
F

Francois Grieu

Le 18/06/2010 11:28, flameice9 a écrit :
Does the following program invoke undefined behaviour?

int x;
int f(void) {
x = 1;
return 2;
}
int main(void) {
x = f();
}

No. However, in a hosted environment, the termination status returned to
the host environment is unspecified.
To fix this, before the last } in main() add this line:
return 0;

Francois Grieu
 
F

flameice9


The standard says only that the updating of the stored value of the
left
operand of the = operator is performed between the previous and next
sequence point. What stops an implementation from noticing f always
returns 2 and deciding to perform this update prior to the call to f?
Worse, what stops an implementation from deciding to perform this
update
"at the same time" as the x = 1 expression inside f, thus modifying x
twice between sequence points?
 
E

Ersek, Laszlo

The standard says only that the updating of the stored value of the left
operand of the = operator is performed between the previous and next
sequence point. What stops an implementation from noticing f always
returns 2 and deciding to perform this update prior to the call to f?
Worse, what stops an implementation from deciding to perform this update
"at the same time" as the x = 1 expression inside f, thus modifying x
twice between sequence points?

An implementation must comply with the workings of the abstract machine.

In main(), x can't be assigned to until f() is evaluated (= until f()
returns). When f() returns, the "x = 1" assignment is complete. (There is
a sequence point after that expression in f().) In main(), you need the
value returned by f() to evaluate the assignment operator. At that time,
"x = 1" is already complete.

Somewhat related:

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

lacos
 
A

Alexander Klauer

Francois said:
Le 18/06/2010 11:28, flameice9 a écrit :

No. However, in a hosted environment, the termination status returned to
the host environment is unspecified.
To fix this, before the last } in main() add this line:
return 0;

Francois Grieu

C99 5.1.2.2.3: "... reaching the } that terminates the main function returns
a value of 0."

(I have only the N1256 draft, though)
 
R

Richard Bos

[ I've rearranged it slightly for legibility; no behaviour changes. ]
The standard says only that the updating of the stored value of the left
operand of the = operator is performed between the previous and next
sequence point. What stops an implementation from noticing f always
returns 2 and deciding to perform this update prior to the call to f?

The sequence points. There's one just before the call to f(), and one
just after each statement inside f().

Richard
 
B

Ben Bacarisse

Francois Grieu said:
Le 18/06/2010 11:28, flameice9 a écrit :

No. However, in a hosted environment, the termination status returned to
the host environment is unspecified.

Not in C99. Omitting the return is permitted.
To fix this, before the last } in main() add this line:
return 0;

That's the effect, in C99, of leaving out the return.
 
F

flameice9

An implementation must comply with the workings of the abstract machine.

In main(), x can't be assigned to until f() is evaluated (= until f()
returns).

This makes sense intuitively, but there appears to be nothing
requiring a function call to be evaluated prior to its returned value
being used. Of course, this is possible only for functions for which
the returned value can be deduced prior to the call.
 
V

Vincenzo Mercuri

Il 18/06/2010 12:34, Francois Grieu ha scritto:
Le 18/06/2010 11:28, flameice9 a écrit :

No. However, in a hosted environment, the termination status returned to
the host environment is unspecified.
To fix this, before the last } in main() add this line:
return 0;

Francois Grieu

Actually before C99 the value returned by main would be undefined.
So, althought the program compiles and the variable x has the expected
values, it's undefined how the operating system would handle that
process (because of it's undefined exit status).
 
F

flameice9

The sequence points. There's one just before the call to f(), and one
just after each statement inside f().

Indeed, execution proceeds as follows:

<sequence point A> (implicit at program start)
<sequence point> (just before f is called)
x = 1
<sequence point B> ("x = 1;" completes)
return 2
<sequence point> (the return statement completes)
<sequence point C> ("x = f();" completes)

However, 6.5.16p3 says only that, in "x = f();", the side effect of
updating the value of x shall occur at some time between A and C. I
cannot determine why it must be done after B.
 
F

flameice9

Being able to deduce the return value
without the function call actually being made,
only allows the program to omit the function call
if the program has the same behavior
as it would if the function call were actually made.

Where is it stated or implied in the standard that in a function call
expression, the call occurs prior to the value of the expression being
used?
 
F

flameice9

Where it says that there is a sequence point before the function call.

At this point, where the function is about to be called but has not yet
been called, the fact that the function has not yet been called means
that its return value is not available (in the abstract machine).

Right, there is a sequence point just before the call. But I see
nothing in the standard requiring the sequence point and call to
occur prior to the return value being known and used. I agree that it
does not make intuitive sense for the return value to be known and
used before the call occurs. However, given my example program:

int x;
int f(void) {
x = 1;
return 2;
}
int main(void) {
x = f();
}

consider an implementation that notices both that f always returns
and that f always returns 2. If the implementation decides to
evaluate "x = f();" by first setting x to 2, then calling f (to
perform f's side effect), what part of the standard has been
violated? I agree that this is not how the abstract machine is
intended to behave, but this intent is not expressed in the standard.
 
I

Ian Collins

Right, there is a sequence point just before the call. But I see
nothing in the standard requiring the sequence point and call to
occur prior to the return value being known and used. I agree that it
does not make intuitive sense for the return value to be known and
used before the call occurs. However, given my example program:

int x;
int f(void) {
x = 1;
return 2;
}
int main(void) {
x = f();
}

consider an implementation that notices both that f always returns
and that f always returns 2. If the implementation decides to
evaluate "x = f();" by first setting x to 2, then calling f (to
perform f's side effect), what part of the standard has been
violated? I agree that this is not how the abstract machine is
intended to behave, but this intent is not expressed in the standard.

None, the value of f() still has to be assigned to x.

If the compiler knows f always returns 2, it will also know that f
doesn't have any side effects and it is free to set x to 2 and not call
f. Which is the same as inlining f.
 
F

flameice9

None, the value of f() still has to be assigned to x.

Yes, but this occurs at an unspecified time between the sequence
point just before x = f() and the sequence point just after it (see
the last sentence of C99 6.5.16p3). Nothing requires x to have the
value of f() after x = f(); is complete. It is allowable that the
x = 1 assignment is performed after the x = f() assignment.
 
B

Ben Bacarisse

flameice9 said:
Yes, but this occurs at an unspecified time between the sequence
point just before x = f() and the sequence point just after it (see
the last sentence of C99 6.5.16p3). Nothing requires x to have the
value of f() after x = f(); is complete. It is allowable that the
x = 1 assignment is performed after the x = f() assignment.

How do you arrive at that conclusion?

That is a genuine question and there seem to be two possibilities: (a)
you feel that some aspects of the language are under-specified or (b)
you don't understand the specification as it stands. If it is (a) I
have some sympathy. For example, the standard does not say what
constitutes a function call. It says that there is a sequence point
just before the call, but not what happens next. What happens is, of
course, obvious (control passes to the body of the called function and
its declarations and statements are executed one after the other) but it
would be nice if this were stated. Is this omission all that is
bothering you? If not, what permission do you find for the body of a
function to have its effect (x = 1) after the call?

Let's put the sequence point into the program:

int x<S1>;
int f(void) {
x = 1;<S3>
return 2;<S4>
}
int main(void) {
x = f()<S2>;<S5>
}

At S1 x is zero. Sequence point S2 occurs at some point during the
evaluation of x = f() after the function designator and the arguments
have been evaluated but before the call. x is still 0 at the point. At
S3 x must be 1 -- the effect of the assignment must have happened by
then and won't change at S4. (There are two sequence points here: one
at the end of the return statement and another just prior to the return
but I don't think you can tell that there are two). Finally, at S5
(after the assignment statement in main) x must be 2.

<snip>
 
F

flameice9

How do you arrive at that conclusion?

That is a genuine question and there seem to be two possibilities: (a)
you feel that some aspects of the language are under-specified or (b)
you don't understand the specification as it stands. If it is (a) I
have some sympathy.

It is under-specified.
For example, the standard does not say what
constitutes a function call. It says that there is a sequence point
just before the call, but not what happens next. What happens is, of
course, obvious (control passes to the body of the called function and
its declarations and statements are executed one after the other) but it
would be nice if this were stated. Is this omission all that is
bothering you?

No, this is fine.
If not, what permission do you find for the body of a
function to have its effect (x = 1) after the call?

There is no such permission, as the last sentence of 5.1.2.3p2
requires the effect of x = 1 to occur during the call. This is not
the problem I was trying to illustrate. The problem is rather that
the effect of x = f() may occur before the call.
Let's put the sequence point into the program:

int x<S1>;
int f(void) {
x = 1;<S3>
return 2;<S4>
}
int main(void) {
x = f()<S2>;<S5>
}

At S1 x is zero. Sequence point S2 occurs at some point during the
evaluation of x = f() after the function designator and the arguments
have been evaluated but before the call.

Good so far.
x is still 0 at this point.

Here lies the problem. This is not the case. Before the call, it may
be deduced both that the call will return (i.e. that control will be
yielded back) and that the value of f() is always 2. The effect of
x = f() (i.e. x is assigned 2) may thus occur before the call.

The problem is in the last sentence of 6.5.16p3:

The side effect of updating the stored value of the left operand
shall occur between the previous and the next sequence point.

This says merely that the effect of x = f() occurs at an unspecified
time between S1 and S5, which allows it to occur even before S2. It
is apparent from the wording ("the previous" and "the next" sequence
points) that this was written considering only the case when there
are no sequence points between the two sequence points enclosing the
assignment expression.

This could be remedied by changing the wording to:

Let S denote the sequence point immediately after the assignment
expression. The side effect of updating the stored value of the
left operand shall occur prior to S, but subsequent to the
sequence point immediately prior to S.
 
J

jchl

Francois Grieu said:
Le 18/06/2010 11:28, flameice9 a écrit :

No. However, in a hosted environment, the termination status returned to
the host environment is unspecified.
To fix this, before the last } in main() add this line:
return 0;

Francois Grieu

Wouldn't the compiler optimise this to just

int main(void) {
return 0;
}

since it realises x isn't being used so f() will never be called.
 
T

Tim Rentsch

flameice9 said:
Where is it stated or implied in the standard that in a function call
expression, the call occurs prior to the value of the expression being
used?

Please direct your attention to the following passages (the _'s
indicate emphasis added):

5.1.2.3p3: In the abstract machine, all expressions _are evaluated as
specified by the semantics_.

6.2.4p5: (Entering an enclosed block or calling a function suspends,
but does not end, execution of the current block.)

6.5p1: An expression is a sequence of operators and operands _that
specifies computation of a value_, or ...

6.5.16.1p2: In simple assignment (=), _the value of the right operand_
is converted to the type of the assignment expression and
replaces the value stored in the object designated by the
left operand. [Semantics of simple assignment.]

6.8.6.4p2: A return statement terminates execution of the current
function and returns control to its caller.

6.8.6.4p3: If a return statement with an expression is executed, _the
value of the expression is returned to the caller as the
value of the function call expression_. [Both these last
two under Semantics of The return statement.]

The function call is an expression that specifies the computation of a
value. Where does the value of the function call come from? 6.8.6.4p3
says it comes from executing a 'return' statement with an expression,
in particular the 'return' function in the called function body.
Because the function call's value is produced by executing the 'return'
statement in the function body, the previous assignment 'x = 1;' must
have finished storing into the variable x.

You can imagine other ways that a C program might execute on
a real machine, but the Standard says it must produce the
same result as an evaluation on the abstract machine, as
specified by the stated semantics. The only way to produce
a value for a function call under these rules is to execute
a 'return' statement. The value to be assigned simply doesn't
exist before the 'return' statement is executed.
 

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,755
Messages
2,569,537
Members
45,023
Latest member
websitedesig25

Latest Threads

Top