webentwicklung-frage-antwort-db.com.de

Was macht Visual Studio mit einem gelöschten Zeiger und warum?

Ein C++ - Buch, das ich gelesen habe, besagt, dass beim Löschen eines Zeigers mit dem Operator delete der Speicher an der Stelle, auf die er zeigt, "freigegeben" und überschrieben werden kann. Es wird auch angegeben, dass der Zeiger weiterhin auf dieselbe Position zeigt, bis er neu zugewiesen oder auf NULL gesetzt wird.

In Visual Studio 2012 jedoch; das scheint nicht der Fall zu sein!

Beispiel:

#include <iostream>

using namespace std;

int main()
{
    int* ptr = new int;
    cout << "ptr = " << ptr << endl;
    delete ptr;
    cout << "ptr = " << ptr << endl;

    system("pause");

    return 0;
}

Wenn ich dieses Programm kompiliere und ausführe, erhalte ich die folgende Ausgabe:

ptr = 0050BC10
ptr = 00008123
Press any key to continue....

Es ist klar, dass sich die Adresse, auf die der Zeiger zeigt, ändert, wenn delete aufgerufen wird!

Warum passiert das? Hat dies speziell mit Visual Studio zu tun?

Und wenn Löschen die Adresse, auf die es verweist, ändern kann, warum sollte Löschen dann nicht automatisch den Zeiger auf NULL anstelle einer zufälligen Adresse setzen?

127
tjwrona1992

Mir ist aufgefallen, dass die in ptr gespeicherte Adresse immer mit 00008123 Überschrieben wurde ...

Dies schien seltsam, also habe ich ein wenig gegraben und fand dies Microsoft Blog Post mit einem Abschnitt über "Automatische Zeiger-Bereinigung beim Löschen von C++ - Objekten".

... Prüfungen auf NULL sind ein gängiges Codekonstrukt, das bedeutet, dass eine vorhandene Prüfung auf NULL in Kombination mit der Verwendung von NULL als Bereinigungswert ein echtes Speichersicherheitsproblem verbergen kann, dessen Grundursache tatsächlich behoben werden muss.

Aus diesem Grund haben wir 0x8123 als Bereinigungswert ausgewählt. Aus Sicht des Betriebssystems befindet sich dieser Wert auf derselben Speicherseite wie die Nulladresse (NULL). Eine Zugriffsverletzung bei 0x8123 ist jedoch für den Entwickler ein besserer Hinweis, da mehr Aufmerksamkeit erforderlich ist .

Es wird nicht nur erklärt, was Visual Studio mit dem Zeiger macht, nachdem er gelöscht wurde, sondern es wird auch beantwortet, warum es sich dafür entschieden hat, ihn NICHT automatisch auf NULL zu setzen!


Diese "Funktion" wird im Rahmen der Einstellung "SDL-Prüfungen" aktiviert. Zum Aktivieren/Deaktivieren gehen Sie zu: PROJECT -> Properties -> Configuration Properties -> C/C++ -> General -> SDL checks

Um dies zu bestätigen:

Wenn Sie diese Einstellung ändern und denselben Code erneut ausführen, wird die folgende Ausgabe generiert:

ptr = 007CBC10
ptr = 007CBC10

