'Lucky' gültige Zeiger Daten zu lokalen Kartendaten zurückgegeben?

8

In meinem C ++ - Programm habe ich eine Funktion, die eine Map mit Elementen zurückgibt, die jeweils einen Zeiger auf ein anderes Element in der Map haben können. Ich setze diese Zeiger vor dem Zurückgeben der Karte am Ende der Funktion. Beispielcode:

%Vor%

An der Call-Site der Funktion ist der Zeiger other immer noch gültig, obwohl ich vermutete, dass er ungültig wird, weil die Karte innerhalb der Funktion, auf die das "Zeigeobjekt" ein Element ist, ausgeht des Umfangs.

Ist das im Prinzip das gleiche wie in Can Auf den Speicher einer lokalen Variable kann außerhalb seines Gültigkeitsbereichs zugegriffen werden? ? Ich bin im Grunde "glücklich" der Zeiger zeigt immer noch auf gültige Daten? Oder ist etwas anderes los?

Ich glaube wirklich, es ist jedes Mal ein Glückstreffer, aber eine Bestätigung wäre sehr nett.

    
Soulrot 06.08.2014, 17:47
quelle

5 Antworten

3

Ja, Sie sind "glücklich" (nicht so viel, Ihr Programm wird früher oder später abstürzen oder schlechte Dinge auf die eine oder andere Weise tun).

Erläuterung :

returnMap wird auf dem Stapel zugewiesen: Es wird zerstört, wenn mapReturningFunction zurückgibt.

Sie nehmen die Adresse eines Objekts in die Karte und weisen sie als other -Zeiger zu.

Wenn Ihre Funktion zurückkehrt, wird returnMap in den zurückgegebenen Wert kopiert, so dass der (kopierte) Zeiger jetzt wirklich auf Müll zeigt.

Optimizer meidet häufig diese letzte Kopie, die " Kopie Elision " (oder "Rückgabewertoptimierung" genannt wird). ) und könnte der Grund für Ihr "normales" Verhalten sein. Aber es spielt keine Rolle, Ihr Programm hat ein undefiniertes Verhalten .

    
quantdev 06.08.2014, 18:08
quelle
1

Es ist kein Glück. Sie geben einen Zeiger auf eine Position auf dem Stapel zurück. Der Speicherort ist gültig und es hat zufällig den letzten Wert, der dort platziert wurde, bis etwas anderes es ändert.

Hier ist ein Beispiel, und ich leihe mir die Idee von der anderen Frage, die Sie auch verlinken, die genau das gleiche ist:

%Vor%

Der Ausdruck aus dem Programm wird folgendes sein:

%Vor%

Der Grund ist dies. Wir rufen zuerst foo () auf, was Speicherplatz auf dem Stapel für die Variable a zuweist. Wir schreiben 5 an diesen Ort, kehren dann von der Funktion zurück, geben diesen Stapel frei, lassen aber den Speicher unberührt. Wir rufen dann nukestack () auf, das Speicherplatz auf dem Stack für seine eigene Variable a zuweist. Da die Funktionen so ähnlich sind und die Variablen in beiden Funktionen die gleiche Größe haben, überlappen sich ihre Speicherorte.

An dieser Stelle hat die neue a-Variable immer noch den alten Wert. Aber wir überschreiben jetzt die 5 mit 7. Wir kehren von der Funktion zurück und unser alter Zeiger p zeigt immer noch auf dieselbe Stelle, die jetzt eine 7 hat.

Dies ist undefiniertes Verhalten und Sie brechen technisch die Regeln, wenn Sie sich darauf verlassen. Bei den meisten Compilern erhalten Sie auch eine Warnung, wenn Sie einen Zeiger auf eine lokale Variable zurückgeben, und Warnungen sollten wirklich nie ignoriert werden.

    
Flawe 06.08.2014 18:11
quelle
1

Ja, es ist nur "glücklich". Sie erhalten undefiniertes Verhalten, indem Sie einen Zeiger auf das Element einer Karte halten, das zerstört wurde. Dies ist nicht genau dasselbe wie einen Zeiger auf eine lokale Karte selbst zu behalten, da die Elemente der Karte dynamisch zugewiesen werden, aber es kommt hier zum selben Ergebnis.

Es ist nicht so einfach, hier einen Absturz oder ein anderes "falsches" Verhalten durchzusetzen. Dies ist vermutlich auf die Optimierung des Rückgabewerts zurückzuführen, d. H. Die Kopie der Karte wird entfernt, so dass die Quelle nicht überschrieben wird.

Aber das Folgende funktioniert für mich in VC ++ 2013:

Ändern Sie in Ihrer mapReturnFunction die return-Anweisung in

%Vor%

So seltsam das auch sein mag, dieser Trick wird die Rückgabewert-Optimierung deaktivieren, was zu einer tatsächlichen Kopie führt. Kompiliert mit /EHsc /Za (obwohl keine dieser beiden Flags hier wirklich einen Unterschied machen sollte), ist das Ergebnis auf meiner Maschine, dass nichts gedruckt wird.

Die Tatsache, dass sich das beobachtbare Verhalten durch eine Aussage, die keinen Unterschied machen sollte, so drastisch ändert, gibt Ihnen einen starken Hinweis darauf, dass etwas furchtbar falsch ist.

    
Christian Hackl 06.08.2014 18:17
quelle
1

Sie haben kein Glück. Copy Elision oder Move-Konstruktoren (C ++ 11) verhindern die Zerstörung der Originaldaten. Während der erste eine optionale Compiler-Optimierung ist, wird der zweite (wenn der erste nicht anwendbar ist) eine verschobene Version des Originals erstellen, ohne die Referenzen zu entwerten.

Seien Sie sich bewusst, sobald Sie eine Kopie (ohne Verschieben) erstellen und ein Original zerstört wird, sind die Zeiger auf andere Elemente ungültig!

    
Dieter Lücking 06.08.2014 18:49
quelle
1

Vor C ++ 11 , Sie sind "glücklich". Der Code funktioniert wahrscheinlich aufgrund Kopie Elision , insbesondere NRVO (Benannte Rückgabewertoptimierung) bedeutet, dass returnMap und returnedMap tatsächlich das gleiche Objekt sind. Sie dürfen dies jedoch nicht tun, daher ruft der Code undefiniertes Verhalten auf.

Post C ++ 11 , Sie sind "glücklich", aber weniger. Die Map hat einen Move-Konstruktor und return returnMap; verschiebt implizit das lokale returnMap als prvalue , das dann für den Move-Konstruktor von returnedMap verwendet werden kann. Der Compiler kann dies immer noch vermeiden, aber Sie dürfen sich darauf verlassen, dass der lokale Wert verschoben wird. Der Standard gibt jedoch keine Garantie dafür, was genau passiert, wenn ein Container verschoben wird. Daher rufen Sie immer noch undefiniertes Verhalten auf, aber das ändert sich wahrscheinlich einmal. LWW öffnen Problem 2321 ist gelöst.

    
eidolon 06.08.2014 19:36
quelle

Tags und Links