An example of using va_copy

This example C program demonstrates the use of va_copy.

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

typedef unsigned char mystring_data_t;

typedef struct mystring {
    mystring_data_t * data;
    size_t size;
    size_t length;
}
mystring_t;

static void mystring_fail (const char * message, ...)
{
    va_list args;
    va_start (args, message);
    vfprintf (stderr, message, args);
    va_end (args);
    fprintf (stderr, "\n");
    exit (EXIT_FAILURE);
}

static void mystring_vprintf (mystring_t * mystring, const char * format, va_list arguments)
{
    int printed_length;
    int remaining;
    mystring_data_t * write_point;
    /* If there is not enough allocated memory within "mystring" to print
       the required text of "format" and "arguments", we have to
       re-attempt the printing, thus we need to copy "arguments" for
       the case of a failure.  */
    va_list copy4fail;

    va_copy (copy4fail, arguments);

    write_point = mystring->data + mystring->length;
    /* Number of remaining characters. */
    remaining = mystring->size - mystring->length;
    printf ("remaining = %d, size = %d\n", remaining, mystring->size);

    printed_length = vsnprintf ((char *) write_point, remaining,
                                format, arguments);
    if (printed_length >= remaining - 1) {
	/* First attempt to print failed. Resize the mystring and try
	   again. */
	mystring->size = mystring->length + printed_length + 1;
	printf ("printed_length = %d, size = %d\n", printed_length, mystring->size);
	mystring->data = realloc (mystring->data, mystring->size);
	if (! mystring->data) {
	    mystring_fail ("no memory");
	}
	remaining = mystring->size - mystring->length;
	/* We need to get the write point again since the memory may
	   have moved, due to realloc. */
	write_point = mystring->data + mystring->length;
	printed_length =
	    vsnprintf ((char *) write_point, remaining, format, copy4fail);
	/* If the printed length is still greater than the supposed
           remaining number of characters, there must have been an
           error, so end processing. */
	if (printed_length > remaining) {
	    mystring_fail ("Inconsistent printed length %d remaining %d",
		       printed_length, remaining);
	}
    }
    mystring->length += (size_t) printed_length;
    va_end (copy4fail);
}

static void mystring_printf (mystring_t * mystring, char * format, ...)
{
    va_list args;
    va_start (args, format);
    mystring_vprintf (mystring, format, args);
    va_end (args);
}

int main ()
{
    mystring_t mystring;
    mystring.size = 10;
    mystring.data = calloc (mystring.size, sizeof (mystring_data_t));
    if (! mystring.data) {
	fprintf (stderr, "Memory failure.\n");
	return -1;
    }
    mystring.data[0] = 'a';
    mystring.data[1] = 'b';
    mystring.length = 2;
    mystring_printf (& mystring, "supercallifragilisticexpialidocious %d %d %d", 99, 100, 101);
    printf ("'%s'\n", (char *) mystring.data);
    free (mystring.data);
    return 0;
}

(download)

The output of the example looks like this:

remaining = 8, size = 10
printed_length = 46, size = 49
'absupercallifragilisticexpialidocious 99 100 101'

I wrote this to test whether it was necessary to use va_copy before using the argument list or not. The manual page for va_copy doesn't really make it clear whether it is safe to use va_copy after using the argument list. I tried moving it within the if block, but this causes segmentation faults sometimes. So it seems necessary to use it outside the if block.


Copyright © Ben Bullock 2009-2023. All rights reserved. For comments, questions, and corrections, please email Ben Bullock (benkasminbullock@gmail.com) or use the discussion group at Google Groups. / Privacy / Disclaimer