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:
- char * format – a regular string
- and a second special argument, … (3 dots, just like that)
… 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;
}
33 Comments
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.
put them into an array!
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?
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
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.
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?
This is how I might do it.
I’ve preserved the original datatypes as well.
Hello,
your exemple is very clear and helpful.
But i’d like to know how can i change value of vars as scanf do?
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.
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;
}
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
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 ) ; }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.
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.
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.
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
I have a similar problem. I’m trying to wrap a C function with va_list into OCaml. Forget about OCaml, here. But the thing is I want to feed a list of unknown length to the function.
I see only one solution to make a clean call this way: Bypass C, and use the C ABI of your platform directly. Calling conventions for multiple arguments is documented in the ABI, so you could wrap it there.
The only reason I’m not doing that is that is becomes platform-specific. So my question is there any platform-agnostic, from C for example, to make a call via the ABI without knowing beforehand the number of argument?
Or is va_list the only implementation of this feature in the ABI?
Basically, my question is: how do constructed the data structure referenced by listPointer?
I want to implement printf() using va_list,but no printf,cin can be used within the function.
help,asap.
thankyou sir,
your explanation are really more clear than the study material available on other website,,
I am da best
Perfect!!
Hi,
One doubt regarding the va_start()
Here you have given the number of arguments as the first parameter to the function and hence used it in va_start().
Is it mandatory to have the number of arguments always ?
Regards,
Vinay
Thank you buddy,
You have explained it very very well …Thank you very much :)
Thanks very much for your work!
Dude, this explanation was perfect, thanks so much!
yes bobbo you are the best:)
Very helpful text.
This is the detailed code of how it really works
void func(size_t size, …)
{
/*
*this statement is same as ‘var_list arg_list’
*/
char* arg_list;
/*
*get the memory address of the first parameter
*/
char* pSize = (char*)&size;
/*
*add the size of integer to the current pointer (each parameter is separated by bytes size of integer)
*this statement is same as ‘var_start(arg_list,size)’
*/
arg_list = pSize + sizeof(int);
for (size_t i = 0; i < size; i++)
{
/*
*To get the argument at index 'i' multiply that by the size of integer and add it to the list address
*this statement is same as 'var_arg(arg_list)' but our statement is random iteratable
*/
char *t = (arg_list + (sizeof(int) * i));
//do something here…
}
/*
/*this statement is same as 'var_end(arg_list)'
*/
arg_list = NULL;
}
int main()
{
func(7, //size of array
'a','b','c','d','e','f','g'); //infinite arguments
}
Quite amazed by the post .. elegantly written, but i am facing a problem in passing the format to the another function from a function like
void fun2(char* format, ..)
{
va_list args;
va_start(args, format);
vprintf(format, args);
va_end(args);
}
void fun1(char* format,…)
{
fun2(format);
}
int main()
{
char* ptr = “Hi”;
int intVar = 10;
fun1(“I am saying %s for %d times”,ptr,intVar);
return 0;
}
This code doesnt work! Plz help me.. But if i try to print in fun1 function, it is working fine..
sorry my previous post was supposed to be
void fun2(char* format, …)
{
va_list args;
va_start(args, format);
vprintf(format, args);
va_end(args);
}
Thanks A LOT! I always wondered how lists of arguments worked :) It was very clear and easy to follow.
I have a question regarding returning in the variadic (variable argumnets) functions.
Suppose I want to return value in the parameters I am passing to the variadic function, how can I do that?
The sample code I want to use it in looks something like this,
int main(void) {
int val1;
int val2;
custom_funct(2, &val1, &val2);
printf(“val1 = %d, val2 = %d”, val1, val2);
return 0;
}
void custom_funct(int num_args, …) {
va_list arglist;
va_start(arglist, num_args);
// assign values using pointers to val1 and val2.
// for example,
// val1 = 100; val2 = 200;
va_end(arglist);
}
3 Trackbacks/Pingbacks
[...] } I metodi che si aspettano un numero variabile di parametri li gestiscono utilizzando la sintassi del linguaggio C, che prevede l’uso di va_list e va_start. Per esempio: – (void)setValori:valori, … { va_list ap; va_sart(ap, group); // …. } Un approfondimento si trova qui. [...]
[...] [...]
[...] Source: http://bobobobo.wordpress.com/2008/01/28/how-to-use-variable-argument-lists-va_list/ [...]