9 Stimmen

Spielt die Reihenfolge mehrerer va_end-Aufrufe eine Rolle?

Ich habe den folgenden Code:

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]);

Ich habe auf verschiedenen Websites nach Dokumentationen zu va_start und va_end gesucht, und alles, was sie sagen, ist, dass va_end für jedes va_start aufgerufen werden sollte, bevor die aufrufende Funktion zurückkehrt.

Was ich mir nicht sicher bin, ist, ob die Reihenfolge der Aufrufe signifikant ist. Insbesondere ist

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

semantisch identisch mit

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

im obigen Beispielcode?

6voto

Die einzige relevante Anforderung im C99-Standard lautet:

7.15.1 Zugriffsmakros für Variablengeradenliste

1 [...] Jeder Aufruf der Makros va_start und va_copy muss durch einen entsprechenden Aufruf des Makros va_end in derselben Funktion abgestimmt werden.

Es gibt keine Anforderung für die Reihenfolge mehrerer va_end-Aufrufe, die mit der von va_start übereinstimmen muss, oder umgekehrt, so dass Implementierungen beide Reihenfolgen akzeptieren müssen.

Sie könnten sogar ein fürchterliches Durcheinander wie folgt verwenden

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

Dies entspricht allen Anforderungen des Standards, daher müssen es Implementierungen akzeptieren. Infolgedessen sind nicht einmal Tricks erlaubt, bei denen va_end etwas mit nicht übereinstimmenden Klammern erweitert.

In der Praxis bin ich nicht einmal davon bewusst, dass irgendeine aktuelle Implementierung hat, bei der va_end überhaupt notwendigen Effekt hat. Alle Implementierungen, die ich finden konnte, setzen höchstens den Wert (oder den ersten Teilwert, abhängig vom Typ) auf null, was dazu führen würde, dass eine weitere Verwendung von va_arg fehlschlägt, aber keine Probleme verursachen würde, wenn Sie va_end aus Ihrem Code weglassen. Die meisten tun nicht einmal das. Jetzt würde ich es tatsächlich nicht aus dem Code entfernen, da es legitime Gründe gibt, warum eine Implementierung (aktuell oder zukünftig) tatsächlich etwas in ihrem va_end tun könnte, aber Sie können davon ausgehen, dass aktuelle und zukünftige Implementierungen zumindest versuchen werden, es so zu implementieren, dass es den Anforderungen des Standards entspricht.

Die historischen Implementierungen, die #define va_end(ap) } verwenden, gehören der Geschichte an. Sie haben dieses Makro nicht in bereitgestellt und hatten nicht einmal einen -Header. Sie sollten sich keine Sorgen darüber machen.

2voto

Kurt Punkte 297

Rufen Sie va_end einfach einmal für jeden va_start auf, aber Sie müssen va_arg verwenden, um die einzelnen Argumente zu erfassen. Hier ist ein Beispiel: http://www.cplusplus.com/reference/cstdarg/va_start/

Außerdem glaube ich nicht, dass die Reihenfolge eine Rolle spielt.

1voto

Auf einigen [alten] Implementierungen wurde va_start zu einer öffnenden geschweiften Klammer { gefolgt von einer Deklaration, und va_end zu einer schließenden geschweiften Klammer }, möglicherweise gefolgt von einer "Finalisierung", erweitert. Moralisch sollten sie zueinander passen. In der Praxis ist die Reihenfolge oft, aber nicht immer, nicht so wichtig (aber prinzipiell ist sie wichtig).

Auf neueren GCC, erweitern sich diese Makros va_start und va_end zu Aufrufen von __builtin_va_start & __builtin_va_end, so dass der Compiler beachten könnte (vielleicht in einer zukünftigen Version), dass diese korrekt verschachtelt sind. Siehe hier. Also sollte die "richtige" Reihenfolge sein:

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 der Praxis könnte die Reihenfolge von va_end nicht so wichtig sein.

Die Einrückung ist von mir, um hervorzuheben, dass va_start & va_end "verschachtelt" sind

Natürlich müssen Sie va_arg aufrufen, um wirklich variable Argumente abzurufen (Ich hoffe, dass Ihr process das macht). stdarg(3) erklärt das gut (für C-Code):

Jeder Aufruf von va_start() muss in derselben Funktion durch einen entsprechenden Aufruf von va_end() übereinstimmen.

Beachten Sie das Wort entsprechend (die Betonung ist von mir). Ich glaube, das bedeutet, dass va_start und va_end wirklich verschachtelt sind (zumindest prinzipiell).

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X