Il codice originale non riesce perché tenta di utilizzare printf()
dove è necessario utilizzare vprintf()
. Prendendo punti discutibili come le dichiarazioni logOpen
e logClose
al valore nominale (dato la notazione, presumibilmente sono le macro che si aprono e chiudono il flusso flog
file), il codice dovrebbe essere:
void logPrintf(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
logOpen;
vfprintf(flog, fmt, ap);
logClose;
va_end(ap);
va_list ap2;
va_start(ap2, fmt);
vprintf(fmt, ap2);
va_end(ap2);
}
Non c'è alcun requisito particolare da usare due variabili separate va_list
; è perfettamente OK usare lo stesso due volte finché si utilizza va_end()
prima di utilizzare nuovamente va_start()
.
void logPrintf(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
logOpen;
vfprintf(flog, fmt, ap);
logClose;
va_end(ap);
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
}
Quando un valore va_list
si passa ad un'altra funzione (vfprintf()
e vprintf()
in questo codice), si deve presumere che non è più utilizzabile nella funzione attuale. Su di esso è sicuro chiamare lo va_end()
.
In questo codice non è necessario va_copy()
. Funziona, ma non è necessario. È necessario va_copy()
in altre circostanze, ad esempio quando la funzione viene passata una va_list
ed è necessario elaborare l'elenco due volte:
void logVprintf(const char *fmt, va_list args1)
{
va_list args2;
va_copy(args2, args1);
logOpen;
vfprintf(flog, fmt, args1);
logClose;
vprintf(fmt, args2);
va_end(args2);
}
Si noti che in questo codice, è responsabilità del codice chiamante per chiamare va_end()
su args1
. Infatti, lo standard dice:
ogni chiamata va_start
e va_copy
macro sono abbinati da una corrispondente invocazione del va_end
macro nella stessa funzione.
Poiché la funzione logVprintf()
non chiama né va_start
o va_copy
inizializzare args1
, non può legittimamente chiamare va_end
su args1
. D'altra parte, lo standard richiede di chiamare va_end
per args2
.
La funzione logPrintf()
può essere implementata in termini di logVprintf()
ora:
void logPrintf(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
logVprintf(fmt, args);
va_end(args);
}
Tale struttura - una funzione operativa che accetta un va_list
e una funzione di copertura che richiederà ellissi (argomenti variabili) e li passa al operativa funzione dopo la conversione in un va_list
- è spesso un buon modo di lavorare. Prima o poi, di solito trovi la necessità della versione con un argomento va_list
.
È necessario utilizzare vprintf la seconda volta, non printf. –