Function Argument Evaluation

Mark Brader msb at sq.sq.com
Tue Mar 26 05:52:34 AEST 1991


(It doesn't seem possible to respond to this without a long inclusion; sorry.)

John Polstra (jdp at polstra.UUCP):

> >    #include <stdio.h>
> >    int x = 100, y = 200, *p;
> >    main() {
> >	printf("%d %d\n", *(p = &x), *(p = &y));
> >    }
> > Could a conforming compiler translate this in such a way that the output
> > of the program is "200 200"?

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

> > If I understand correctly, it would be valid to evaluate in this order:
> >
> >    "%d %d\n"	/* First argument */
> >    (p = &x)	/* Subexpression within second argument */
> >    (p = &y)	/* Subexpression within third argument */
> >    *p		/* Second argument */
> >    *p		/* Third argument */

Chris Volpe (volpecr at crd.ge.com):

>       ^^^ These are not in fact the second and third arguments of the
> function. Stick an asterisk in front of each of the subexpressions you
> listed above and then you have the second and third arguments. It is not
> the value of p at the time of the call that is being dereferenced here.
> It is the result of the assignment expression that is being dereferenced, 
> and that has nothing to do with any side effects that take place anywhere
> in this example. Nowhere in the example are you "reading" p's value.
> The value of the expression "(p = &x)" is "&x". Therefore,
> "*(p = &x)" is "*(&x)" which is x.

Chris is right about the error in John's analysis.  The value of an
assignment expression is the value of its right-hand operand, converted
to the top of its left-hand operand.

However, the answer to John's question is still "yes", because of the
following clause in 3.3:

#  Between the previous and next sequence point an object shall have
#  its stored value modified at most once by the evaluation of an
#  expression.  Furthermore, the prior value shall be accessed only
#  to determine the value to be stored.

Since it doesn't say what happens if you disobey this, the general rule
from 1.6 kicks in:

#  If a "shall" or "shall not" requirement that appears outside of a
#  constraint is violated, the behavior is undefined.

There is also a clarifying footnote to the 3.3 wording:

#  This paragraph renders undefined statement expressions [sic] such as
#      i = ++i + 1;
#  while allowing
#      i = i + 1;

John's program modifies the value of p twice between sequence points,
which means it has undefined behavior and a conforming implementation
could do *anything* -- generate code that outputs "200 200", refuse to
compile the program, invoke rogue, petition the Toronto City Council
to rename John Street to Mark Street, etc.

Incidentally, if the program does compile in the expected way, it also
returns an undefined termination status, since it neither calls exit() nor
returns with a value from main().  But this is not "undefined behavior".
-- 
Mark Brader, Toronto			"Don't be silly -- send it to Canada"
utzoo!sq!msb, msb at sq.com			     -- British postal worker

Original text in this article is in the public domain.



More information about the Comp.std.c mailing list