逐个处理va_list

时间:2016-11-03 14:09:29

标签: c++ variadic-functions

以下程序适用于64位Linux计算机,但在32位Linux计算机上崩溃。

#include <cstdarg>
#include <iostream>

void proc_int(std::va_list va)
{
    std::cout << va_arg(va, int);
}

void proc_str(std::va_list va)
{
    std::cout << va_arg(va, const char*);
}

void outputv(std::va_list va)
{
    proc_int(va);
    std::cout << " ";
    proc_str(va);
    std::cout << "\n";
}

void output(int dummy, ...)
{
    va_list va;
    va_start(va, dummy);
    outputv(va);
    va_end(va);
}

int main()
{
    output(0, 42, "hello");
}

我认为这是因为va_list在32位上是char*而在64位上是struct __va_list_tag[1]。我可以进行哪些更改以使此程序可移植,最好不要更改outputv的签名?

3 个答案:

答案 0 :(得分:3)

来自cppreference

  

如果创建va_list实例,传递给另一个函数,并在该函数中通过va_arg使用,那么在调用函数的任何后续使用之前都应该调用{{1} }

不完全清楚(后续使用)是否包括传递给另一个功能,但它肯定是合理的。

检查本地(Linux)手册页以进行比较:

  

如果将va_end [va_list] 传递给使用ap的函数,那么在返回该函数后,ap的值是未定义的

所以你根本不允许传递va_arg(ap,type)并按照你的方式使用它,32位版本恰好可以逃脱它。

  

我可以进行哪些更改以使该程序可移植,最好不更改outputv的签名?

好吧,只是不要将va_list传递给其他函数,并希望它之后仍能正常工作:

va_list

答案 1 :(得分:1)

无论va_list是否为typedefed都是一个数组,这应该有效:

#include <cstdarg>
#include <iostream>
#include <type_traits>

using my_va_list = std::decay<std::va_list>::type;

void proc_int(my_va_list &va)
{
    std::cout << va_arg(va, int);
}

void proc_str(my_va_list &va)
{
    std::cout << va_arg(va, const char*);
}

void outputv(std::va_list va)
{
    my_va_list mva = va;
    proc_int(mva);
    std::cout << " ";
    proc_str(mva);
    std::cout << "\n";
}

不保证可以与所有实现一起使用,但至少在gcc,clang和vc ++下它可以在32位和64位模式下工作。

答案 2 :(得分:0)

根据C标准(其varargs功能直接包含在C ++中),C99,第7.15节第3段(强调我的):

  

声明的类型是va_list,它是适合的对象类型   保存宏va_startva_arg所需的信息,   va_endva_copy。如果访问不同的参数是   希望,被调用的函数应声明一个对象(通常   在本子条款中称为ap,类型为va_list。的的   对象ap可以作为参数传递给另一个函数;如果说   函数使用参数va_arg调用ap宏,值为   调用函数中的ap是不确定的,应该传递给   在进一步引用va_end之前的ap宏。 212)

由于您正在将va_list传递给函数并且在其中使用va_arg,因此在该函数返回后您无法重用va_list。并且“212)”注释给出了解决方案:

  

212)允许创建指向va_list的指针并传递它   指向另一个函数的指针,在这种情况下原始函数可以   在其他功能之后进一步使用原始列表   回报。