"feature" steht in Anführungszeichen, da in einem Fall, in dem Sie zwei Zeiger auf denselben Speicherort haben, durch das Aufrufen von delete nur einer von ihnen bereinigt wird [~ # ~] [~ # ~]. Der andere zeigt auf den ungültigen Ort.

Visual Studio kann Sie auf eine schwierige Situation vorbereiten, indem es diesen Konstruktionsfehler nicht dokumentiert.

172
tjwrona1992

Sie sehen die Nebenwirkungen der /sdl Kompilierungsoption. Standardmäßig für VS2015-Projekte aktiviert, ermöglicht es zusätzliche Sicherheitsüberprüfungen, die über die von/gs bereitgestellten hinausgehen. Verwenden Sie Projekt> Eigenschaften> C/C++> Allgemein> SDL-Überprüfungen, um diese Einstellung zu ändern.

Zitat aus dem MSDN-Artikel :

  • Führt eine begrenzte Zeigerbereinigung durch. In Ausdrücken ohne Dereferenzen und in Typen ohne benutzerdefinierten Destruktor werden Zeigerreferenzen nach einem Aufruf zum Löschen auf eine ungültige Adresse gesetzt. Dies hilft, die Wiederverwendung veralteter Zeigerreferenzen zu verhindern.

Denken Sie daran, dass das Setzen von gelöschten Zeigern auf NULL eine schlechte Praxis ist, wenn Sie MSVC verwenden. Es besiegt die Hilfe, die Sie sowohl vom Debug-Heap als auch von dieser/sdl-Option erhalten. Sie können ungültige freie/gelöschte Aufrufe in Ihrem Programm nicht mehr erkennen.

30
Hans Passant

Außerdem wird angegeben, dass der Zeiger weiterhin auf dieselbe Position zeigt, bis er neu zugewiesen oder auf NULL gesetzt wird.

Das sind definitiv irreführende Informationen.

Es ist klar, dass sich die Adresse, auf die der Zeiger zeigt, ändert, wenn delete aufgerufen wird!

Warum passiert das? Hat dies speziell mit Visual Studio zu tun?

Dies liegt eindeutig innerhalb der Sprachspezifikationen. ptr ist nach dem Aufruf von delete ungültig. Die Verwendung von ptr nach deleted führt zu undefiniertem Verhalten. Tu es nicht. Die Laufzeitumgebung kann mit ptr nach dem Aufruf von delete tun, was sie will.

Und wenn delete die Adresse ändern kann, auf die es zeigt, warum sollte delete den Zeiger nicht automatisch auf NULL anstatt auf eine zufällige Adresse setzen?

Das Ändern des Zeigerwerts auf einen beliebigen alten Wert liegt innerhalb der Sprachspezifikation. Soweit ich es auf NULL ändere, würde ich sagen, das wäre schlecht. Das Programm würde sich sinnvoller verhalten, wenn der Wert des Zeigers auf NULL gesetzt würde. Dadurch wird das Problem jedoch ausgeblendet. Wenn das Programm mit verschiedenen Optimierungseinstellungen kompiliert oder in eine andere Umgebung portiert wird, wird das Problem wahrscheinlich im ungünstigsten Moment auftreten.

19
R Sahu
delete ptr;
cout << "ptr = " << ptr << endl;

Im Allgemeinen lesen (wie oben) Werte ungültiger Zeiger (Zeiger wird beispielsweise ungültig, wenn Sie delete it) ist implementierungsdefiniertes Verhalten. Dies wurde in CWG # 1438 eingeführt. Siehe auch hier .

Beachten Sie, dass das Lesen von Werten ungültiger Zeiger zuvor ein undefiniertes Verhalten war. Was Sie also oben angegeben haben, ist ein undefiniertes Verhalten, was bedeutet, dass alles passieren kann.

10
giorgi moniava

Ich glaube, Sie führen eine Art Debug-Modus aus und VS versucht, Ihren Zeiger auf einen bekannten Speicherort neu zu positionieren, damit ein weiterer Versuch der Dereferenzierung verfolgt und gemeldet werden kann. Versuchen Sie, dasselbe Programm im Release-Modus zu kompilieren/auszuführen.

Zeiger werden in der Regel nicht innerhalb von delete geändert, um die Effizienz zu erhöhen und eine falsche Vorstellung von der Sicherheit zu vermeiden. Das Setzen des Löschzeigers auf einen vordefinierten Wert ist in den meisten komplexen Szenarien nicht sinnvoll, da der zu löschende Zeiger wahrscheinlich nur einer von mehreren ist, die auf diesen Ort zeigen.

In der Tat, je mehr ich darüber nachdenke, desto mehr stelle ich fest, dass VS daran schuld ist, wie immer. Was ist, wenn der Zeiger const ist? Wird es das noch ändern?

1
SergeyA

Nach dem Löschen des Zeigers ist der Speicher, auf den er zeigt, möglicherweise noch gültig. Um diesen Fehler zu manifestieren, wird der Zeigerwert auf einen offensichtlichen Wert gesetzt. Dies hilft wirklich beim Debuggen. Wenn der Wert auf NULL gesetzt wurde, wird er möglicherweise nie als potenzieller Fehler im Programmablauf angezeigt. Wenn Sie später mit NULL testen, wird möglicherweise ein Fehler ausgeblendet.

Ein weiterer Punkt ist, dass einige Laufzeitoptimierer diesen Wert überprüfen und seine Ergebnisse ändern können.

In früheren Zeiten stellte MS den Wert auf 0xcfffffff.

0
Karsten