va_arg返回错误的参数

时间:2010-07-02 18:01:53

标签: c++ c variadic-functions

使用以下代码,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时才会出现这种行为。

4 个答案:

答案 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。