passing variable arguments

Tim McDaniel mcdaniel at amara.uucp
Sun Jun 10 05:46:16 AEST 1990


In an otherwise excellent article, dankg at tornado.Berkeley.EDU (Dan
KoGai) make a few minor errors:

> On C convention, arguments are pushed to the stack [in] right to
> left order before it jumps to the function.

Not so.  Arguments are often pushed on a stack left to right, or
however the implementation likes.  (Consider an architecture with an
"argument pointer" register, pointing to the leftmost argument.)  Some
implementations put a few arguments in registers for efficiency: they
could be the rightmost few or the leftmost few.  Some calling
conventions pass an argument count, but ANSI C doesn't require it.  A
smart compiler can see when there's no recursion and thus no need for
a stack, and is permitted to put arguments in a static area or
"inline" the function.  In ANSI C, if a function has a variable number
of arguments (like printf), it must be called with a function
prototype in scope ending in ",...)", to tell the compiler this fact.
The compiler may (and sometimes must) choose a different
parameter-passing convention for ",..." functions than for a normal
prototyped or unprototyped function.  Certain schemes may be easier to
implement than others, but it is a serious mistake to depend on a
particular argument-passing scheme, and only the implementor has to
know how it actually works.

For ",..."  functions, the passing method has to be such that the callee
- can find the first variable argument given the last known argument
  (for va_start)
- can get the next variable argument given the current one and the
  next one's expected type (for va_arg)
The callee need not be told, and cannot (portably) find out
- the number of actual arguments
- the type (or even sizeof) of each actual argument
With such loose requirements, I can think of any number of schemes.
As dankg wisely notes, it is free to crash if you make a mistake in
number or types.

To stomp on a common misconception, by the way: section 3.3.2.2 of the
ANSI C standard, page 42, lines 20-21: "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."  Translation: however programs actually pass arguments,
you can't depend on the order of evaluation of side-effects in
expressions in a function call, except that the side-effects take
place before the actual call.

> you have to explicity include the number of arguments somewhere in
> your argument[s] and there's no other way of getting number of
> arguments

The two usual methods are
- an argument count (e.g. in printf, derived from the contents of the
  format string).
- a sentinel: the last argument is (int)0 or (char *)0 or some other
  special value.

> in printf() and scanf(), et al., It's number of '%'s in format

Almost: "%%" means "output a single %", and there's no argument in
this case.  Also, "%*" starts a scanf conversion with suppressed
assignment, and there must not be an argument for it.  (I know, "pick
pick pick".)

> And variable argument is implied by "...".

Actually, by ",...".  You must have a least one non-variable argument:
   extern foo(...);
is not valid in ANSI C.

   > char *mstrcat(int, nsrc, char *dst, char *src1, ...)
Remove first ",".
   > /*... means more args to come */
   > {
   > 	va_list srcs;	/* va_list is typically void ** */
Don't assume anything, typical or not.  A va_list is a va_list, period.
   > 	va_start(srcs, src1);	/* now srcs = &src1 */
   > 	while(nsrc--){	/* repeat nsrc times */
   > 		strcat(dst, va_arg(char *, srcs));
Arguments reversed to va_arg: va_list followed by type.
   > 		/* va_arg(type, valist) returns *(type)valist 
   > 		  then inclements valist */
   > 	}
   > 	va_end(srcs)	/* clears stack or do nothing: compiler dependent */
Must have a ";" at the end.
   > 	return dst;
   > }

General problem: va_arg starts with the first variable argument.  The
example as given skips the last known argument, "src1".  It has to be
special-cased outside the loop.

For this function, I would have used a sentinel rather than a count.
The last argument would have to be "(char *)0" in such a scheme.

> You can get more details from K&R.  The most important thing to
> remember is that in C you have to know how many number of what types
> of arguments are there.  You C compiler will not take care of you.

Exactly!
--
"I'm not a nerd -- I'm 'socially challenged'."

Tim McDaniel
Internet: mcdaniel at adi.com             UUCP: {uunet,sharkey}!puffer!mcdaniel



More information about the Comp.lang.c mailing list