Das Kompilieren des obigen Codes mit GCC 4.4.1, g ++ test.cc -Wextra -O1, gibt diese verwirrende Warnung:
%Vor%Die Zeile 1035 von stl_vector.h befindet sich in einer Hilfsfunktion, die vom Konstruktor vector (n, value) verwendet wird, den ich beim Konstruieren von foo aufruft. Die Warnung verschwindet, wenn der Compiler den Argumentwert herausfinden kann (z. B. ist es ein numerisches Literal), daher verwende ich argc in diesem Testfall, weil der Compiler den Wert davon nicht bestimmen kann.
Ich nehme an, die Warnung könnte daran liegen, dass der Compiler die Vektorkonstruktion so optimiert, dass sie tatsächlich nach dem setjmp-Landepunkt passiert (was hier der Fall zu sein scheint, wenn das Konstruktorargument von einem Parameter der Funktion abhängt).
Wie kann ich das Problem vermeiden, vorzugsweise ohne den setjmp-Teil auf eine andere Funktion zu setzen?
Die Verwendung von setjmp ist keine Option, weil ich mit einer Menge C-Bibliotheken festhalte, die es für die Fehlerbehandlung benötigen.
Die Regel besagt, dass jede nichtflüchtige, nicht statische lokale Variable im Stapelrahmen, die setjmp aufruft, durch einen Aufruf von longjmp geplottert werden könnte. Der einfachste Weg, um damit umzugehen, ist sicherzustellen, dass der Frame, den Sie setjmp aufrufen, keine Variablen enthält, die Ihnen wichtig sind. Dies kann normalerweise getan werden, indem setjmp in eine Funktion gesetzt wird und Verweise auf Dinge übergeben werden, die in einer anderen Funktion deklariert wurden, die setjmp nicht aufruft:
%Vor%Beachten Sie auch, dass in diesem Zusammenhang clobbering eigentlich nur bedeutet, auf den Wert zurückzusetzen, den es hatte, als setjmp aufgerufen wurde. Also, wenn longjmp nie nach einer Änderung eines lokalen aufgerufen werden kann, ist es auch in Ordnung.
Bearbeiten
Das genaue Zitat aus der C99-Spezifikation für setjmp lautet:
Alle zugänglichen Objekte haben Werte und alle anderen Komponenten der abstrakten Maschine have state, zu dem Zeitpunkt, als die longjmp - Funktion aufgerufen wurde, außer dass die Werte von Objekte mit automatischer Speicherdauer, die lokal für die Funktion sind, die das Objekt enthält Aufruf des entsprechenden setjmp-Makros, das keinen flüchtigen qualifizierten Typ hat und wurden zwischen dem setjmp-Aufruf und dem longjmp-Aufruf geändert unbestimmt.
Dies ist keine Warnung, die Sie ignorieren sollten, longjmp () - und C ++ - Objekte kommen nicht miteinander aus. Das Problem ist, dass der Compiler automatisch einen Destruktor-Aufruf für Ihr foo-Objekt ausgibt. Ein longjmp () kann den Destruktoranruf umgehen.
C ++ - Ausnahmen entpacken auch Stack-Frames, aber sie garantieren, dass Destruktoren lokaler Objekte aufgerufen werden. Keine solche Garantie von longjmp (). Wenn Sie herausfinden wollen, ob longjmp () zum Byte geht, müssen Sie die lokalen Variablen in jeder Funktion sorgfältig analysieren, die wegen des longjmp () früh beendet werden könnten. Das ist nicht einfach.
Wie die Zeilennummer 1035 in der Fehlermeldung zeigt, hat Ihr Code-Snippet den eigentlichen Problemcode erheblich vereinfacht. Du bist zu weit gegangen. Es gibt keine Ahnung, wie Sie "zuerst" verwenden. Das Problem ist, dass der Compiler das selbst im echten Code nicht herausfinden kann. Es besteht die Befürchtung, dass der Wert von "first" nach einer von Null abweichenden Rückgabe von "setjmp" möglicherweise nicht so ist, wie Sie es für richtig halten. Dies liegt daran, dass Sie den Wert vor und nach dem ersten Aufruf (Nullpunkt) in 'setjmp' geändert haben. Wenn die Variable in einem Register gespeichert wurde, ist der Wert wahrscheinlich anders, als wenn sie im Speicher gespeichert wäre. Der Compiler wird also konservativ, indem er Ihnen die Warnung gibt.
Um einen Blindsprung zu machen und die Frage zu beantworten, können Sie möglicherweise die Warnmeldung loswerden, indem Sie die Deklaration von "first" mit "volatile" qualifizieren. Sie könnten auch versuchen, "zuerst" global zu machen. Wenn Sie die Optimierungsebene (-O-Flag) löschen, können Sie möglicherweise bewirken, dass der Compiler Variablen im Speicher behält. Dies sind schnelle Fehlerbehebungen, die möglicherweise einen Fehler verbergen.
Sie sollten sich wirklich Ihren Code ansehen und wie Sie "zuerst" verwenden. Ich nehme eine andere wilde Vermutung und sage, dass Sie diese Variable möglicherweise eliminieren können. Könnte dieser Name "first" bedeuten, dass Sie ihn verwenden, um einen ersten Aufruf (null Rückkehr) an "setjmp" anzuzeigen? Wenn ja, loswerden - Redesign der Logik.
Wenn der reelle Code bei einer Rückgabe ungleich null von 'setjmp' (wie im Snippet) gerade beendet wird, ist der Wert von 'first' in diesem logischen Pfad nicht von Bedeutung. Benutze es nicht auf beiden Seiten des 'setjmp'.
Die schnelle Antwort: Lassen Sie das -O1-Flag fallen oder regulieren Sie den Compiler auf eine frühere Version. Entweder ließ man die Warnung auf meinem System verschwinden. Ich musste gcc4.4 bauen und benutzen, um die Warnung überhaupt zu bekommen. (verdammt, das ist ein riesiges System)
Nein? Ich dachte nicht.
Ich verstehe wirklich nicht alles, was C ++ mit seinen Objekten macht und wie sie genau zugewiesen werden. Der Kommentar von OP, dass das Problem nicht auftrat, wenn ein konstanter Wert anstelle von "argc" für die Vektorgröße verwendet wurde, gibt mir die Möglichkeit, meinen Hals herauszuhalten. Ich wage eine Vermutung, dass C ++ den '__first'-Zeiger nur dann zur Freigabe verwendet, wenn die anfängliche Zuweisung keine Konstante ist. Auf der höheren Ebene der Optimierung verwendet der Compiler die Register mehr und es gibt einen Konflikt zwischen den Pre- und Post-Setjmp-Zuweisungen ... Ich weiß nicht, es macht keinen Sinn.
Die allgemeine Bedeutung dieser Warnung lautet: "Sind Sie sicher, dass Sie wissen, was Sie tun?" Der Compiler weiß nicht, ob Sie wissen, was der Wert von "__first" sein wird, wenn Sie longjmp ausführen, und erhalten eine von Null verschiedene Rückkehr von "setjmp". Die Frage ist, ob der Wert nach der Rückgabe (ungleich Null) der Wert ist, der in den Sicherungspuffer eingegeben wurde, oder der Wert, den Sie nach dem Speichern erstellt haben. In diesem Fall ist es verwirrend, weil Sie nicht wussten, dass Sie "__first" verwenden, und weil in solch einem einfachen Programm keine (explizite) Änderung zu "__first"
erfolgtDer Compiler kann den Logikfluss in einem komplexen Programm nicht analysieren, so dass er es scheinbar gar nicht für irgendein Programm versucht. Es ermöglicht die Möglichkeit, dass Sie den Wert geändert haben. So gibt es nur ein freundliches "Heads-up". Der Compiler rät Ihnen zu zweit und versucht, hilfreich zu sein.
Wenn Sie mit der Wahl des Compilers und der Optimierung stur sind, gibt es einen Programmierfehler. Speichern Sie die Umgebung vor der Zuweisung des Vektors. Verschieben Sie den 'setjmp' an den Anfang des Programms. Abhängig von der Vektorverwendung und der Fehlerlogik in dem realen Programm kann dies andere Änderungen erfordern.
Bearbeiten 1/21 -------
meine Begründung (mit g ++ - mp-4.4 -Wextra -O1 main.cpp):
%Vor%Keine Warnungen; a.out produziert:
Beginnen Sie mit 0 0
Beginnen Sie mit 1 1
Beginnen Sie mit 2 1
Beginnen Sie mit 3 1
Beginnen Sie mit 4 1
Ende mit 4 1