use of varargs....portable???

Guy Harris guy at gorodish.Sun.COM
Sun Apr 10 08:19:11 AEST 1988


In article <206 at vedge.UUCP>, lai at vedge.UUCP (David Lai) writes:
> Perhaps this has been harped on before, but I seem to have discovered
> some interesting differences between 2 C compilers (sun3 and sun4) that
> make varargs not so portable.  If you know of any 'portable' varargs
> implementations, please let me know.

How about Sun-3 and VAX?

The following program:

	#include <varargs.h>

	struct goober1 {
		char	c;
	};

	struct goober2 {
		int	i;
		int	j;
		int	k;
	};

	main()
	{
		struct goober1 goober1_1, goober1_2;
		struct goober2 goober2_1, goober2_2;

		goober1_1.c = 11;
		goober2_1.i = 1;
		goober2_1.j = 2;
		goober2_1.k = 3;
		goober1_2.c = 22;
		goober2_2.i = 100;
		goober2_2.j = 200;
		goober2_2.k = 300;
		varargs_func(2, goober1_1, 17, goober2_1, 34);
		varargs_func(3, goober1_1, 17, goober2_1, 34, goober1_2, 51);
		varargs_func(4, goober1_1, 17, goober2_1, 34, goober1_2, 51,
		    goober2_2, 68);
	}

	int
	varargs_func(va_alist)
	va_dcl
	{
		va_list ap;
		register int i;
		register int count;
		struct goober1 one;
		struct goober2 two;
		register int num;

		va_start(ap);
		count = va_arg(ap, int);
		printf("count = %d\n", count);
		for (i = 0; i < count; i++) {
			if (!(i&01)) {
				one = va_arg(ap, struct goober1);
				printf("goober1: c = %d\n", one.c);
			} else {
				two = va_arg(ap, struct goober2);
				printf("goober2: i = %d, j = %d, k = %d\n",
				    two.i, two.j, two.k);
			}
			num = va_arg(ap, int);
			printf("num = %d\n", num);
		}
	}

gives different answers on a VAX running 4.3BSD and a Sun-3 running SunOS
4.0 - *neither* of which is correct!  (A VAX running System V will probably
give the same wrong answer as a VAX running 4.3BSD; a VAX running VMS may very
well give a different answer.)

You see, on the VAX, "sizeof (struct goober1)" is 1, while on the Sun-3 (and, I
suspect, on most other 68K boxes), "sizeof (struct goober1)" is 2.  It appears,
however, that *both* compilers align arguments pushed onto the stack on 4-byte
boundaries, so that the value of "ap" should be bumped by 4 when fetching a
"struct goober1", not by 1 (as is done on the VAX) or 2 (as is done on the
Sun-3 and probably on other 68Ks).

Another way of putting this is "passing structures to varargs functions doesn't
work, in general".  Sad, but true.

It may be possible to fix some or all of these problems in current C; the
68K/VAX problem can be fixed by having the "va_arg" macro round the size of the
object up to a multiple of 4 bytes before adding it to "ap".

The problem of machines that don't pass structures simply by putting them
in-line after the other arguments (what do other machines that pass arguments
in registers, such as the MIPS or Pyramid, do here?) may require ANSI C to
solve it; if you declare a function as being a function taking a variable
number of arguments, e.g.

	#include <stdarg.h>

	...

	extern int varargs_func(int count, ...);

	...

	int
	varargs_func(int count, ...)
	{
		va_list ap;
		register int i;
		struct goober1 one;
		struct goober2 two;
		register int num;

		va_start(ap, count);
		...
	}

the compiler would know that a different calling sequence should be used and
just dump all the arguments past the last "fixed" argument into memory in the
form the <stdarg.h> implementation expects.

> The sun3 compiler:

	...

> 	structures are passed on the stack subject to:
> 		1) If the size of the structure is less than 4
> 		   then it will be padded on lower addresses
> 		   and passed as size 4, sizeof in the function
> 		   will not report the padding
> 		2) If the structure size is 4 or more, it will
> 		   be passed unpadded.

Note that, as mentioned above, "sizeof" does report whatever padding is made to
satisfy the compiler's general structure alignment rules, so on the Sun-3
structures are padded to a 2-byte boundary and that padding *is* reported by
"sizeof".  (Also note that "sizeof" should report the *same* size inside and
outside the function.)

> The sun4 compiler:

> 	<I'm not sure how arguments are passed, but eventually they
> 	 appear in a linear block of memory>

If you're not passing any floating-point parameters, the first 6 parameters are
passed in %o0-%o5; all subsequent parameters are passed on the stack.  The
SunOS 4.0 and Sys4-3.2L <varargs.h> includes

	#if defined(sparc)
	# define va_alist __builtin_va_alist
	#endif

and the appearance of "__builtin_va_alist" in the argument list tells the
compiler to generate code to dump arguments onto the stack in the proper place
for "va_arg" to pick them up.

> 	structures are first copied into some temporary memory, then
> 	 passed as pointers.  Within the function sizeof shows the
> 	 actual structure size, not the pointer size.

Which is as it should be; "sizeof (struct foo)" should always give the size of
such a structure.



More information about the Comp.lang.c mailing list