2009-02-25 6 views
46

va_end - Macro da ripristinare arg_ptr.Che cosa è esattamente va_end? È sempre necessario chiamarlo?

Dopo l'accesso a un elenco di argomenti variabili, il puntatore arg_ptr di solito è ripristinato con va_end(). Capisco che sia necessario se vuoi ripetere l'elenco, ma è davvero necessario se non lo fai? È solo una buona pratica, come la regola "avere sempre un default: nel tuo switch"?

+1

È davvero una buona domanda. Vorrei che qualcuno rispondesse descrivendo un'architettura in cui va_end non è un no-op. – erikkallen

+1

FYI: MSVS2008 - #define _crt_va_end (ap) (ap = (va_list) 0) – Yarik

+0

@erikkallen: fai una ricerca su google per "define va_end" e troverai alcune definizioni insolite che potrebbero o meno essere essenzialmente no- operazione. – PlasmaHH

risposta

40

va_end viene utilizzato per eseguire la pulizia. Non vuoi distruggere lo stack, vero?

Da man va_start:

va_end()

ogni chiamata va_start() deve essere accompagnata da una corrispondente invocazione di va_end() nella stessa funzione. Dopo la chiamata va_end (ap) la variabile ap non è definita. Sono possibili più attraversamenti dell'elenco, ciascuno tra parentesi con va_start() e va_end(). va_end() può essere una macro o una funzione.

Nota la presenza della parola deve.

Lo stack potrebbe essere danneggiato perché non si sa cosa sta facendo va_start(). I macro va_* sono pensati per essere trattati come scatole nere. Ogni compilatore su ogni piattaforma può fare tutto ciò che vuole lì. Potrebbe non fare nulla, o potrebbe fare molto.

Alcune ABI passano i primi pochi arg in registri e il resto in pila. A va_arg() ci potrebbe essere più complicato. Puoi cercare in che modo una determinata implementazione fa vararg, che può essere interessante, ma scrivendo codice portatile dovresti trattarli come operazioni opache.

+0

Significa che il puntatore è "globale" e quando la funzione viene chiamata una seconda volta senza reimpostare il puntatore, lo stack si corrompe? – Yarik

+3

Potrebbe essere danneggiato perché * non si sa cosa sta facendo va_start(). * Potrebbe fare qualsiasi cosa. E ha bisogno di essere ripulito. Pertanto, quando chiami va_start(), * DEVE * corrispondere con va_end(). – greyfade

+0

Grazie per il chiarimento. – Yarik

10

Nell'implementazione comune "parametri passati nello stack", credo che va_end() di solito non è nulla/vuoto/null. Tuttavia, su piattaforme che hanno schemi meno tradizionali, diventa necessario. È una "buona pratica" includerla per rimanere neutrale rispetto alla piattaforma.

+0

Potrebbe reimpostare lo stack nel caso in cui non sia stato eseguito iterazione su tutti var_args. – Spidey

11

Su Linux x86-64 è possibile eseguire solo una traversata su una variabile va_list. Per fare più attraversamenti è necessario prima copiarlo usando va_copy. man va_copy spiega i dettagli:

va_copy()

Un ovvio attuazione avrebbe un va_list essere un puntatore alla cornice pila della funzione variadic.In una tale configurazione (di gran lunga la più comune) non sembra nulla contro un'assegnazione

va_list aq = ap; 

Purtroppo, ci sono anche sistemi che ne fanno un array di puntatori (di lunghezza 1), e non si ha la necessità

va_list aq; 
    *aq = *ap; 

Infine, nei sistemi in cui sono passati argomenti nei registri, può essere necessaria per va_start() per allocare la memoria, memorizzare gli argomenti lì, e anche un'indicazione di cui argomento è prossimo, in modo che va_arg () può fare il passo attraverso la lista. Ora va_end() può liberare nuovamente la memoria allocata . Per soddisfare questa situazione, C99 aggiunge una va_copy macro(), in modo che l'assegnazione sopra può essere sostituito da

va_list aq; 
    va_copy(aq, ap); 
    ... 
    va_end(aq); 

ogni chiamata va_copy() deve essere accompagnata da una corrispondente invoca- zione di va_end() nella stessa funzione. Alcuni sistemi che non forniscono va_copy() hanno invece __va_copy, poiché quello era il nome utilizzato nella proposta di bozza .

+2

Per chiarire questo; niente è speciale per Linux x86-64. 'va_copy' è richiesto se si desidera ripetere l'iterazione due volte su un elenco quando è disponibile la variabile' va_list'. (ad esempio all'interno di una funzione che assume 'va_list' come argomento). Puoi sempre chiamare 'va_start' e' va_end' quanto vuoi. –

+0

@MattMcNabb Su x86-64 'va_list' mantiene lo stato di attraversamento. Su x86 no. –

+0

@downvoter cosa c'è che non va? –