2009-02-25 4 views
46

va_end - Macro pour réinitialiser arg_ptr.Qu'est-ce que va_end exactement? Est-il toujours nécessaire de l'appeler?

Après avoir accès à une liste d'arguments variable, le pointeur arg_ptr est remis à zéro habituellement avec va_end(). Je comprends que c'est nécessaire si vous voulez réitérer la liste, mais est-ce vraiment nécessaire si vous ne le faites pas? Est-ce juste une bonne pratique, comme la règle "toujours avoir un default: dans votre switch"?

+1

C'est une très bonne question. Je souhaite que quelqu'un y réponde en décrivant une architecture où va_end n'est pas un no-op. – erikkallen

+1

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

+0

@erikkallen: Faire une recherche google pour "define va_end" et vous trouverez quelques définitions inhabituelles qui peuvent ou non être essentiellement un op. – PlasmaHH

Répondre

40

va_end est utilisé pour effectuer le nettoyage. Vous ne voulez pas casser la pile, n'est-ce pas?

De man va_start:

va_end()

chaque invocation de va_start() doit être accompagnée d'une invocation correspondante de va_end() dans la même fonction. Après l'appel va_end (ap) la variable ap est indéfinie. Plusieurs traversées de la liste, chacune encadrée par va_start() et va_end() sont possibles. va_end() peut être une macro ou une fonction.

Notez la présence du mot doit.

La pile peut être corrompue car vous ne savez pas ce que va_start() fait. Les macros va_* sont censées être traitées comme des boîtes noires. Chaque compilateur sur chaque plate-forme peut faire ce qu'il veut là-bas. Cela peut ne rien faire ou faire beaucoup.

Certains ABI transmettent les premiers arguments dans les registres et le reste dans la pile. Un va_arg() il peut y avoir plus compliqué. Vous pouvez rechercher comment une implémentation donnée fait varargs, ce qui peut être intéressant, mais en écrivant du code portable, vous devriez les traiter comme des opérations opaques.

+0

Cela signifie-t-il que le pointeur est 'global' et lorsque la fonction est appelée une seconde fois sans réinitialiser le pointeur, la pile sera corrompue? – Yarik

+3

Il pourrait être corrompu parce que * vous ne savez pas ce que va_start() fait. * Il pourrait faire n'importe quoi. Et il doit être nettoyé. Par conséquent, lorsque vous appelez va_start(), vous devez * le faire correspondre avec va_end(). – greyfade

+0

Merci pour la clarification. – Yarik

10

Dans l'implémentation commune des "paramètres passés sur la pile", je crois que va_end() est généralement rien/vide/null. Cependant, sur les plateformes qui ont des schémas moins traditionnels, cela devient nécessaire. C'est une "bonne pratique" de l'inclure pour rester neutre sur la plateforme.

+0

Il pourrait réinitialiser la pile dans le cas où vous n'avez pas itéré sur tous les var_args. – Spidey

11

Sous Linux x86-64, une seule traversée peut être effectuée sur une variable va_list. Pour faire plus de traversées, il faut d'abord la copier en utilisant va_copy. man va_copy explique les détails:

va_copy()

Une implémentation évidente un va_list être un pointeur sur le cadre de la pile de la fonction variadique.Dans une telle configuration (de loin le plus commun), il semble rien contre une cession

va_list aq = ap; 

Malheureusement, il y a aussi des systèmes qui en font un tableau de pointeurs (de longueur 1), et on a besoin

va_list aq; 
    *aq = *ap; 

Enfin, sur les systèmes où les arguments sont passés dans des registres, il peut être nécessaire pour va_start() pour allouer de la mémoire, de stocker les arguments là, et aussi une indication dont l'argument est le suivant, de sorte que va_arg () peut étape à travers la liste. Maintenant va_end() peut à nouveau libérer la mémoire allouée . Pour faire face à cette situation, C99 ajoute une macro va_copy(), donc que la cession ci-dessus peut être remplacé par

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

chaque invocation de va_copy() doit être accompagnée d'une tion correspondante de invocation va_end() dans la même fonction. Certains systèmes qui ne fournissent pas va_copy() ont plutôt __va_copy, puisque c'est le nom utilisé dans la proposition de projet .

+2

Pour cliarfy ceci; rien n'est spécial pour Linux x86-64. 'va_copy' est requis si vous voulez parcourir deux fois une liste quand vous n'avez que la variable' va_list' disponible. (par exemple, à l'intérieur d'une fonction prenant 'va_list' comme argument). Vous pouvez toujours appeler 'va_start' et' va_end' autant que vous voulez. –

+0

@MattMcNabb Sur x86-64 'va_list' conserve l'état de traversée. Sur x86 ce n'est pas le cas. –

+0

@downvoter quel est le problème? –

Questions connexes