Reputation: 423
Is it safe and defined behaviour to read va_list like an array instead of using the va_arg function?
EX:
void func(int string_count, ...)
{
va_start(valist, string_count);
printf("First argument: %d\n", *((int*)valist));
printf("Second argument: %d\n", *(((int*)valist)+1));
va_end(valist);
}
Same question for assigningment EX:
void func(int string_count, ...)
{
va_start(valist, string_count);
printf("Third argument: %d\n", *(((int*)valist)+2));
*((int*)valist+2)=33;
printf("New third argument: %d\n", *(((int*)valist)+2));
va_end(valist);
}
PS: This seems to work on GCC
Upvotes: 1
Views: 3439
Reputation: 81247
If an implementation's documentation specifies that va_list may be used in ways beyond those given in the Standard, you may use them in such fashion on that implementation. Attempting to use arguments in other ways may have unpredictable consequences even on platforms where the layout of parameters is specified. For example, on a platform where variadic arguments are pushed on the stack in reverse order, if one were to do something like:
int test(int x, ...)
{
if (!x)
return *(int*)(4+(uintptr_t)&x); // Address of first argument after x
... some other code using va_list.
}
int test2(void)
{
return test(0, someComplicatedComputation);
}
a compiler which is processing test2 might look at the definition of test, notice that it (apparently) ignores its variadic arguments when the first argument is zero, and thus conclude that it doesn't need to compute and pass the result of someComplicatedComputation. Even if the documentation for the platform documents the layout of variadic arguments, the fact that the compiler can't see that they are accessed may cause it to conclude that they are not.
Upvotes: 0
Reputation: 13590
No, it is not, you cannot assume anything because the implementation varies across libraries.
The only portable way to access the values is by using the macros defined in stdarg.h
for accessing the
ellipsis. The size of the type is important, otherwise you end up reading garage
and if your read more bytes than has been passed, you have undefined behaviour.
So, to get a value, you have to use va_arg
.
See: STDARG documentation
You cannot relay on a guess as to how va_list
works, or on a particular
implementation. How va_list
works depends on the ABI, the architecture, the
compiler, etc. If you want a more in-depth view of va_list
, see
this answer.
edit
A couple of hours ago I wrote this answer explaining how to use the
va_*
-macros. Take a look at that.
Upvotes: 3
Reputation: 141638
No, this is not safe and well-defined. The va_list
structure could be anything (you assume it is a pointer to the first argument), and the arguments may or may not be stored contiguously in the "right order" in some memory area being pointed to.
Example of va_list implementation that doesn't work for your code - in this setup some arguments are passed in registers instead of the stack, but the va_arg
still has to find them.
Upvotes: 3