Alex Sim
Alex Sim

Reputation: 423

Using va_list as an array in C

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

Answers (3)

supercat
supercat

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

Pablo
Pablo

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

M.M
M.M

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

Related Questions