varargs question

Chris Torek chris at mimsy.UUCP
Sat Aug 5 08:36:25 AEST 1989


In article <11286 at cit-vax.Caltech.Edu> mazer at bek-owl.caltech.edu
(Jamie Mazer) writes:
>... Our masscomp libs don't have vsprintf() etc, so as a kludge, I
>first tried to cobble something together along the lines of:
>	format(a1, a2, a3, ... , a10)    /* ugly, but it often works ;-) */
>	  char *a1, *a2, *a3, ... , *a10)
>	{ ... sprintf(buf, a1, a2, a3, ... , a10); return(buf) ... }
>
>However, then someone suggested I try the following solution:
>	format(va_alist)
>	  va_dcl
>	{ ... sprintf(buf, va_alist); return(buf); ... }
>
>The question is, is the second method a valid one?

No.

>Is it legit to pass va_alist into another function like that?

No, not in this form.  You may---and should---use something like
this:

	char *
	format(va_alist)
		va_dcl
	{
		va_list l;
		char *fmt;
		char buf[SIZE];

		va_start(l);
		fmt = va_arg(l, char *);
		vsprintf(buf, fmt, l);
		va_end(l);
		return (buf);
	}

>If so, then why are the vprintf() functions necessary at all?

The vprintf function is

	int vprintf(char *fmt, va_list l);

---that is, it is a function of two arguments, the first being
`char *' and the second `va_list' (usually an array type, making
`l' act like a pointer, or a basic pointer type).  The printf function,
on the other hand, is

	int printf(char *fmt, ...);

---that is, a function of 1 or more arguments, the first being `char *'
and the rest being undefined.  It is legal for compilers to use completely
different calling conventions for the two functions.  For instance,
on many machines the `return from subroutine' function can pop a fixed
number of bytes from the call stack.  On such a machine, one might
compile

	vprintf("foo %d bar %o\n", l);
	fn();

as

		.string Lxxx,"foo %d bar %o\n"
		push	#Lxxx		# push address of string
		push	8(frame)	# push value of l
		call	vprintf_	# vprintf()
		call	fn_		# fn()
		.
		.
		.
	vprintf_:
		<code>		# arg 1 is -8(frame), arg 2 is -4(frame)
		ret	2	# pop two arguments

but

	printf("foo %d bar %o\n", x, y);
	fn();

as

		.string	Lxxx,"foo %d bar %o\n"
		push	16(frame)	# push value of y
		push	12(frame)	# push value of x
		push	#Lxxx		# push address of string
		call	printf_		# printf()
		pop	r0		# clear stack
		pop	r0
		call	fn_		# fn()
		.
		.
		.
	printf:
		<code>		# arg 1 is -4(frame), arg2 -8(frame), etc
		ret	1	# pop 1 fixed argument

Note that I switched the *order* of the arguments; this too is legal,
although I have never seen a compiler go this far.  (One would be
more likely to see a compiler pass arguments to fixed-argument functions
in registers, and variable-argument functions on a stack, for instance.)

In principle, then (if not in acutal implementation on certain machines),
printf and vprintf are entirely different beasts.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris at mimsy.umd.edu	Path:	uunet!mimsy!chris



More information about the Comp.unix.wizards mailing list