When trying to compile this code
#include <stdarg.h>
void bar_ptr(int n, va_list *pvl) {
// do va_arg stuff here
}
void bar(int n, va_list vl) {
va_list *pvl = &vl; // error here
bar_ptr(n, pvl);
}
void foo(int n, ...) {
va_list vl;
va_list *pvl = &vl; // fine here
va_start(vl, n);
bar(n, vl);
va_end(vl);
}
int main() {
foo(3, 1, 2, 3);
return 0;
}
the GCC compiler prints a warning about initialization from incompatible pointer type
in the bar
function. The identical statement is fine in foo
.
It seems that the type of an agument of type va_list
is not a va_list
. This can be tested easily with a static assertion like
_Static_assert(sizeof(vl) == sizeof(va_list), "invalid type");
in the bar
function. With GCC, the _Static_assert
fails. The same can be tested also in C++ with declytpe
and std::is_same
.
I would like to take the address of the va_list vl
argument of bar
, and pass it as argument of bar_ptr
, to do thinks like the one described in this thread. On the other hand, it is fine to call bar_ptr(n, pvl)
directly from main
, replacing bar(n, vl)
.
According to the footnote 253 of the C11 final draft,
It is permitted to create a pointer to a
va_list
and pass that pointer to another function
Why this cannot be done if va_list
is defined as argument of the function, and not in the function body?
Workaround:
Even if this does not answer the question, a possible workaround is to change the content of bar
by using a local copy of the argument created with va_copy:
void bar(int n, va_list vl) {
va_list vl_copy;
va_copy(vl_copy, vl);
va_list *pvl = &vl_copy; // now fine here
bar_ptr(n, pvl);
va_end(va_copy);
}