[comp.std.c] Passing a `va_list *'

mcgrath@tully.Berkeley.EDU (Roland McGrath) (10/28/89)

Is this kosher:

#include <stddef.h>

void foo(char *s, ...)
{
  va_list args;
  va_start(args, s);
  bar(va_arg(args, double));
  ack(&args);
  ugh(va_arg(args, int));
  va_end(args);
}

void ack(va_list *args)
{
  blip(va_arg(args, int **));
}

I can't tell from the Standard.  It works on machines where a `va_list' is a
pointer into the stack, but I don't know about other schemes.
--
	Roland McGrath
	Free Software Foundation, Inc.
roland@ai.mit.edu, uunet!ai.mit.edu!roland

gwyn@smoke.BRL.MIL (Doug Gwyn) (10/28/89)

In article <MCGRATH.89Oct27150426@tully.Berkeley.EDU> mcgrath@tully.Berkeley.EDU (Roland McGrath) writes:
>#include <stddef.h>

That should be <stdarg.h>, of course.

>  va_list args;
>  ack(&args);
...
>void ack(va_list *args)
>{
>  blip(va_arg(args, int **));
>}

I wonder how this could possibly have worked.  va_arg() takes a va_list
as its first argument, not a va_list*.  Let's assume this is a typo.

I don't have my copy of the Standard at hand at the moment, but I did
spend a lot of time working in this area, and as I recall the Standard
guarantees that you can pass the va_list to a function and continue to
use it within the function, but not necessarily a pointer to the va_list.
Upon return from the function, the va_list is "dead" until va_end()ed
and re-va_start()ed.

To answer the question of whether you can use a pointer to the va_list,
you need to consider what types are allowed for va_list.  I don't recall
exactly how this ended up; it changed around a few times.  The one to
watch out for is an array type.

mcgrath@paris.Berkeley.EDU (Roland McGrath) (10/29/89)

My example did indeed have typos.  What I meant was:

#include <stdarg.h>

void
foo(const char *s, ...)
{
  va_list args;
  va_start(args, s);
  bar1(va_arg(args, int));
  ack(&args);
  bar2(va_arg(args, float *));
  va_end(args);
}

void ack(va_list *args)
{
  blip(va_arg(*args, int **));
}


I have a somewhat old draft of the Standard (May 1988), but I can't find
anything that actually says this isn't allowed.

I don't see the problem with whatever type `va_list' is.  You can take the
address of an object of any type (though not every storage class), and
dereference the pointer thus obtained.
--
	Roland McGrath
	Free Software Foundation, Inc.
roland@ai.mit.edu, uunet!ai.mit.edu!roland

gwyn@smoke.BRL.MIL (Doug Gwyn) (10/30/89)

In article <MCGRATH.89Oct28150927@paris.Berkeley.EDU> mcgrath@paris.Berkeley.EDU (Roland McGrath) writes:
>I don't see the problem with whatever type `va_list' is.  You can take the
>address of an object of any type (though not every storage class), and
>dereference the pointer thus obtained.

I'm not sure there is a problem, but you need to check carefully what
happens when va_list is an array type (which is permitted but not
required by the Standard).  I'm away from my copy of the Standard now,
but for example if va_arg() were permitted to be a function, *args in
the inner function would be an attempt to pass an array to va_args(),
not just the name of an array (which would be converted to a pointer
to the array's first element) as in the top-level function.  I forget
whether this usage would also result in conversion to a pointer to
the first element, but if not then it is illegal (and if it did
work, would result in the wrong type being passed to va_args()).  I'll
have to look this up and let you know.

In any case &array will do something different in older PCC implementations
than in Standard C (and is officially not allowed according to K&R1), so if
the code has to work in both <varargs.h> and <stdarg.h> environments, it
may not be portable.

I wish I remembered why we allowed (in one draft, even required) the
va_list to be an array type.  It sure is a nuisance.