[comp.lang.c] Passing variable #of args

daveh@marob.MASA.COM (Dave Hammond) (01/20/89)

Two [semi-related] questions, first:

Given B() which expects 3 arguments passed to it, it's reasonably
obvious what can occur when A() invokes B() and passes less than 3 args.

What potential problems exist if A() passes *more* than 3 args to B()?
It seems to me that this would harm nothing, since the extra stuff
ends up beyond the current stack position.

Second:

I was surprised to see the following in a "GNU-something" source file:

lprintf(fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9,10)
char *fmt;
{
	char buf[BUFSIZ];
	sprintf(buf,fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9,10)
	...
}

Given the availability of varargs, is this style still acceptable and,
more importantly, is it portable?

--
Dave Hammond
...!uunet!masa.com!{marob,dsix2}!daveh

gwyn@smoke.BRL.MIL (Doug Gwyn ) (01/21/89)

In article <470@marob.MASA.COM> daveh@marob.masa.com (Dave Hammond) writes:
>What potential problems exist if A() passes *more* than 3 args to B()?
>It seems to me that this would harm nothing, since the extra stuff
>ends up beyond the current stack position.

Stack?  What stack?  The C language doesn't say anything about a stack.

In fact there are reasonable implementations that
	(a) pass arguments differently for different parameter types
	(b) use different mechanisms for passing different numbers
	    of arguments.

>I was surprised to see the following in a "GNU-something" source file:
>lprintf(fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9,10)
>char *fmt;
>{
>	char buf[BUFSIZ];
>	sprintf(buf,fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9,10)
>}
>Given the availability of varargs, is this style still acceptable and,
>more importantly, is it portable?

That style never WAS acceptable; it relies on implementation-dependent
kludgery and will not work on some implementations.  Before there was
<varargs.h>, this technique was resorted to fairly heavily because it
happened to work (most of the time) on the local implementation and
there was no better recourse.  The proper way to do this is with
<varargs.h> or <stdarg.h> macros, passing the va_list to vsprintf().
Of course, some systems don't have vsprintf(), but the solution there
is to implement vsprintf() (it's just a few lines of code) and add it
to the local set of C library extensions.

henry@utzoo.uucp (Henry Spencer) (01/22/89)

In article <470@marob.MASA.COM> daveh@marob.masa.com (Dave Hammond) writes:
>Given B() which expects 3 arguments passed to it, it's reasonably
>obvious what can occur when A() invokes B() and passes less than 3 args.

Oh really?  In a particular implementation, maybe.  Not in general.
About the only general prediction which can be made (which is probably
what Dave was thinking of) is "trouble".

>What potential problems exist if A() passes *more* than 3 args to B()?
>It seems to me that this would harm nothing, since the extra stuff
>ends up beyond the current stack position.

Stack?  What's that?  And why do you assume that extras end up on the
tail end?  They could just as easily end up on the front.  The only
general statement that can be made is "possible trouble".  You can get
away with it in some implementations, including many of the early C
implementations... but not all.

>lprintf(fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9,10)
>char *fmt;
>{
>	char buf[BUFSIZ];
>	sprintf(buf,fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9,10)
>	...
>}
>
>Given the availability of varargs, is this style still acceptable and,
>more importantly, is it portable?

It has never been portable.  Whether it is acceptable, or has ever been
acceptable, depends on who you ask; the answers at utzoo are "no" and "no".
-- 
Allegedly heard aboard Mir: "A |     Henry Spencer at U of Toronto Zoology
toast to comrade Van Allen!!"  | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

ge@phoibos.UUCP (Ge Weijers) (01/23/89)

In article <470@marob.MASA.COM>, daveh@marob.MASA.COM (Dave Hammond) writes:
> I was surprised to see the following in a "GNU-something" source file:
> 
> lprintf(fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9,10)
> char *fmt;
> {
> 	char buf[BUFSIZ];
> 	sprintf(buf,fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9,10)
> 	...
> }
> 
> Given the availability of varargs, is this style still acceptable and,
> more importantly, is it portable?
> 
> --
> Dave Hammond
> ...!uunet!masa.com!{marob,dsix2}!daveh

This is NOT portable. Some compilers use strange calling conventions which
will cause this example to break. An example:

The Turbo C compiler for the Atari ST (sold in Europe) uses registers to pass
arguments. The calling convention is the following (except for details, I'm
producing this from memory):

The first 3 scalar (int, long, char etc.) variables are passed in registers
D0-D2, and the first 2 pointer variables in A0-A1. The call
f(3,&var) and f(&var,3) will yield the same code. The above example breaks.
It is possible to use the ANSI <stdarg.h> variable argument passing, because
the compiler does not pass arguments in registers that correspond to the ...
in an ANSI prototype (line lprintf(char *format, ...))

You'd be better off using <varargs.h> or <stdarg.h>, then it is at least obvious
where you have to change a program.