[英]Sharing va_list between my functions and vprintf-like library functions
我想對格式化打印(使用可變參數)添加一些特殊處理,我發現我的代碼中使用va_list
不符合 C 標准。 雖然它在使用 GCC 編譯時不會導致任何錯誤,但我希望它是兼容的。 以下是簡化版:
#include <stdarg.h>
#include <stdio.h>
typedef struct S_ {
int i;
va_list *v;
} S;
void fb( S* s )
{
int x = va_arg( *s->v, int ); // Line A
vfprintf( stdout, "c:%c s:%s\n", *s->v ); // Line B
int y = va_arg( *s->v, int ); // Line C
fprintf( stdout, "d:%d\n", s->i * x * y );
}
void fa( int i, ... )
{
S s = { i };
va_list v;
va_start( v, i );
s.v = &v;
fb( &s );
va_end( v );
}
void main()
{
fa( 1, 2, 'c', "string", 3 );
}
該標准在第 249 頁上說:
如果需要訪問變化的 arguments,被調用的 function 應聲明一個類型為 va_list 的 object(在本節中通常稱為 ap)。 object ap 可以作為參數傳遞給另一個 function; 如果 function 調用帶有參數 ap 的 va_arg 宏,則調用 function 中 ap 的值是不確定的,應在進一步引用 ap 之前傳遞給 va_end 宏。 (允許創建指向 va_list 的指針並將該指針傳遞給另一個 function,在這種情況下,原始 function 可以在其他 function 返回后進一步使用原始列表。)
據我了解,如果 A 行后跟 B 行沒有問題,因為調用者 ( fb
) 在調用被調用者 ( vfprintf
) 之前使用va_arg
。 我想(雖然沒有在文本中找到它)允許這樣做的原因是va_list
在調用者的 scope 中,因此va_arg
所做的任何更改在傳遞給被調用者時都會反映在對象的 state 中( vfprintf
)。
但是,我認為 B 行后跟 C 行不符合標准,因為被調用方( vfprintf
)收到的是va_list
而不是va_list*
,因此*s->v
的值在 B 行之后是不確定的,應該不能像第 C 行那樣使用(而是應該用va_end
關閉)。 我想(雖然沒有在文本中找到它)在被調用者( vfprintf
)返回后不允許在調用者( fb
)中使用va_list
的原因是*s->v
可能已經(取決於實現)復制到 vfprintf 的局部變量(在vfprintf
調用中),在這種情況下,由於該副本在vfprintf
返回時被銷毀,因此它所做的更改(消耗一個char
和一個char*
)不會反映在原始 object 中,因此y
不會被分配3
而是'c'
(可能以某種扭曲的方式,因為char
比int
窄)。
不幸的是,我不能讓vfprintf
接受va_list*
但需要“混合它”(可能在使用相同的va_arg
調用它之前和之后調用va_list
)。
我的論點是否正確,我的代碼是否不正確? 如果是這樣,我該如何解決?
請勿分享:復制:
typedef struct S{
int i;
va_list v;
} S;
void fb( S* s )
{
int x = va_arg( s->v, int ); // Line A
vfprintf( stdout, "c:%c s:%s\n", s->v ); // Line B
int y = va_arg( s->v, int ); // Line C
fprintf( stdout, "d:%d\n", s->i * x * y );
va_end(s -> v);
}
void fa( int i, ... )
{
S s = { i };
va_list v;
va_start( v, i );
va_copy(s.v, v);
fb( &s );
va_end( v );
}
你的推理是正確的,你的代碼是不正確的。 現在,它可能恰好在某些架構上工作而在某些架構上不工作。 例如,如果架構將va_list
實現為指向底層 state 的指針,那么當它傳遞給 function 並且 function 更新迭代 state 時,這可能在調用 scope 中可見,然后它會為您繼續迭代工作那里。 但是,如果體系結構將其實現為某種結構,其迭代值為 state,那么當它傳遞給 function 並且 function 更新迭代 state 時,在調用 scope 中將不可見,因此您的代碼將無法工作. 這就是未定義行為所發生的情況——無法保證它有效或無效。
除了基本上重新實現vfprintf()
之外,我沒有看到實現此目的的一般方法,而是在va_list *
而不是va_list
上進行操作。 一旦按值將va_list
傳遞給 function,就不能再在調用 scope 時對該va_list
執行va_arg
。 在 C 標准的vfprintf()
部分中,有一個腳注證實了這一點:
由於函數vfprintf、vfscanf、vprintf、vscanf、vsnprintf、vsprintf和vsscanf調用了va_arg宏,返回后arg的值是不確定的。
va_copy()
在這里沒有幫助。 您不能在調用va_copy()
之后調用vfprintf()
,因為va_list
當時已經不確定。 如果在調用va_copy()
之前使用vfprintf()
,則在讀取用於vfprintf()
的 arguments 之前,復制的va_list
仍將位於 state 中。 因此,您需要以某種方式“跳過”用於vfprintf()
,以便在那些 arguments 之后獲取參數。為此,您需要知道用於vfprintf()
及其類型。 所以基本上你需要重新實現vfprintf()
的格式字符串解析邏輯來做到這一點。 (如果你打算這樣做,你也可以重新實現vfprintf()
來獲取va_list *
。)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.