Warum stürzt der folgende Code ab?

8

Dies erstellt einfach einige Listenelemente und löscht dann ein Element an seinem Anfang, indem es sich über eine umgekehrte Iteration nähert. Es ist eine Replik eines tatsächlichen Problems mit dem Code, der Elemente löscht, während sie umgekehrt durchlaufen.

%Vor%

Beim Ausführen stürzt es ab mit:

%Vor%

Wenn mit valgrind ausgeführt wird, heißt es:

%Vor%

Compiler:

%Vor%

Bogen:

%Vor%

Glauben Sie, dass es ein Fehler ist, oder mache ich hier etwas falsch?

ps. Wenn Sie case 33 entfernen (was nie passieren sollte), wird dies zu einer Endlosschleife anstelle eines Absturzes.

    
dragonroot 22.12.2012, 10:27
quelle

4 Antworten

11

Okay, also habe ich einen Stift und Papier rausgeholt und jetzt denke ich, dass damit zu tun ist, dass Ihr e Iterator ungültig gemacht wurde. Denken Sie daran, dass Reverse-Iteratoren einen normalen Iterator enthalten, der auf das nächste Element im Container verweist. Dies ist der -Basisiterator . Das heißt, wenn Sie den rbegin() Iterator haben, der auf das letzte Element zeigt, zeigt sein interner Iterator auf das Element "past-the-end". Wenn Sie den rend() Iterator haben, der auf den Iterator vor dem Anfang zeigt (ein imaginäres Element, auf das reverse Iteratoren zeigen können), zeigt sein interner Iterator ebenfalls auf das erste Element.

So sieht Ihre Liste ungefähr so ​​aus (BTB = vor dem Anfang, PTE = nach dem Ende):

%Vor%

Die gestrichelten Linien zeigen, wo die Basis-Iteratoren zeigen.

Nun, in der ersten Iteration zeigen Sie auf das letzte Element (1. in umgekehrter Richtung) und count ist 0, weil Sie Postfix-Inkrement machen. Also, wenn der Schalter für 32 übereinstimmt, zeigen Sie auf das erste Element (33. in umgekehrter Reihenfolge) in der Liste.

Okay, jetzt sind wir in diesem Zustand:

%Vor%

Sie führen dann den folgenden Code aus:

%Vor%

Die erste Zeile bringt uns in diesen Zustand:

%Vor%

Dann löschen Sie das Element, auf das der Basisiterator zeigt, und setzen den umgekehrten Iterator so, dass seine Basis jetzt auf das Element nach des gelöschten Elements zeigt. Jetzt haben wir:

%Vor%

Nun wurde e jedoch ungültig gemacht. Seine Basis zeigt nicht mehr auf das erste Element der Liste, es zeigt auf ein ungültiges Element.

Nun sollte Ihre Schleife anhalten, weil i am Ende steht, aber nicht. Es wird ein anderes Mal fortgesetzt, mit count als 33 , zuerst mit i++ :

%Vor%

Und dann versuchen, die Basis zu löschen. Ach je! Die Basis zeigt nicht auf ein gültiges Element und wir bekommen einen Absturz. Tatsächlich denke ich, dass du bereits ein undefiniertes Verhalten hast, sobald du zu weit fortgeschritten bist.

Die Lösung

Der Weg, es zu beheben, besteht darin, bei jeder Iteration einfach rend() zu erhalten:

%Vor%

Alternativ können Sie e aktualisieren, wenn Sie Elemente löschen:

%Vor%

Nun, meine vorherige Antwort war, das Inkrement und das Löschen zu tauschen, was funktionierte, aber warum? Nun, gehen wir zurück zu dem Punkt, wo es wichtig ist (ich habe ein weiteres Element hinzugefügt, um die nächsten Schritte zu verdeutlichen):

%Vor%

Also löschen wir zuerst die Basis und geben uns folgendes:

%Vor%

Dann erhöhen wir i :

%Vor%

Dann i == e und wir beenden die Schleife. Während dies funktioniert , tut es nicht, was Sie wollen. Es entfernt nur das zweite Element.

    
Joseph Mansfield 22.12.2012, 10:33
quelle
4

Der Fehler ist, dass e ungültig wird. Sie sollten direkt mit lst.rend() vergleichen. Warum wird es ungültig gemacht? Nun, schauen wir uns die Definition von rend() ( §23.2.1.9 ): reverse_iterator(begin()) an.

Also hängt die Konstruktion rend() vom begin() Iterator ab, der auf das erste Element zeigt, das Sie tatsächlich mit case 32 löschen würden. Da diese Operation begin() ungültig macht, wird sehr wahrscheinlich auch rend() ungültig, je nachdem, wie es implementiert wurde, was in dieser Version von libstdc++ eindeutig geschieht.

Es macht auch Sinn, dass case 33 zum Absturz führt, diesmal zeigt der Iterator auf etwas, das überhaupt nicht mehr in der Liste ist. Das Entfernen wird natürlich endlos wiederholt, weil e ungültig ist und Ihre Stoppbedingung nicht trifft.

    
KillianDS 22.12.2012 11:05
quelle
1

Wenn Sie Elemente löschen, wird e ungültig gemacht! Sie müssen e nach dem Beschleunigen aktualisieren:

%Vor%     
Anonymous Coward 22.12.2012 10:43
quelle
0

Das soll funktionieren -

%Vor%     
SChepurin 22.12.2012 12:38
quelle

Tags und Links