By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
424,687 Members | 2,028 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 424,687 IT Pros & Developers. It's quick & easy.

Reusing a va_list

P: n/a
#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);
}
Nov 14 '05 #1
Share this Question
Share on Google+
4 Replies


P: n/a
In article <news:84**************************@posting.google. com>
Old Wolf <ol*****@inspire.net.nz> writes:
#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.
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (4039.22'N, 11150.29'W) +1 801 277 2603
email: forget about it http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.
Nov 14 '05 #2

P: n/a
ark

"Old Wolf" <ol*****@inspire.net.nz> wrote in message
news:84**************************@posting.google.c om...
#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
Nov 14 '05 #3

P: n/a
> >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.
Nov 14 '05 #4

P: n/a

In article <bu********@enews1.newsguy.com>, Chris Torek <no****@torek.net> writes:
[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.

--
Michael Wojcik mi************@microfocus.com

Not the author (with K.Ravichandran and T.Rick Fletcher) of "Mode specific
chemistry of HS + N{_2}O(n,1,0) using stimulated Raman excitation".
Nov 14 '05 #5

This discussion thread is closed

Replies have been disabled for this discussion.