Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are there gotchas using varargs with reference parameters

I have this piece of code (summarized)...

AnsiString working(AnsiString format,...) {     va_list argptr;     AnsiString buff;      va_start(argptr, format);     buff.vprintf(format.c_str(), argptr);      va_end(argptr);     return buff; } 

And, on the basis that pass by reference is preferred where possible, I changed it thusly.

AnsiString broken(const AnsiString &format,...) { ... the rest, totally identical ... } 

My calling code is like this:-

AnsiString s1, s2;     s1 = working("Hello %s", "World");     s2 = broken("Hello %s", "World"); 

But, s1 contains "Hello World", while s2 has "Hello (null)". I think this is due to the way va_start works, but I'm not exactly sure what's going on.

like image 284
Roddy Avatar asked Oct 21 '08 14:10

Roddy


People also ask

Can a reference parameter be modified?

Yes, that is the correct way to declare a reference argument. And yes, you can modify objects through a reference.

How many number of values can be accommodated by Varargs in Java?

A method (including the static class initializer) can have at most 64k. If the arguments are such that they can be pushed with a single bytecode that is 1 byte long each, you can have something about 64000 arguments on a call.

What is the main feature of reference parameter?

When a reference parameter is used, the address (not the value) of an argument is automatically passed to the function. Within a function, operations on the reference parameters are automatically dereferenced.


1 Answers

If you look at what va_start expands out to, you'll see what's happening:

va_start(argptr, format);  

becomes (roughly)

argptr = (va_list) (&format+1); 

If format is a value-type, it gets placed on the stack right before all the variadic arguments. If format is a reference type, only the address gets placed on the stack. When you take the address of the reference variable, you get the address or the original variable (in this case of a temporary AnsiString created before calling Broken), not the address of the argument.

If you don't want to pass around full classes, your options are to either pass by pointer, or put in a dummy argument:

AnsiString working_ptr(const AnsiString *format,...) {     ASSERT(format != NULL);     va_list argptr;     AnsiString buff;      va_start(argptr, format);     buff.vprintf(format->c_str(), argptr);      va_end(argptr);     return buff; }  ...  AnsiString format = "Hello %s"; s1 = working_ptr(&format, "World"); 

or

AnsiString working_dummy(const AnsiString &format, int dummy, ...) {     va_list argptr;     AnsiString buff;      va_start(argptr, dummy);     buff.vprintf(format.c_str(), argptr);      va_end(argptr);     return buff; }  ...  s1 = working_dummy("Hello %s", 0, "World"); 
like image 123
Eclipse Avatar answered Sep 18 '22 07:09

Eclipse