[英]va_arg returning the wrong argument
使用以下代碼,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時才會出現這種行為。
您每次都將相同的va_list
傳遞給vProcessType()
- 在每次調用vProcessType()
您將對列表中的第一個va_arg
執行操作。
因此,在調用vProcessType()
時,您始終處理TEXT("foobar")
參數。
另請注意,標准有關於將va_list
傳遞給另一個函數的說法:
對象
ap
[類型為va_list
]可以作為參數傳遞給另一個函數; 如果該函數使用參數ap
調用va_arg
宏,則調用函數中的ap
值是不確定的,並且應該在進一步引用ap
之前傳遞給va_end
宏。
標准中的腳注表明將指針傳遞給va_list
是完美的,所以你可能要做的是讓vProcessType()
獲取指向va_list
的指針:
void vProcessType(va_list* pargs, int type);
當您將va_list
對象傳遞給另一個函數並且另一個函數在相應參數上使用va_arg
時,該控件返回時該va_list
將在調用函數中具有不確定的值 。 您唯一可以做的是將va_end
應用於該va_list
對象。
這就是標准中規定的方式(7.15 / 3)
如果需要訪問變量參數,則被調用函數應聲明類型為
va_list
的對象(在va_list
條款中通常稱為ap
)。 對象ap
可以作為參數傳遞給另一個函數; 如果該函數使用參數ap
調用va_arg
宏,則調用函數中的ap
值是不確定的,並且應該在進一步引用ap
之前傳遞給va_end
宏。
不要按值傳遞va_list
對象。 如果您的目的是在每個后續子函數中繼續進行參數解析,那么您必須通過指針傳遞va_list
對象。
如果您確實希望按值傳遞va_list
對象,即如果希望每個子函數從同一點解析,則必須使用va_copy
宏手動復制va_list
對象。 (“提前”意味着在任何子功能有機會對其執行va_arg
之前,您必須制作所需數量的副本。)
您按值傳遞了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);
}
正如其他人所指出的那樣,C99標准允許程序通過va_list多次運行便攜式(在C99實現中)方式。 在C99之前的實現中沒有任何好的可移植方法。 當我需要多次運行printf列表時,我做了什么(對於“中心字符串”函數,必須先評估參數一次以確定它們的寬度,然后第二次實際顯示它們)檢查編譯器供應商的“stdarg.h”並捏造我自己實現的必要功能。
如果想要一個可以在早期C編譯器上運行的真正可移植的實現,並且如果事先知道所需的最大傳遞次數,我認為可以創建一個va_ptr對象的數組,在所有這些對象上使用va_start,以及然后將數組傳遞給子例程。 但是,排序icky。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.