[net.unix-wizards] Function Parameter Expansion

cottrell@nbs-vms.arpa (COTTRELL, JAMES) (03/12/86)

/*
> I just spent an hour trying to figure out why my working program
> suddenly stopped working.  Here's the culprit:
> 
> 	    expand(*ptr++,*ptr++,*ptr++,ptr++,ptr++,ptr++);

Welcome to the club! You are certainly not the first!!! Try

expand(ptr[0],ptr[1],ptr[2],ptr[3],ptr[3],ptr[3]); ptr += 3;

Or maybe you mean (the compiler thinks you do):

expand(ptr[3],ptr[2],ptr[1],ptr[0],ptr[0],ptr[0]); ptr += 3;
 
> After careful searching of K&R I found the reason;
> 
> 	 The order of evaluation of arguments is undefined by the
> 	 language; take note that the various compilers differ.
> 							[p. 186]
> 
> The compiler I am using evaluates right to left.  If I were to number
> the above arguments 1-6, when expand gets them they are 6-1.  I find
> this contrary to my intuition.   

Intuition is often wrong. For example, most high level languages
index from one, when zero is much easier to deal with. I won't
even mention the Little Endian/Big Endian schism! (Hey, anyone
out there remember the `Big Indian' Pinball Machine?).

> Can anyone out there give me a good
> reason as to why this is left up to compiler implementation?  And
> if so is it generally true that evaluation of parameters is r-l?

Parameters are usually pushed on the stack right-to-left, so that
the first argument (and all others) is at a constant offset from
the stack (or frame) pointer. Some machines/compilers build
*argument* blocks forward (malloc'ed from a heap I guess) and
therefore do it `backwards' (forwards that is).

Allowing the particular implementation to choose makes everything
go fastest for everyone. Except if you don't know.

BTW, expressions such as a[i] = i++; are undefined as well.
You might get lucky sometimes, but don't press your luck.
 
> 
> 			Sincerely,
> 				  Michael Berman
> 					(tanj@ucscc.BITNET)

	jim		cottrell@nbs
*/
------

rich@rexago1.UUCP (K. Richard Magill) (03/17/86)

In article <1703@brl-smoke.ARPA> tanj@ucbvax.uucp writes:
>I just spent an hour trying to figure out why my working program
>suddenly stopped working.  Here's the culprit:
>
>	    expand(*ptr++,*ptr++,*ptr++,ptr++,ptr++,ptr++);
>

Effectively, all "full C" compilers *PUSH* right to left.  Evaluation order
may be changed by the optimizer or whoever.  Y'see C does not quite define
how parameters are passed to functions.  In order to be able to use functions
of varying numbers of parameters (like fprintf & family) it *implies*
that the caller pushes and pops while the callee only reads.  Actually this
could be done left to right if an additional argument was added that
represented the number of arguments. (or some such)

On one compiler I worked with, you had three options:
	1) caller pushes r to l and pops, callee just reads.
		(easiest to debug, allows varying numbers af arguments)
	2) caller pushes (r to l) callee pops.  In this case callee *must*
		know the number of arguments.  on this compiler that was the
		user's problem.
	3) caller pushes (r to l) *except* for the left most argument which
		was left in a register. callee pops.  Again lint warns but its
		really up to you.

These are listed in order of increasing speed. (& space)  One C
compiler I own offers PL/I style (caller pushes l to r, callee pops).
I *think* lattice uses this.

I should point out that you get very different errors using (1) than from using
(3).  If I call a function with 2 arguments when it is expecting 1, under (1)
the second argument is ignored.  Under (2), IN THIS IMPLEMENTATION, the second
argument became the return address & the real return address is left on the
stack.

Weak argument: When order evaluation is not specified, (like for C
function arguments), then evaluation of each expression *could* be passed
on to other processors or the like.

People actually worry about these things when:

	- The application is time or space CRITICAL. (z80 based controllers)
	- More than one compiler is in use. (linking to a fortran library,
		or between any of the myriad pc "C"'s)
	- Cottrel-esque previously working "C" code is ported to new
		machines/compilers/processors and doesn't initially work.

K. Richard Magill
if its questionable or machine specific, COMMENT IT WELL.

ark@alice.UucP (Andrew Koenig) (03/20/86)

> Effectively, all "full C" compilers *PUSH* right to left.

Nope.  It is indeed true that on a machine whose stack grows
downwards, such as the VAX, it is easiest to push arguments
from right to left.  However, not all machines have stacks
that grow down, and not all compilers push each argument
separately.  It is, for instance, very easy to imagine a compiler
even on the VAX that moves the stack pointer once and then
stuffs the arguments into the newly allocated memory.