Search code examples
linuxgccg++glibc

How to use va_list object in a nested way, especially on gcc x64 compiler


Please look through my small C/C++ program.

// va_nest.c
#include <stdarg.h>
#include <stdio.h>

void nest2(const char *fmt, ...)
{
    va_list args2, args_dig;
    int *pi, i;

    va_start(args2, fmt);

    args_dig = va_arg(args2, va_list); 
        // !!! try to fetch the nested va_list object.
    pi = va_arg(args_dig, int*);
    i = va_arg(args_dig, int);

    printf("Inner: @%p , %d\n", pi, i);

    va_end(args2);
}

void nest1(const char *fmt, ...)
{
    va_list args1;
    va_start(args1, fmt);

    nest2(fmt, args1);

    va_end(args1);
}

int main()
{
    int var = 11;
    printf("Outer: @%p , %d\n", &var, var);
    nest1("abc", &var, var);
    return 0;
}

On openSUSE 13.1 x86, it outputs just want I expect(no matter using gcc or g++),

Outer: @0xbfa18d8c , 11
Inner: @0xbfa18d8c , 11

However, it FAILS with x86_64 compiler on openSUSE 13.1 x64 machine.

If I compile it with gcc, it compiles ok, but output is unexpected.

$ gcc -o vac va_nest.c

$ ./vac
Outer: @0x7fffb20f766c , 11
Inner: @0x1b01000000636261 , 4209411

If I compile it with g++, it even fails to compile.

$ g++ -o vacpp va_nest.c
va_nest.c: In function ‘void nest2(const char*, ...)’:
va_nest.c:12:11: error: invalid array assignment
  args_dig = va_arg(args2, va_list);
           ^

gcc and g++ version is 4.8.1

Can anybody help me with that? I hope to achieve the same result on x64 as in Linux x86 compiler.


Solution

  • I realize passing va_list object's address(instead of object itself) is feasible, verified on linux gcc and MSVC 2008(32-bit & 64-bit compiler).

    // va_nesta.c
    #include <stdarg.h>
    #include <stdio.h>
    #include <assert.h>
    
    void nest2(const char *fmt, ...)
    {
        va_list args2, *args_dig;
        int *pi, i;
    
        va_start(args2, fmt);
    
        args_dig = va_arg(args2, va_list*); 
            // !!! ★ try to fetch the nested va_list object's address.
        pi = va_arg(*args_dig, int*);
        i = va_arg(*args_dig, int);
    
        printf("Inner: @%p , %d\n", pi, i);
    
        va_end(args2);
    }
    
    void nest1(const char *fmt, ...)
    {
        va_list args1;
        va_start(args1, fmt);
    
        nest2(fmt, &args1); // ★use va_list object's address!
    
        va_end(args1);
    }
    
    int main()
    {
        int var = 11;
        printf("Outer: @%p , %d\n", &var, var);
        nest1("abc", &var, var);
    
    
        return 0;
    }