简体   繁体   English

va_arg返回错误的参数

[英]va_arg returning the wrong argument

With the following code va_arg is returning garbage for the second and third pass through vProcessType. 使用以下代码,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;
    }
}

Is passing a va_list thing not allowed in this way? 是不是以这种方式传递了一个va_list的东西? The first call to va_arg inside vProcessType returns the expected string. 在vProcessType中第一次调用va_arg会返回预期的字符串。 The second and third time through this function it returns a pointer to the start of the first string, instead of the values expected. 第二次和第三次通过此函数返回指向第一个字符串开头的指针,而不是预期的值。

If I hoist the va_arg call to vProcessList, everything seems to work fine. 如果我将va_arg调用提升到vProcessList,一切似乎都能正常工作。 It only seems when I pass a va_list through a function that I'm getting this behaviour. 只有当我通过函数传递va_list时才会出现这种行为。

You're passing the same va_list each time to vProcessType() - in each call to vProcessType() you're acting on the first va_arg in the list. 您每次都将相同的va_list传递给vProcessType() - 在每次调用vProcessType()您将对列表中的第一个va_arg执行操作。

So you're always dealing with the TEXT("foobar") parameter when calling vProcessType() . 因此,在调用vProcessType()时,您始终处理TEXT("foobar")参数。

Also note that the standard has this to say about passing a va_list to another function: 另请注意,标准有关于将va_list传递给另一个函数的说法:

The object ap [of type va_list ] may be passed as an argument to another function; 对象ap [类型为va_list ]可以作为参数传递给另一个函数; if that function invokes the va_arg macro with parameter ap , the value of ap in the calling function is indeterminate and shall be passed to the va_end macro prior to any further reference to ap . 如果该函数使用参数ap调用va_arg宏,则调用函数中的ap值是不确定的,并且应该在进一步引用ap之前传递给va_end宏。

A foot note in the standard indicate that it's perfect OK to pass a pointer to a va_list , so what you might want to do is have vProcessType() take a pointer to the va_list : 标准中的脚注表明将指针传递给va_list是完美的,所以你可能要做的是让vProcessType()获取指向va_list的指针:

void vProcessType(va_list* pargs, int type);

When you pass the va_list object to another function and that another function uses va_arg on the corresponding parameter, that va_list will have indeterminate value in the calling function when the control returns. 当您将va_list对象传递给另一个函数并且另一个函数在相应参数上使用va_arg时,该控件返回时该va_list将在调用函数中具有不确定的值 The only thing you are allowed to do is to apply va_end to that va_list object. 您唯一可以做的是将va_end应用于该va_list对象。

This is how it is stated in the standard (7.15/3) 这就是标准中规定的方式(7.15 / 3)

If access to the varying arguments is desired, the called function shall declare an object (generally referred to as ap in this subclause) having type va_list . 如果需要访问变量参数,则被调用函数应声明类型为va_list的对象(在va_list条款中通常称为ap )。 The object ap may be passed as an argument to another function; 对象ap可以作为参数传递给另一个函数; if that function invokes the va_arg macro with parameter ap , the value of ap in the calling function is indeterminate and shall be passed to the va_end macro prior to any further reference to ap . 如果该函数使用参数ap调用va_arg宏,则调用函数中的ap值是不确定的,并且应该在进一步引用ap之前传递给va_end宏。

Don't pass va_list objects by value . 不要按值传递va_list对象。 If your intent is to continue argument parsing in each consequent sub-function, then you have to pass the va_list object by pointer . 如果您的目的是在每个后​​续子函数中继续进行参数解析,那么您必须通过指针传递va_list对象。

If you really want to pass your va_list object by value , ie if you want each sub-function to parse from the same point, you have to manually copy your va_list object in advance by using the va_copy macro. 如果您确实希望按值传递va_list对象,即如果希望每个子函数从同一点解析,则必须使用va_copy宏手动复制va_list对象。 ("In advance" means that you have to make as many copies as you'll need before any sub-function has a chance to do a va_arg on it.) (“提前”意味着在任何子功能有机会对其执行va_arg之前,您必须制作所需数量的副本。)

You're passing your va_list by value. 您按值传递了va_list Try passing a pointer to the one value instead (or if you wanted a C++ only version, you could use a reference): 尝试将指针传递给一个值(或者如果您只想要一个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);
}

As others have noted, the C99 standard allows a portable (among C99 implementations) way for a program to run through a va_list more than once. 正如其他人所指出的那样,C99标准允许程序通过va_list多次运行便携式(在C99实现中)方式。 There isn't any nice portable way to do that in pre-C99 implementations. 在C99之前的实现中没有任何好的可移植方法。 What I did when I needed to run through a printf list more than once (for a "center string" function which had to evaluate the arguments once to determine how wide they would be, and then a second time to actually display them) was to examine the compiler vendor's "stdarg.h" and fudge my own implementation of the necessary functionality. 当我需要多次运行printf列表时,我做了什么(对于“中心字符串”函数,必须先评估参数一次以确定它们的宽度,然后第二次实际显示它们)检查编译器供应商的“stdarg.h”并捏造我自己实现的必要功能。

If one wanted a really portable implementation that would work on earlier C compilers, and if one knew in advance the maximum number of passes that would be required, I think one could create an array of va_ptr objects, use va_start on all of them, and then pass the array to the child routine. 如果想要一个可以在早期C编译器上运行的真正可移植的实现,并且如果事先知道所需的最大传递次数,我认为可以创建一个va_ptr对象的数组,在所有这些对象上使用va_start,以及然后将数组传递给子例程。 Sorta icky, though. 但是,排序icky。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM