以下程序适用于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
的签名?
答案 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_start
,va_arg
所需的信息,va_end
和va_copy
。如果访问不同的参数是 希望,被调用的函数应声明一个对象(通常 在本子条款中称为ap
,类型为va_list
。的的 对象ap
可以作为参数传递给另一个函数;如果说 函数使用参数va_arg
调用ap
宏,值为 调用函数中的ap
是不确定的,应该传递给 在进一步引用va_end
之前的ap
宏。 212)
由于您正在将va_list
传递给函数并且在其中使用va_arg
,因此在该函数返回后您无法重用va_list
。并且“212)”注释给出了解决方案:
212)允许创建指向
va_list
的指针并传递它 指向另一个函数的指针,在这种情况下原始函数可以 在其他功能之后进一步使用原始列表 回报。