I have a concern about variadic functions under Linux amd64 (x86_64).
My example build and work fine on linux i386 (ia32), but when built for linux amd64, GCC produces such errors:
stdarg.c: In function ‘vtest’:
stdarg.c:21:5: attention : passing argument 2 of ‘vptest’ from incompatible pointer type [enabled by default]
stdarg.c:5:1: note: expected ‘struct __va_list_tag (*)[1]’ but argument is of type ‘struct __va_list_tag **’
Here the example:
#include <stdio.h>
#include <stdarg.h>
static int
vptest(int count, va_list *a)
{
printf("%8s: a = %p\n", __func__, a);
printf("%8s: %d: %d\n", __func__, count, va_arg(*a, int));
return 0;
}
static int
vtest(int count, va_list ap)
{
printf("%8s: &ap = %p\n", __func__, &ap);
/* passing a pointer to ap allows ap to be used again in the calling function */
for(; count > 1; count --) {
vptest(count, &ap);
}
if (count) {
printf("%8s: %d: %d\n", __func__, count, va_arg(ap, int));
}
return 0;
}
static
int test(int count, ...)
{
va_list ap;
va_start(ap, count);
printf("%8s: &ap = %p\n", __func__, &ap);
/* after passing ap to subfunction, this function must not use ap again without calling va_start */
vtest(count, ap);
va_end(ap);
return 0;
}
int
main(void)
{
test(4,
1, 2, 3, 4);
return 0;
}
According to a C11 draft (ISO/IEC 9899:2011)
The object ap may be passed as an argument to another function; if that function invokes the va_arg macro with parameter ap, the value of ap in the calling function is indeterminate and shall be passed to the va_end macro prior to any further reference to ap.
But latter add
It is permitted to create a pointer to a va_list and pass that pointer to another function, in which case the original function may make further use of the original list after the other function returns.
It's not clear to me if the AMD 64 ABI is wrong here regarded to the standard.
Changing the function vtest()
to use a pointer at first call fixes the problem, but it feels wrong to have something not working in inner functions actually works in outer function.
@@ -12,16 +12,16 @@
}
static int
-vtest(int count, va_list ap)
+vtest(int count, va_list *a)
{
- printf("%8s: &ap = %p\n", __func__, &ap);
+ printf("%8s: a = %p\n", __func__, a);
/* passing a pointer to ap allows ap to be used again in the calling function */
for(; count > 1; count --) {
- vptest(count, &ap);
+ vptest(count, a);
}
if (count) {
- printf("%8s: %d: %d\n", __func__, count, va_arg(ap, int));
+ printf("%8s: %d: %d\n", __func__, count, va_arg(*a, int));
}
return 0;
@@ -37,7 +37,7 @@
printf("%8s: &ap = %p\n", __func__, &ap);
/* after passing ap to subfunction, this function must not use ap again without calling va_start */
- vtest(count, ap);
+ vtest(count, &ap);
va_end(ap);
If someone could find somewhere if AMD64 ABI behavor is matching the standard. Additional points for people who provide me others ABI with the (same) problem on stdarg usage.
Regards