2014-09-03 11 views
6

Ho il codice seguente:L'ordine di più chiamate va_end è importante?

va_list va[2]; 
va_start(va[0], fmt); 
va_start(va[1], fmt); 
process(fmt, va); 
va_end(va[0]); 
va_end(va[1]); 

Ho guardato vari siti per la documentazione su va_start e va_end, e tutto quello che dicono è che va_end dovrebbe essere invocato per ogni va_start prima che la funzione restituisce la chiamata.

Ciò di cui non sono sicuro è se l'ordine delle chiamate è significativo o meno. In particolare, è

va_end(va[0]); 
va_end(va[1]); 

sementically identico a

va_end(va[1]); 
va_end(va[0]); 

nel codice di esempio sopra?

+1

Potresti anche essere interessato a 'va_copy'. –

risposta

1

Su alcuni [vecchi] implementazioni, va_start ampliato ad una parentesi graffa aperta { seguita da qualche dichiarazione, e va_end ampliato ad una parentesi destra } eventualmente preceduta da qualche "finalizzazione". Moralmente dovrebbero corrispondere. In pratica, spesso ma non sempre, l'ordine non importa molto (ma in linea di principio importa).

sui recenti GCC, questi va_start e va_end macro si espandono alle invocazioni di __builtin_va_start & __builtin_va_end in modo che il compilatore può cura (forse in qualche versione futura) che questi sono correttamente nidificato. Vedi this. Così l'ordine "buona" dovrebbe essere:

va_list va[2]; 
va_start(va[0], fmt); 
    va_start(va[1], fmt); 
    process(fmt, va); 
    va_end(va[1]); 
va_end(va[0]); 

In pratica l'ordine di va_end potrebbe non importa più di tanto.

Il rientro è mio sottolineare che va_start & va_end sono "Raggruppamento"

Naturalmente, è necessario chiamare va_arg per recuperare veramente argomenti variadic (spero che il vostro process che sta facendo). stdarg(3) spiega che ben (codice C):

Ciascuna invocazione va_start() deve corrispondere un corrispondente invocazione va_end() nella stessa funzione.

Avviso del corrispondente parola (corsivo è mio). Credo che significhi che va_start e va_end sono in realtà nidificazione (almeno in linea di principio).

+0

IIRC, non è stato eseguito '' va_start' '' di implementazione. Alcune (pre-standard) implementazioni '' hanno fatto, ma C standard e C++ standard non hanno mai avuto un'intestazione ''. – hvd

+0

concordato. Ma le implementazioni potrebbero fare cose strane. –

+0

A meno che uno standard non richieda uno specifico ordine di invocazioni 'va_end', no, le implementazioni non possono fare cose strane. – hvd

3

L'unico requisito rilevante nello standard C99 è:

7.15.1 lista degli argomenti accesso variabili macro

1 [...] Ogni invocazione dei va_start e va_copy macro sarà abbinato da una corrispondente chiamata della macro va_end nella stessa funzione.

Non v'è alcun obbligo per l'ordine di molteplici va_end invocazioni a corrispondere a quella del va_start, o per abbinare il contrario di quello di va_start, così implementazioni sono tenuti ad accettare qualunque ordine.

Si potrebbe anche usare un orribile pasticcio come

void f(int a, ...) { 
    va_list ap; 
    goto b; 
a: 
    va_end(ap); 
    return; 
b: 
    va_start(ap, a); 
    goto a; 
} 

Questo corrisponde a tutti i requisiti della norma, in modo le implementazioni devono accettarlo. Di conseguenza, anche i punti in cui va_end si espande a qualcosa con parentesi non corrispondenti non sono consentiti.

In pratica, non sono nemmeno a conoscenza di alcuna implementazione corrente in cui va_end ha alcun effetto necessario. Tutte le implementazioni che sono stato in grado di trovare, al massimo impostano il valore (o il primo sotto-valore, a seconda del tipo) su zero, che farebbe un ulteriore uso di va_arg fallire, ma non causerebbe problemi se si omette va_end dal tuo codice. La maggior parte non lo fa nemmeno. Ora, in realtà non lo rimuoverò dal codice, poiché ci sono motivi legittimi per cui un'implementazione (attuale o futura) potrebbe effettivamente fare qualcosa nel suo va_end, ma si può presumere che le implementazioni attuali e future tenteranno almeno di implementarla in un modo che corrisponda ai requisiti dello standard.

Le implementazioni storiche che utilizzano #define va_end(ap) } sono proprio questo: cronologia. Non hanno fornito quella macro in <stdarg.h> e non avevano nemmeno un'intestazione <stdarg.h>. Non dovresti preoccuparti di loro.

+0

Ok, ma questa è solo la metà della risposta. Molte implementazioni fanno cose che non sono "permesse" di fare. Ce ne sono qui? –

+0

@LightnessRacesinOrbit Conosco alcune implementazioni pre-C89 con '' necessario 'va_start' e' va_end' per corrispondere lessicalmente, ma non sono a conoscenza di alcun problema su qualsiasi implementazione di ''. Inoltre, in generale, "lo standard dice che va bene", se supportato, è sufficiente come risposta. Certo, ci sono implementazioni che hanno dei bug, e se qualche compilatore mainstream mostra questi bug è bello menzionarli in una risposta, ma per me è scontato quello che lo standard dice semplicemente a causa di ipotetiche implementazioni di bug. – hvd

+0

@LightnessRacesinOrbit E in realtà, ho cercato di recente implementazioni in cui 'va_end' ha fatto * qualsiasi cosa * con il suo argomento che avrebbe causato problemi se fossero stati omessi' va_end', ma non sono stato in grado di trovare alcun esempio. Quindi, in pratica, penso che i requisiti dello standard siano più rigidi di quelli di cui le implementazioni hanno effettivamente bisogno. – hvd

Problemi correlati