Reusing a va_list

O

Old Wolf

#include <stdio.h>
#include <stdarg.h>

Is this safe:

void foo(const char *fmt, ...)
{
va_list ap;

va_start(ap,fmt);
vprintf(fmt, ap);
va_end(ap);

va_start(ap,fmt);
vprintf(fmt, ap);
va_end(ap);
}

Supposing it is, how can you re-use the list in this case:

void bar(const char *fmt, va_list ap)
{
vprintf(fmt, ap);
/* ? */
vprintf(fmt, ap);
va_end(ap);
}

where bar() is called by something like:

void baz(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
bar(fmt, ap);
}
 
C

Chris Torek

#include <stdio.h>
#include <stdarg.h>

Is this safe:

void foo(const char *fmt, ...)
{
va_list ap;

va_start(ap,fmt);
vprintf(fmt, ap);
va_end(ap);

va_start(ap,fmt);
vprintf(fmt, ap);
va_end(ap);
}

Yes, this is quite safe in both C89 and C99 (and I have never even
heard of systems on which it fails, which is more remarkable than
its being officially standard :) ).
Supposing it is, how can you re-use the list in this case:

void bar(const char *fmt, va_list ap)
{
vprintf(fmt, ap);
/* ? */
vprintf(fmt, ap);
va_end(ap);
}

where bar() is called by something [that does the va_start for it]?

In C89: you cannot.

In C99: use va_copy() to make a copy of "ap" before passing it to
the first vprintf().

The "C89 workaround" is to force bar()'s caller to pass it two
separate va_list objects:

void call_bar(const char *fmt, ...) {
va_list a1, a2;

va_start(a1, fmt);
va_start(a2, fmt);
bar(fmt, a1, a2);
va_end(a1);
va_end(a2);
}

with the corresponding change to bar().

This workaround is often unsatisfying, to say the least. One trick
is to define your own (non-portable) va_copy() if one is not already
defined. I suggested earlier doing this via "#ifndef va_copy",
because the C99 draft I have said that va_copy() had to be a macro,
but apparently the final C99 standard no longer says this. In any
case, remember that constructing your own va_copy() makes you depend
on your implementation (or implementations, if you make more than
one). On quite a few implementations va_copy() can just use ordinary
assignment, which is convenient; but on others you need the Secret
Handshake that only the compiler-writers know.
 
A

ark

Old Wolf said:
#include <stdio.h>
#include <stdarg.h>

Is this safe:

void foo(const char *fmt, ...)
{
va_list ap;

va_start(ap,fmt);
vprintf(fmt, ap);
va_end(ap);

va_start(ap,fmt);
vprintf(fmt, ap);
va_end(ap);
}

I recall that once I had to write my own printf() clone using ARM SDT 2.51.
My need was to do it two-pass: a dry run to see the number of characters I
would fill, and then the actual run to fill them.
There, I tried your "va_restart" and it didn't work - ON THAT COMPILER ONLY.
Basically, the stack went awry. It was a throwaway demo code, so I settled
on restoring the list pointer by hand.
So, sometimes, relying on standard compliance can be a slippery path...
That's my $0.02
- Ark
 
O

Old Wolf

Supposing it is, how can you re-use the list in this case:
void bar(const char *fmt, va_list ap)
{
vprintf(fmt, ap);
/* ? */
vprintf(fmt, ap);
va_end(ap);
}

where bar() is called by something [that does the va_start for it]?

In C89: you cannot. One trick
is to define your own (non-portable) va_copy() if one is not already
defined. I suggested earlier doing this via "#ifndef va_copy",
because the C99 draft I have said that va_copy() had to be a macro,
but apparently the final C99 standard no longer says this. In any
case, remember that constructing your own va_copy() makes you depend
on your implementation (or implementations, if you make more than
one). On quite a few implementations va_copy() can just use ordinary
assignment, which is convenient; but on others you need the Secret
Handshake that only the compiler-writers know.

Thanks for the informative answer :)

The implementation I use appears to not mung the stack
and would actually allow the two consecutive vprintf() calls above
without anything in between (based on inspecting the contents of
stdarg.h, I haven't actually tried it yet), so I will do this and make
sure to document it in case I have to port the code elsewhere later.
 
M

Michael Wojcik

[re: reusing a va_list without an intervening va_end/va_start]
In C89: you cannot.

In C99: use va_copy() to make a copy of "ap" before passing it to
the first vprintf().

In case anyone's curious about implementations where improperly reusing
a va_list actually fails: Chris was kind enough to correct this mistake
in a code snippet I posted to c.l.c some time back, and I promptly made
the necessary fixes throughout my code - except in one module, which I
somehow overlooked. And that module then failed on a Linux for zSeries
machine. (Fortunately, I double-checked the va_list-using code early
in the debugging process, since this sort of thing can be tough to
track down.)

So thanks again, Chris.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,431
Messages
2,571,679
Members
48,796
Latest member
Greg L.

Latest Threads

Top