使用以下代码,va_arg将通过vProcessType为第二次和第三次传递返回垃圾。
// va_list_test.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <tchar.h>
#include <cstdarg>
#include <windows.h>
void processList(LPTSTR str, ...);
void vProcessList(LPTSTR str, va_list args);
void vProcessType(va_list args, int type);
int _tmain(int argc, _TCHAR* argv[])
{
LPTSTR a = TEXT("foobar");
int b = 1234;
LPTSTR c = TEXT("hello world");
processList(TEXT("foobar"), a, b, c);
return 0;
}
void processList(LPTSTR str, ...)
{
va_list args;
va_start(args, str);
vProcessList(str, args);
va_end(args);
}
void vProcessList(LPTSTR str, va_list args)
{
vProcessType(args, 1);
vProcessType(args, 2);
vProcessType(args, 1);
}
void vProcessType(va_list args, int type)
{
switch(type)
{
case 1:
{
LPTSTR str = va_arg(args, LPTSTR);
printf("%s", str);
}
break;
case 2:
{
int num = va_arg(args, int);
printf("%d", num);
}
break;
default:
break;
}
}
是否以这种方式传递va_list事件?在vProcessType中第一次调用va_arg会返回预期的字符串。第二次和第三次通过这个函数,它返回一个指向第一个字符串开头的指针,而不是预期的值。
如果我将va_arg调用提升到vProcessList,一切似乎都能正常工作。只有当我通过函数传递va_list时才会出现这种行为。
答案 0 :(得分:5)
您每次都va_list
传递vProcessType()
- 每次调用vProcessType()
,您都会对列表中的第一个va_arg
采取行动。
因此,在调用TEXT("foobar")
时,您始终会处理vProcessType()
参数。
另请注意,标准有关于将va_list
传递给另一个函数的说法:
对象
ap
[类型va_list
]可以作为参数传递给另一个函数;如果该函数调用带有参数va_arg
的{{1}}宏,则调用函数中ap
的值是不确定的,并且在进一步引用之前应传递给ap
宏到va_end
。
标准中的注释表示将指针传递给ap
是完美的,所以您可能要做的是va_list
指向vProcessType()
的指针:
va_list
答案 1 :(得分:4)
当您将va_list
对象传递给另一个函数并且另一个函数对相应参数使用va_arg
时,va_list
将不确定值控件返回时调用函数。您唯一可以做的是将va_end
应用于该va_list
对象。
这是标准(7.15 / 3)中的陈述方式
如果访问不同的参数是 希望,被叫函数应该 声明一个对象(通常称为 至本条款中的
ap
) 输入va_list
。对象ap
可能是 作为一个论点传递给另一个人 功能;如果该函数调用 参数va_arg
的{{1}}宏, 调用函数中ap
的值为 不确定并且应该被传递给 {I}之前的ap
宏 参考va_end
。
不要按值传递ap
个对象。如果您的意图是在每个后续子函数中继续参数解析,那么您必须通过指针传递va_list
对象。
如果您确实想要按值传递va_list
对象,即如果您希望每个子函数从同一点解析,则必须手动复制{{1使用va_list
宏提前对象。 (“提前”意味着在任何子功能有机会对其进行va_list
之前,您必须制作所需数量的副本。)
答案 2 :(得分:3)
您按价值传递了va_list
。尝试将指针传递给一个值(或者如果您只想要一个C ++版本,则可以使用引用):
void vProcessList(LPTSTR str, va_list args)
{
vProcessType(&args, 1);
vProcessType(&args, 2);
vProcessType(&args, 1);
}
void vProcessType(va_list *args, int type)
{
...
LPTSTR str = va_arg(*args, LPTSTR);
...
int num = va_arg(*args, int);
}
答案 3 :(得分:1)
正如其他人所说,C99标准允许程序通过va_list多次运行便携式(在C99实现中)方式。在C99之前的实现中没有任何好的可移植方法。当我需要多次运行printf列表时,我做了什么(对于“中心字符串”函数,必须先评估参数一次以确定它们的宽度,然后第二次实际显示它们)检查编译器供应商的“stdarg.h”并捏造我自己的必要功能实现。
如果想要一个可以在早期C编译器上运行的真正可移植的实现,并且如果事先知道所需的最大传递次数,我认为可以创建一个va_ptr对象的数组,在所有的上使用va_start它们,然后将数组传递给子例程。但是,排序icky。