簡體   English   中英

在我的函數和類似 vprintf 的庫函數之間共享 va_list

[英]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' (可能以某種扭曲的方式,因為charint窄)。

不幸的是,我不能讓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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM