Warum zerstört Löschen nichts?

Lesezeit: 7 Minuten

Benutzer-Avatar
Kamixave

Ich spiele ein wenig mit der dynamischen Speicherzuweisung, aber ich bekomme keinen Punkt. Bei der Zuweisung von Speicher mit der new -Anweisung soll ich in der Lage sein, den Speicher zu zerstören, auf den der Zeiger zeigt delete.

Aber wenn ich es versuche, das delete Der Befehl scheint nicht zu funktionieren, da der Platz, auf den der Zeiger zeigt, nicht geleert zu sein scheint.

Nehmen wir diesen wirklich einfachen Code als Beispiel:

#include <iostream>  

using namespace std;

int main()  
{  
    //I create a pointer-to-integer pTest, make it point to some new space,  
    // and fulfill this free space with a number;  
    int* pTest;  
    pTest = new int;  
    *(pTest) = 3;  
    cout << *(pTest) << endl; 

    // things are working well so far. Let's destroy this
    // dynamically allocated space!
    delete pTest;

    //OK, now I guess the data pTest pointed to has been destroyed 
    cout << *(pTest) << endl; // Oh... Well, I was mistaking.  

    return 0;  
}  

Irgendeine Ahnung ?

  • Ja, ein Noob-Quieston, aber nicht so ein trivialer.

    – scharfer Zahn

    19. Juli 2010 um 11:24 Uhr

  • “Zerstörung”, wie vom Standard definiert, befasst sich damit, wenn Objekte für die Verwendung ungültig werden, nachdem ein vom Klassenautor geschriebener Bereinigungscode ausgeführt wurde. Es bezieht sich nicht auf das Bereinigen des zuvor von dem Objekt belegten Speichers, was der Autor/Benutzer der Klasse nicht angefordert hat und daher Zeitverschwendung wäre.

    – Unterstrich_d

    27. Oktober 2018 um 17:43 Uhr

  • Wenn Sie den Speicher zerstören möchten, verwenden Sie einen Schraubendreher, um die Chips von der Hauptplatine zu hebeln. Das alles delete zulassen, dass dieser Speicher etwas anderem zugewiesen wird. Es kann die in den tatsächlichen Bits gespeicherten Werte ändern oder nicht. Aber das Lesen dieser Bits mit einem Zeiger auf das gelöschte Objekt verursacht undefiniertes Verhalten

    – Tim Randall

    22. September 2021 um 13:39 Uhr


Benutzer-Avatar
GManNickG

Es ist Zeit zu lernen, was undefiniertes Verhalten ist. 🙂

Wenn Sie in C++ etwas Illegales/Unsinniges/Schlechtes/etc. Der Standard sagt oft, dass “es zu undefiniertem Verhalten führt”. Das bedeutet, dass der Status Ihres Programms ab diesem Zeitpunkt vollständig nicht garantiert ist und alles passieren kann.

An dem Punkt, wo du dein letztes tust *(pTest), erhalten Sie undefiniertes Verhalten. Das ist weil pTest zeigt nicht auf ein gültiges Objekt, und die Dereferenzierung eines solchen Zeigers ist undefiniert. Was Sie sehen, ist also absolut erlaubt: undefinierte Ausgabe.

Alles, was Sie getan haben, ist zu sagen: “Ich bin mit dieser Zuordnung fertig.” Sobald Sie das gesagt haben, sollten Sie (und können es tatsächlich nicht) diese Erinnerung nicht länger untersuchen oder sich um sie kümmern. Es macht nicht einmal konzeptionell Sinn, etwas aufzuheben und dann zu versuchen, es zu benutzen; Du hast gesagt, du wärst fertig!

Ihre Ausgabe ist jedoch etwas vorhersehbar: Wahrscheinlich sagt Ihr Betriebssystem einfach “okay, danke für den Speicher” und das war’s. Es gibt keinen Grund, den Speicher tatsächlich “zurückzusetzen” oder irgendetwas Besonderes zu tun. Das wäre in der Tat Zeitverschwendung, wenn niemand (einschließlich Ihres eigenen Programms) es nicht verwendet.

Aber denken Sie daran, dass diese Ausgabe völlig undefiniert ist. Versuchen Sie nicht, Objekte zu verwenden, die nicht existieren. Vielleicht wäre ein besserer Test gewesen:

#include <iostream>

struct foo
{
    ~foo()
    {
        std::cout << "foo is gone :(" << std::endl;
    }
};

int main(void)
{
    foo* f = new foo();
    delete f; // you'll see that the object is destroyed.
}

Obwohl es scheint, dass Sie sehen wollten, was mit der Erinnerung selbst passiert. Denken Sie nur daran, dass es keinen Sinn macht, das Gedächtnis loszuwerden und es dann zu verwenden, also lautet die Antwort: Wer weiß. Es hängt von Ihrer spezifischen Plattform ab, die C++ nicht interessiert.

  • Das Hässliche an undefiniertem Verhalten ist, dass es meistens gut aussieht.

    – Peterchen

    19. Juli 2010 um 11:04 Uhr

  • Beachten Sie, dass einige Implementierungen Wille Ändern Sie den Inhalt des Speichers, um beim Debuggen zu helfen. Beispielsweise sehen Sie möglicherweise Bytemuster wie 0xDEADBEEF, 0xFEEEFEEE oder 0xDDDDDDDD, je nachdem, wie der Speicher freigegeben wurde.

    – Tim Randall

    22. September 2021 um 13:44 Uhr


  • “von diesem Punkt an” nicht nur vorwärts vom undefinierten Verhalten, rückwärts entlang aller Pfade, die am undefinierten Verhalten enden

    – Caleth

    22. September 2021 um 13:48 Uhr

Benutzer-Avatar
Macmade

Der Aufruf von delete markiert den Speicherbereich als frei. Es wird nicht notwendig sein, seinen alten Wert zurückzusetzen.

Es wird empfohlen, den Zeiger nach dem Aufruf von delete auf 0 zu setzen:

delete pTest;
pTest = 0;

  • Ich würde stattdessen raten, die Variable nicht zu verwenden überhaupt nach Anruf delete (…es sei denn, es wird etwas anderem zugewiesen).

    – stakx – trägt nicht mehr bei

    19. Juli 2010 um 11:28 Uhr


  • Eine bessere Lösung besteht darin, sicherzustellen, dass Ihre Variable kurz nach dem Löschen den Gültigkeitsbereich verlässt und daher nicht wiederverwendet werden kann.

    – Martin York

    19. Juli 2010 um 12:22 Uhr

Der delete-Operator ruft den Destruktor des Objekts auf und gibt den zuvor dem Objekt zugewiesenen Speicher frei. Es wirkt sich nicht auf die Zeigervariable aus, die auf das gelöschte Objekt zeigt.

Wenn Sie also einen Zeiger dereferenzieren, der auf ein zerstörtes Objekt zeigt, werden Sie Probleme bekommen.

  • Ich denke, die Frage ist wirklich, warum der Speicher, auf den gezeigt wird, immer noch denselben Wert hat wie vor der Zerstörung des Objekts, und nicht, warum der Zeiger immer noch auf einen solchen zeigt.

    – Unterstrich_d

    27. Oktober 2018 um 17:40 Uhr

Die Antwort ist Leistung.

Es ist eine großartige Debug-Hilfe, um den gesamten freigegebenen Speicher mit einem ungültigen Wert zu füllen (0xCCCCCCCC, 0xDEADDEADusw.), um Versuche abzufangen, veraltete Zeiger auf bereits freigegebenen Speicher zu verwenden.

Das Ändern eines freigegebenen Speichers kostet jedoch CPU-Zeit, sodass das Betriebssystem aus Leistungsgründen den freigegebenen Speicherblock einfach zu seiner “freien” Liste hinzufügt und den Inhalt intakt lässt.

Das Dereferenzieren eines Zeigers, der auf freigegebenen Speicher zeigt, ist ein undefiniertes Verhalten.

Oft wird es einfach funktionieren, weil der Speicher von bereitgestellt wird new ist normalerweise Teil eines größeren Teils des zugewiesenen Speichers, den der Zuordner verwaltet. Wenn du anrufst delete, ruft es die relevanten Destruktoren auf und markiert den Speicher als frei, was normalerweise “bereit zur Wiederverwendung” bedeutet. Wenn Sie also in diesem Speicher nachsehen, werden Sie dieselben Daten finden, die vor dem Aufruf von da waren deleteoder einige andere Daten, wenn dieser Teil des Speichers nach a neu zugewiesen wurde new Anruf.

Beachten Sie, dass nichts verbietet, dass die new/delete allocator arbeitet als dünner Wrapper um die virtuellen Speicherfunktionen des Betriebssystems, so dass, wenn alle zugeordneten Blöcke relativ zu einer Seite freigegeben wurden, die gesamte Seite freigegeben wird und jeder Versuch, darauf zuzugreifen, zu einer Adressverletzung führt.

TL-, DR-Version: keine Referenzzeiger, die auf freigegebenen Speicher zeigen: es kann manchmal funktionieren, manchmal wird es Müll zurückgeben, manchmal wird es eine Zugriffsverletzung auslösen.

Ein guter Weg, um sofort zu bemerken, wenn Sie diese Art von Fehler machen, besteht darin, Ihre Zeiger auf NULL zu setzen, nachdem Sie den Speicher gelöscht haben, auf den sie zeigen: Wenn Ihr Code versucht, einen NULL-Zeiger zu dereferenzieren, führt dies auf fast jedem System zum Absturz der Anwendung , damit Fehler wie diese nicht unbemerkt bleiben.

Benutzer-Avatar
Sarnold

Es hätte sich auf jedes Stück abgebildeten Speichers beziehen können. Oder vielleicht nicht zugeordneter Speicher, abhängig davon, wie lange Ihr Programm ausgeführt wurde, Details der Speicherzuweisungen und ob die Bibliotheken später Speicher an das Betriebssystem zurückgeben …

Wenn delete tatsächlich den gesamten zu löschenden Speicher gelöscht haben, würden Programme wesentlich länger laufen, weil sie viel Zeit damit verschwenden würden, Speicher zu löschen, der wahrscheinlich früher oder später sowieso überschrieben wird. Es mag gut zum Debuggen sein, aber im Produktionseinsatz gibt es einfach nicht viel Bedarf, Speicherinhalte tatsächlich zu löschen. (Kryptoschlüssel sind natürlich eine gute Ausnahme; diese vor dem Aufrufen löschen delete oder free ist eine gute Idee.)

Benutzer-Avatar
jjujuma

Was würde die Vernichtung der Daten bedeuten? Ich nehme an, es könnte es auf Null setzen, aber warum sich die Mühe machen? Es wird angenommen, dass es schmutzig ist, wenn wir es aus der Umwelt bekommen, also warum es reinigen, bevor wir es zurückgeben? Es ist uns egal, was darin steht, denn wir verzichten auf unser Recht, es zu lesen. Und warum delete den Zeiger selbst nicht auf Null setzt:

http://www2.research.att.com/~bs/bs_faq2.html#delete-zero

1015940cookie-checkWarum zerstört Löschen nichts?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy