Understanding va_list

Writing your own function that uses a va_list is really easy!

But first, let’s identify what a va_list is.

Think about the printf() C function.

printf(“Hello there! I like the numbers %d, %d and %d\n\n\n”, 1, 3, 7);

Obviously the output of that function call would be:

Hello there! I like the numbers 1, 3 and 7

But the key point here is, the printf() function can accept a VARYING NUMBER OF ARGUMENTS. That’s because it uses a va_list.

If you look at the signature for printf(), it looks like this:

int printf( char * format, … );

So the argument list for printf() has 2 main things:

  1. char * format – a regular string
  2. and a second special argument, … (3 dots, just like that)
  3. … is called an “ellipsis”, and it means, in plain English: “any number of optional arguments can go here.”

    So somehow, in the innermost bowels of printf(), is some sticky code that somehow retrieves each one of the the list of args you’re passing in, in the place of the “…”.

    Cool! So is it possible for us to write our functions that have their own sticky code that can process a set of VARIABLE ARGUMENTS???

    YES YOU CAN. And its actually simple!

    An example:

    #include <stdio.h>
    #include <stdarg.h>
    
    int addThemAll( int numargs, ... )
    {
        // So this function can accept a variable number
        // of arguments.  No (practically speaking) limits.
    
        // RULES you must know in order to be able to use "..." in one of your
        // own functions:
        //
        // 1)  The ... MUST appear exactly as ...
        // It cannot be "..." (with the quotes),
        //     '...', or anything else weird.
        //
        // 2)  The ... __MUST GO LAST__ IN THE ARGUMENT LIST
        //
        // 3)  THERE MUST BE AT LEAST ONE MANDATORY, NON-OPTIONAL ARGUMENT,
        //     THAT COMES BEFORE THE ...
    
        // We'll be using these macros here:
    /*
         va_list    va_start    va_end    va_arg
    */
    
        // All of the above va_* things are actually special MACROS,
        // exclusively defined for us to use when working with
        // _V_ariable _A_rgument lists.
    
        // FIRST, we create a POINTER that will be used
        // to point to the first element of the VARIABLE
        // ARGUMENT LIST.
        va_list listPointer;
    
        // Currently, listPointer is UNINITIALIZED, however,
        // SO, now we make listPointer point to
        // the first argument in the list
        va_start( listPointer, numargs );
    
        // Notice that numargs is the LAST MANDATORY ARGUMENT
        // that the addThemAll() function takes.
        // By "LAST MANDATORY ARGUMENT", I mean 'numargs'
        // is the last argument to the addThemAll() function
        // JUST BEFORE the "..."
    
        // NEXT, we're going to start to actually retrieve
        // the values from the va_list itself.
        // THERE IS A CATCH HERE.  YOU MUST KNOW THE
        // DATA TYPE OF THE DATA YOU ARE RETRIEVING
        // FROM THE va_list.  In this example, I'm assuming
        // they're all ints, but you could always pass a format
        // string that lets you know the types.
    
        int sum = 0;
        for( int i = 0 ; i < numargs; i++ )
        {
            // GET an arg.  YOU MUST KNOW
            // THE TYPE OF THE ARG TO RETRIEVE
            // IT FROM THE va_list.
            int arg = va_arg( listPointer, int );
    
            printf( "    The %dth arg is %d\n", i, arg );
    
            sum += arg;
        }
    
        printf("--");
        printf("END OF ARGUMENT LIST\n\n");
    
        // FINALLY, we clean up by saying
        // va_end().  Don't forget to do this
        // BEFORE the addThemAll() function returns!
        va_end( listPointer );
    
        printf("The total sum was %d\n\n", sum);
    
        return sum;
    }
    
    int main()
    {
        // Try it out.
    
        printf("Calling 'addThemAll( 3, 104, 29, 46 );' . . .\n");
        addThemAll( 3, 104, 29, 46 );
    
        printf("Calling 'addThemAll( 8,   1, 2, 3, 4, 5, 6, 7, 8 );' . . .\n");
        addThemAll( 8,   1, 2, 3, 4, 5, 6, 7, 8 );
    
        return 0;
    }
    

    Download Visual Studio project files.

