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