Was heißt "Won't x be leaked"das Der Typ von xist ein integrierter Datentyp. Warum wählst du kein besseres Beispiel?
Nawaz
2
@Nawaz: Das Beispiel ist perfekt so wie es ist. Fast jedes Mal, wenn ich mit jemandem darüber spreche goto, denken sie, dass sogar Variablen für die automatische Speicherdauer irgendwie "durchgesickert" sind. Dass Sie und ich etwas anderes wissen, ist völlig nebensächlich.
Leichtigkeitsrennen im Orbit
1
@ David: Ich stimme zu, dass diese Frage viel sinnvoller ist, wenn die Variable einen nicht trivialen Destruktor hat ... und ich habe in Tomalaks Antwort nachgesehen und ein solches Beispiel gefunden. Während ein intnicht auslaufen kann, kann es außerdem ausgelaufen sein . Zum Beispiel: void f(void) { new int(5); }Lecks an int.
Ben Voigt
Warum ändern Sie die Frage nicht in "Wie wird im angegebenen Beispiel der Code-Ausführungspfad von f () nach main () übertragen, ohne den Stapel und andere Funktionen für die Rückkehr von Funktionen zu löschen? Wäre es wichtig, wenn ein Destruktor erforderlich wäre?" genannt? Ist das gleiche in C? " Würden beide die Absicht der Frage beibehalten und mögliche Missverständnisse vermeiden?
Jack V.
Antworten:
210
Achtung: Diese Antwort bezieht sich auf C ++ nur ; Die Regeln sind in C ganz anders.
Wird nicht xdurchgesickert sein?
Nein auf keinen Fall.
Es ist ein Mythos, bei dem gotoes sich um ein Konstrukt auf niedriger Ebene handelt, mit dem Sie die in C ++ integrierten Scoping-Mechanismen überschreiben können. (Wenn überhaupt, ist dies longjmpmöglicherweise anfällig dafür.)
Berücksichtigen Sie die folgenden Mechanismen, die verhindern, dass Sie mit Etiketten (einschließlich caseEtiketten) "schlechte Dinge" tun .
1. Beschriftungsbereich
Sie können nicht über Funktionen springen:
void f(){int x =0;goto lol;}int main(){
f();
lol:return0;}// error: label 'lol' used but not defined
[n3290: 6.1/1]:[..] Der Umfang eines Etiketts ist die Funktion, in der es erscheint. [..]
2. Objektinitialisierung
Sie können nicht über die Objektinitialisierung springen:
int main(){goto lol;int x =0;
lol:return0;}// error: jump to label ‘lol’// error: from here// error: crosses initialization of ‘int x’
struct T {
T(){ cout <<"*T";}~T(){ cout <<"~T";}};int main(){int x =0;
lol:
T t;if(x++<5)goto lol;}// Output: *T~T*T~T*T~T*T~T*T~T*T~T
[n3290: 6.6/2]:[..] Die Übertragung aus einer Schleife, aus einem Block oder zurück an einer initialisierten Variablen mit automatischer Speicherdauer vorbei beinhaltet die Zerstörung von Objekten mit automatischer Speicherdauer, die an dem Punkt, der von übertragen wurde, aber nicht an dem Punkt, an den übertragen wurde, in den Geltungsbereich fallen . [..]
Sie können nicht in den Bereich eines Objekts springen, auch wenn es nicht explizit initialisiert wurde:
int main(){goto lol;{
std::string x;
lol:
x ="";}}// error: jump to label ‘lol’// error: from here// error: crosses initialization of ‘std::string x’
... mit Ausnahme bestimmter Arten von Objekten , mit denen die Sprache unabhängig umgehen kann, da sie keine "komplexe" Konstruktion erfordern:
int main(){goto lol;{int x;
lol:
x =0;}}// OK
[n3290: 6.7/3]:Es ist möglich, in einen Block zu übertragen, jedoch nicht so, dass Deklarationen bei der Initialisierung umgangen werden. Ein Programm, das von einem Punkt, an dem sich eine Variable mit automatischer Speicherdauer nicht im Gültigkeitsbereich befindet, zu einem Punkt springt, an dem sie sich im Gültigkeitsbereich befindet, ist fehlerhaft, es sei denn, die Variable hat einen Skalartyp, einen Klassentyp mit einem trivialen Standardkonstruktor und einen trivialen Destruktor, a cv-qualifizierte Version eines dieser Typen oder ein Array eines der vorhergehenden Typen und wird ohne Initialisierer deklariert. [..]
3. Das Springen hält sich an den Umfang anderer Objekte
struct T {
T(){ cout <<"*T";}~T(){ cout <<"~T";}};int main(){{
T t;goto lol;}
lol:return0;}// *T~T
[n3290: 6.6/2]:Beim Verlassen eines Bereichs (wie auch immer ausgeführt) werden Objekte mit automatischer Speicherdauer (3.7.3), die in diesem Bereich erstellt wurden, in umgekehrter Reihenfolge ihrer Erstellung zerstört. [..]
Fazit
Die oben genannten Mechanismen stellen sicher, dass gotoSie die Sprache nicht brechen können.
Natürlich bedeutet dies nicht automatisch, dass Sie es gotofür ein bestimmtes Problem "verwenden" sollten , aber es bedeutet , dass es bei weitem nicht so "böse" ist, wie der verbreitete Mythos die Menschen glauben macht.
Möglicherweise stellen Sie fest, dass C nicht verhindert, dass all diese gefährlichen Dinge passieren.
Daniel
13
@ Daniel: Die Frage und Antwort beziehen sich sehr speziell auf C ++, sind aber fair. Vielleicht können wir eine weitere FAQ haben, die den Mythos zerstreut, dass C und C ++ gleich sind;)
Leichtigkeitsrennen im Orbit
3
@Tomalak: Ich glaube nicht, dass wir hier nicht einverstanden sind. Viele der auf SO gegebenen Antworten sind irgendwo explizit dokumentiert. Ich habe nur darauf hingewiesen, dass es für einen C-Programmierer verlockend sein könnte, diese Antwort zu sehen und anzunehmen, dass sie in C ++ ähnlich funktionieren sollte, wenn sie in C ++ funktioniert
Daniel
2
Vielleicht möchten Sie auch hinzufügen, dass all diese Dinge, die über die Initialisierung springen, für Fallbezeichnungen gleich sind.
PlasmaHH
12
Wow, ich hatte gerade angenommen, dass die Semantik von C ++ für goto gebrochen war, aber sie ist überraschend vernünftig! Gute Antwort.
"Won't x be leaked"
das Der Typ vonx
ist ein integrierter Datentyp. Warum wählst du kein besseres Beispiel?goto
, denken sie, dass sogar Variablen für die automatische Speicherdauer irgendwie "durchgesickert" sind. Dass Sie und ich etwas anderes wissen, ist völlig nebensächlich.int
nicht auslaufen kann, kann es außerdem ausgelaufen sein . Zum Beispiel:void f(void) { new int(5); }
Lecks anint
.Antworten:
Achtung: Diese Antwort bezieht sich auf C ++ nur ; Die Regeln sind in C ganz anders.
Nein auf keinen Fall.
Es ist ein Mythos, bei dem
goto
es sich um ein Konstrukt auf niedriger Ebene handelt, mit dem Sie die in C ++ integrierten Scoping-Mechanismen überschreiben können. (Wenn überhaupt, ist dieslongjmp
möglicherweise anfällig dafür.)Berücksichtigen Sie die folgenden Mechanismen, die verhindern, dass Sie mit Etiketten (einschließlich
case
Etiketten) "schlechte Dinge" tun .1. Beschriftungsbereich
Sie können nicht über Funktionen springen:
2. Objektinitialisierung
Sie können nicht über die Objektinitialisierung springen:
Wenn Sie über die Objektinitialisierung zurückspringen , wird die vorherige "Instanz" des Objekts zerstört :
Sie können nicht in den Bereich eines Objekts springen, auch wenn es nicht explizit initialisiert wurde:
... mit Ausnahme bestimmter Arten von Objekten , mit denen die Sprache unabhängig umgehen kann, da sie keine "komplexe" Konstruktion erfordern:
3. Das Springen hält sich an den Umfang anderer Objekte
Ebenso Objekte mit automatischer Speicherdauer sind nicht „durchgesickert“ , wenn Sie
goto
aus ihrem Umfang :Fazit
Die oben genannten Mechanismen stellen sicher, dass
goto
Sie die Sprache nicht brechen können.Natürlich bedeutet dies nicht automatisch, dass Sie es
goto
für ein bestimmtes Problem "verwenden" sollten , aber es bedeutet , dass es bei weitem nicht so "böse" ist, wie der verbreitete Mythos die Menschen glauben macht.quelle