Is va_list defined by <stdio.h>?

Chris Torek torek at elf.ee.lbl.gov
Sat Apr 6 10:29:28 AEST 1991


In article <16863 at hoptoad.uucp> gnu at hoptoad.uucp (John Gilmore) writes:
>In an ANSI C implementation, it appears that the type "va_list" must be
>defined by <stdio.h> ...

No; in fact, the type `va_list' must not be defined there, but must be
*used* there regardless (as you note).

This leaves the problem of somehow using a type without first defining
it.  There is a way, based on the observation that `typedef' does not
define a new type, but rather defines a new name for an existing type.
What one does, then, is this:

 A. Define some underlying representation V for `va_list'.  This is
    machine dependent, but is typically something like `pointer to
    char' or `pointer to struct __va_list'.

 B. In <stdio.h>, declare v*printf with something like:

	int vprintf(const char *fmt, struct __va_list *);

 C. in <stdarg.h>, define the type `va_list' with something like

	typedef struct __va_list *va_list;

This introduces two new problems: <stdio.h> is now machine dependent
(an otherwise-unnecessary situation), and <stdarg.h> and <stdio.h> must
somehow be kept in sync.

What we have done in the current BSD system, which solves all of this,
is create a machine-dependent header file (found in <machine/ansi.h>,
which is a minor misnomer) that reads more or less as follows:

	/* faux MIPS <machine/ansi.h> */

	#ifndef _ANSI_H_
	#define _ANSI_H_

	struct __va_list { int n0; char *p0, *p1; };	/* 2 save areas */

	#define	_CLOCK_T_	unsigned long
	#define	_PTRDIFF_T_	int
	#define	_SIZE_T_	unsigned int
	#define	_TIME_T_	unsigned int
	#define	_VA_LIST_	struct __va_list *
	#define	_WCHAR_T_	unsigned short

	#endif /* _ANSI_H_ */

This file is included whenever any of the machine-dependent types is
required.  Typedefs that appear in more than one standard header,
such as size_t (which appears in <stddef.h>, <stdio.h>, <string.h>,
and <time.h>), are defined in each standard header with the sequence

	#ifdef	_SIZE_T_
	typedef _SIZE_T_ size_t;
	#undef	_SIZE_T_
	#endif

so that each such typedef appears at most once.  Typedefs that appear in
only one header (namely va_list) are simply defined via

	typedef _VA_LIST_ va_list;

and <stdio.h> just uses the sequence

	#include <machine/ansi.h>
	int vprintf(const char *, _VA_LIST_);

(had va_list to appear in more than one standard header, we would have
had to create two implementation-space macro names for it; fortunately
this is not the case).

This `backwards' sequence (of defining the types via macros, then
turning those into typedefs exactly once by undefining the macro in the
process) uses the minimal amount of verbiage.  Another scheme is to
have one macro for each type, and another for each `typedef has been
done' flag, but this is unnecessary.

Machine-dependent headers such as <stdarg.h> are in fact read from
the `machine' directory as well.

Note that the technique used in SunOS 4.1 and 4.1.1, in which
<sys/stdtypes.h> contains all the `typedef's for all standard headers,
is incorrect, precisely because of the problem addressed here (some
headers must use some types which those same headers must not define).

(Good grief, these one-sentence paragraphs are starting to look like
Bill-Joy-ese. :-)  I find all the passive voice annoying too. [Look
what happens when you get wheedled into editing man pages....])
-- 
In-Real-Life: Chris Torek, Lawrence Berkeley Lab CSE/EE (+1 415 486 5427)
Berkeley, CA		Domain:	torek at ee.lbl.gov



More information about the Comp.std.c mailing list