16 Comments

    • Ran
    • Posted May 14, 2008 at 12:07 pm
    • Permalink

    OK. This is clear and simple. but what if I want to postpone processing the variable list for later? For example, say that I have a class A:
    class A{
    public:
    void setParams( int args, …);
    void processParams();
    };
    surly I need to keep the variable parameters in A somehow between the call to ’set’ and ‘process’. If you are looking for justification for such requirement, it is very simple: from one context you set the parameters and from another you process them.

  1. put them into an array!

    • Ran
    • Posted June 1, 2008 at 3:58 pm
    • Permalink

    Please be more specific. what is the type of this array? How to declare:
    myArray[ args ]; ?
    What is ?
    If I need to process the arguments to keep them, then it doesn’t help. I want to postpone the processing. I need something that looks like the stack when entering a function with ‘…’ parameters.
    Any suggestions?

  2. assuming, they’re all ints, you can maintain an array like:

    int * arr = (int*)malloc( numargs*sizeof(int) );

    for( int i = 0 ; i < numargs; i++ )
    {
    // . . get arg, store in array
    }

    careful of leaks!

    hope that helps

    • Ran
    • Posted June 4, 2008 at 8:44 am
    • Permalink

    Nope. If I know the types I have no problems. If it is only one type, even if I don’t know what is this type – still no problem (I can figure the type later and copy the parameters to the largest type acceptable array). I need the whole – different types, different number of variables, completely unseen. As it in printf. Actually, I need that my users will use my function exactly as they use printf. Only that the processing of %d, %p and alike will be done later, in a different task. I didn’t find a solution for this up till now.

    • Ran
    • Posted June 4, 2008 at 9:48 am
    • Permalink

    OK. After writing you the answer the solution entered my mind. The idea is to keep every variable in the list in the *maximum* storage that might be needed. That means, for instance, that every parameter is copied into a 4 bytes length field. then, if I have this code: Ranf( “char %c, int %d” ); they will both be copied into an array of 4 bytes fields:
    UINT32 myVariableTypes[ MAX_PARAMETERS ];
    Ranf( const char* text, … )
    {
    //copy every parameter into //myVariableTypes. Later the string //’text’ will indicate the specific type
    }

    The only problem is how to know the number of parameters. For this I can either scan ‘text’ or ask the user to supply it: Ranf( UINT8 noOfParameters, const char* text, … );
    It is a bit annoying but saves run time, which is very critical for me.

    what do you say?

  3. This is how I might do it.

    I’ve preserved the original datatypes as well.

    • Als
    • Posted October 3, 2008 at 2:24 am
    • Permalink

    Hello,
    your exemple is very clear and helpful.
    But i’d like to know how can i change value of vars as scanf do?

    • Kidar
    • Posted October 31, 2008 at 4:15 pm
    • Permalink

    Thank you for your example, it helped me to do my custom fprintf(UARTPutChar,”Hello World! %d”, year) function for example, that prints to UART output instead to stdin or string array.

  4. Here is a ANSI C example of how pass … from one function to another without intermediate pointer. I believe that works in C++ too

    #include
    #include

    void
    my_funcv (const char *text, va_list args)
    {
    printf(“I’m my_newv and I receive … from my_func\n”);
    printf(“Value is: %s\n”, text);
    char *value;
    while (value = va_arg(args, char *))
    printf(“Value is: %s\n”, value);
    }

    void
    my_func (const char *text, …)
    {
    va_list args;
    va_start(args, text);
    my_funcv (text, args);
    va_end (args);
    }

    int
    main (int argc, char *argv[])
    {
    my_func(“This”, “is”, “a”, “test”, NULL);
    return 0;
    }

    • rocketGuy
    • Posted July 9, 2009 at 3:33 pm
    • Permalink

    Can you have a function that takes a variable argument list and directly pass it to another fucntion?

    void myLogger(char *format, int numArgs, …)
    {
    systemLogger(char *format, int numArgs, …);
    }
    or do you have to extract the argument then pass them on to the second function?

    void myLogger(cahr *format, int

  5. You can pass listPointer to another function, yes

    vsprintf is an example of a such function that accepts an already started va_list as an argument

    void log( char * fmt, ... )
    {
      // create and start the va_list
      va_list listPointer ;
      va_start( listPointer, fmt ) ;
    
      // vsprintf is an example of
      // a function that works with
      // an "already started" va_list
      static char buf[ 1024 ] ;
      vsprintf( buf, fmt, listPointer ) ;
    }
    
    • beezee
    • Posted August 28, 2009 at 4:38 pm
    • Permalink

    Very good discussion. Another way to pass va_list is by using va_copy. This creates a memory copy of the listPointer on the heap. This macro needs to be within the va_start and va_end block so that the memory assigned can be released.

    • peter
    • Posted September 7, 2009 at 12:55 pm
    • Permalink

    I am glsd to see the discussions here,
    But i ahve a doubt here. How to find out the second argument directly , i know the type previously.

    The condition is
    a) some times i have only one argument a char * string

    b) some time i have 2 arguments one a string and other an integer.

    How i will find out the existance of second argument.

    It will be very greatful i will get a proper reply. Thanks in advance.

    • The Big Priest
    • Posted September 30, 2009 at 8:49 am
    • Permalink

    Here’s another challange,
    What if I need to pass a different number of arguments to a function (one of a set).
    Consider the following code:

    //declerations
    void A::foo();
    void A::foo(int n1);
    void A::foo(int n1, int n2);
    void A::foo(int n1, int n2, int n3);

    void A::my_func(int num_args, …)
    {
    .
    .
    .
    // now I need to call specific foo()
    foo(???);
    }

    Do I have to parse the aguments before and then call the function? I mean with a set of ‘if’ statements [if (num_args==0) call this, if (num_args==1) call that and so on]
    OR
    Do I have a simple way?

    Thanks.

  6. It looks like the way you’re doing it is the simplest way.

    void A::my_func(int num_args, ... )
    {
      switch( num_args )
      {
        case 0:
          foo() ;
          break;
        case 1:
          // pull 1 arg here,
          foo( arg ) ;
          break;
      }
    }
    

    I think you’re thinking of scripting languages like javascript where you can “form” the string of code you want to execute, then eval() it later. But in C++ at some point in the code you’re going to have to explicitly write the code that calls the 1 argument version, 2 argument version, etc


Post a Comment