The va_copy() is supposed to be part of the C99 specification; as with all variadic parameter support is very platform dependent.  The va_list, va_copy(), va_start(), va_end() macros are defined in stdarg.h .
GCC: When attempting to reuse a va_list on GCC, one MUST use va_copy(), as the GCC implementation causes the va_list to be modified, causing the pointer to be positioned after last param after use by a v??printf() function.
SUN: When attempting to reuse a va_list in SunStudio (v11,v12), the va_list variable is untouched and can be reused as many times as desired without needing to va_copy().
MS_Visual C: Not certain, but looks like the 2010 VC++ docs do mention 'va_copy()' and may imply reuse of va_list is reasonable, but should be tested.
Example:
#include <stdio.h>
#include <stdarg.h>
/**
 * Version of vsprintf that dynamically resizes the given buffer to avoid overrun.
 * Uses va_copy() under GCC compile.
 **/    
int my_vsprintf(char **buffer, char *msg, va_list args)
{
   int bufLen = 0;
   va_list dupArgs;       // localize args copy
#ifdef __GNUC__
   va_copy(dupArgs,args); // Clone arguments for reuse by different call to vsprintf.
#else 
   dupArgs = args;        // Simply ptr copy for GCC compatibility
#endif
   // Perform 1st pass to calculate required buffer size.  The vsnprintf() funct
   // returns the number of chars (excluding \0 term) necessary to produce the output.
   // Notice the NULL pointer, and zero length.
   bufLen = vsnprintf(NULL,0,msg, dupArgs); 
   // NOTE: dupArgs on GCC platform is mangled by usage in v*printf() and cannot be reused.
#ifdef __GNUC__
   va_end(dupArgs); // cleanup 
#endif
   *buffer = realloc(*buffer,bufLen + 1);  // resize buffer, with \0 term included.
#ifdef __GNUC__
   va_copy(dupArgs,args); // Clone arguments for reused by different call to vsprintf.
#endif
   // Perform 2nd pass to populate buffer that is sufficient in size,
   // with \0 term size included.
   bufLen = vsnprintf(buffer, bufLen+1, msg, dupArgs);
   // NOTE: dupArgs on GCC platform is mangled by usage in v*printf() and cannot be reused.
#ifdef __GNUC__
   va_end(dupArgs); // cleanup
#endif
   return(bufLen);  // return chars written to buffer.
}
/**
 * Version of sprintf that dynamically resizes the given buffer to avoid buffer overrun
 * by simply calling my_vsprintf() with the va_list of arguments.
 *
 * USage:
 **/
int my_sprintf(char **buffer, char *msg, ...)
{
    int bufLen = 0;
    va_list myArgs; 
    va_start(myArgs, msg);   // Initialize myArgs to first variadic parameter.
    // re-use function that takes va_list of arguments.       
    bufLen = my_vsprintf(buffer, msg, myArgs ); 
    va_end(myArgs);
}