如何以嵌套方式使用va_list对象,尤其是在gcc x64编译器上

时间:2014-10-27 15:03:56

标签: linux gcc g++ glibc

请查看我的小型C / C ++程序。

// 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;
}

在openSUSE 13.1 x86上,它输出的只是我想要的(无论使用gcc还是g ++),

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

但是,它在openSUSE 13.1 x64机器上使用x86_64编译器失败。

如果我用gcc编译它,它编译好,但输出是意外的。

$ gcc -o vac va_nest.c

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

如果我用g++编译它,它甚至无法编译。

$ 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和g ++版本是4.8.1

有人可以帮我吗?我希望在x64上获得与Linux x86编译器相同的结果。

2 个答案:

答案 0 :(得分:4)

请勿尝试使用va_list聪明。相反,使用这样的代码:

nest2(fmt, args1);

(即确保所有函数使用隐藏在va_list后面的相同结构)。然后你可以像这样定义第二个函数:

void nest2(const char *fmt, va_list args1)

同时确保您完全致电va_end() 。嵌套将无法可靠地工作(即如果它有效,那就比其他任何东西更幸运了。)

如有疑问,请查看GLIBC的源代码,尤其是fprintf.cvfprintf.c

int
__fprintf (FILE *stream, const char *format, ...)
{
  va_list arg;
  int done;

  va_start (arg, format);
  done = vfprintf (stream, format, arg);
  va_end (arg);

  return done;
}

以后:

int
vfprintf (FILE *s, const CHAR_T *format, va_list ap)
{

答案 1 :(得分:1)

我意识到传递va_list对象的地址(而不是对象本身)是可行的,在linux gcc和MSVC 2008(32位和64位编译器)上验证。

// 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;
}