【问题标题】:How to use va_list object in a nested way, especially on gcc x64 compiler如何以嵌套方式使用 va_list 对象,尤其是在 gcc x64 编译器上
【发布时间】:2014-10-27 15:03:55
【问题描述】:

请看一下我的小 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 编译器中相同的结果。

【问题讨论】:

    标签: linux gcc g++ glibc


    【解决方案1】:

    不要试图对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)
    {
    

    【讨论】:

      【解决方案2】:

      我意识到传递 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;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-09
        • 1970-01-01
        • 2023-03-16
        • 1970-01-01
        • 2019-08-01
        • 1970-01-01
        相关资源
        最近更新 更多