Ist es sicher, einen NULL-Zeiger zu löschen?

299

Ist es sicher, einen NULL-Zeiger zu löschen?

Und ist es ein guter Codierungsstil?

Qiuxiafei
quelle
21
Es wird empfohlen, C ++ - Programme ohne einen einzigen Aufruf von zu schreiben delete. Verwenden Sie stattdessen RAII . Das heißt, verwenden Sie std::vector<T> v(100);statt T* p = new T[100];, verwenden Sie intelligente Zeiger wie unique_ptr<T>und shared_ptr<T>sorgen Sie für das Löschen anstelle von rohen Zeigern usw.
Fredoverflow
8
Dank make_shared(c ++ 11) und make_unique(c ++ 14) sollte Ihr Programm null von newunddelete
sp2danny
2
Es kann immer noch einige seltene Fälle geben, in denen Neu / Löschen erforderlich ist, z. B. Atomic <T *>: Atomic <Unique_PTR <T>> ist nicht zulässig, und Atomic <Shared_Ptr <T>> hat Overhead, der in einigen Fällen möglicherweise nicht akzeptabel ist.
atb
2
Um eine Klasse mit Ressourcenverwaltung unter Verwendung von RAII zu deklarieren, müssen Sie new aufrufen und richtig löschen? Oder Sie sagen, dass es eine Vorlagenklasse gibt, die dies auch dies verbirgt.
VinGarcia
2
@VinGarcia Der Punkt ist, dass die meisten Benutzer- / Client -Codes ( dh Nicht-Bibliotheks-Code) niemals schreiben müssen newoder delete. Klassen, die zum Verwalten von Ressourcen entwickelt wurden, in denen Standardkomponenten die Aufgabe nicht ausführen können, können natürlich das tun, was sie tun müssen, aber der Punkt ist, dass sie die hässlichen Dinge mit dem von ihnen verwalteten Speicher erledigen, nicht mit dem Endbenutzercode. Erstellen Sie also Ihre eigene Bibliothek / Hilfsklasse für new/ deleteund verwenden Sie diese Klasse anstelle von ihnen.
underscore_d

Antworten:

265

deleteführt die Prüfung trotzdem durch, so dass eine Überprüfung auf Ihrer Seite zusätzlichen Aufwand verursacht und hässlicher aussieht. Eine sehr gute Vorgehensweise besteht darin, den Zeiger nachher auf NULL zu setzen delete(dies hilft, doppeltes Löschen und andere ähnliche Probleme mit der Speicherbeschädigung zu vermeiden).

Ich würde auch gerne, wenn deleteder Parameter standardmäßig wie in auf NULL gesetzt würde

#define my_delete(x) {delete x; x = NULL;}

(Ich kenne R- und L-Werte, aber wäre es nicht schön?)

ruslik
quelle
72
Beachten Sie, dass es immer noch mehrere andere Zeiger geben kann, die auf dasselbe Objekt verweisen, selbst wenn Sie beim Löschen einen auf NULL setzen.
etw
13
In den meisten Fällen verlässt der Zeiger in meinem Code den Gültigkeitsbereich, sobald er gelöscht wurde. Viel sicherer als nur auf NULL zu setzen.
Jalf
142
Eine sehr gute Übung ist es , den Zeiger nach dem Löschen nicht auf NULL zu setzen. Das Setzen eines Zeigers auf NULL nach dem Löschen maskiert Speicherzuordnungsfehler, was eine sehr schlechte Sache ist. Ein korrektes Programm löscht einen Zeiger nicht zweimal, und ein Programm, das einen Zeiger zweimal löscht, sollte abstürzen.
Damon
15
@ Alice: Es ist irrelevant, was der Standard in dieser Hinsicht sagt. Der Standard definierte das Löschen eines Nullzeigers, der vor 30 Jahren aus irgendeinem absurden Grund gültig war, also legal (höchstwahrscheinlich ein C-Erbe). Das zweimalige Löschen desselben Zeigers (auch nach Änderung des Bitmusters) ist jedoch immer noch ein schwerwiegender Programmfehler. Nicht nach dem Wortlaut des Standards, sondern nach Programmlogik und Eigentum. Wie das Löschen eines Nullzeigers, da der Nullzeiger keinem Objekt entspricht , konnte möglicherweise nichts gelöscht werden. Ein Programm muss genau wissen, ob ein Objekt gültig ist und wem es gehört und wann es gelöscht werden kann.
Damon
27
@Damon Trotz dieser Aufhebung Ihrer drakonischen Eigentumsregeln sind sperrenfreie Strukturen nachweislich robuster als sperrenbasierte. Und ja, meine Mitarbeiter lieben mich in der Tat wegen des verbesserten Ausführungsprofils, das diese Strukturen bieten, und der strengen Thread-Sicherheit, die sie aufrechterhalten, was es einfacher macht, über Code nachzudenken (ideal für die Wartung). Weder dies noch Ihr impliziter persönlicher Angriff haben jedoch mit einer Definition von Richtigkeit, Gültigkeit oder Eigentum zu tun. Was Sie vorschlagen, ist eine gute Faustregel, aber es ist weder ein universelles Gesetz noch in der Norm verankert.
Alice
77

Aus dem C ++ 0x Standardentwurf.

$ 5.3.5 / 2 - "[...] In beiden Alternativen kann der Wert des Operanden delete ein Nullzeigerwert sein. [... '"

Natürlich würde niemand jemals einen Zeiger mit dem Wert NULL löschen, aber das ist sicher. Idealerweise sollte kein Code vorhanden sein, mit dem ein NULL-Zeiger gelöscht wird. Es ist jedoch manchmal nützlich, wenn Zeiger (z. B. in einem Container) in einer Schleife gelöscht werden. Da das Löschen eines NULL-Zeigerwerts sicher ist, kann man die Löschlogik wirklich schreiben, ohne explizit zu prüfen, ob der NULL-Operand gelöscht werden soll.

Abgesehen davon sagt C Standard $ 7.20.3.2 auch, dass 'frei' auf einem NULL-Zeiger keine Aktion ausführt.

Die freie Funktion bewirkt, dass der Speicherplatz, auf den ptr zeigt, freigegeben wird, dh für die weitere Zuweisung verfügbar gemacht wird. Wenn ptr ein Nullzeiger ist, wird keine Aktion ausgeführt.

Chubsdad
quelle
2
Ich würde diese Antwort für ihre Zitate wirklich mögen, wenn sie nicht absichtlich Ineffizienz in nicht optimiertem Code einführen würde. Wie in der akzeptierten Antwort angegeben, ist das Löschen eines Nullzeigers ein No-Op. Daher ist die Überprüfung, ob ein Zeiger vor dem Löschen null ist, völlig irrelevant.
Codetaku
47

Ja, es ist sicher.

Das Löschen eines Nullzeigers schadet nicht. Es reduziert häufig die Anzahl der Tests am Ende einer Funktion, wenn die nicht zugewiesenen Zeiger auf Null initialisiert und dann einfach gelöscht werden.


Da der vorige Satz Verwirrung gestiftet hat, ein Beispiel - was nicht ausnahmesicher ist - für das, was beschrieben wird:

void somefunc(void)
{
    SomeType *pst = 0;
    AnotherType *pat = 0;

    
    pst = new SomeType;
    
    if (…)
    {
        pat = new AnotherType[10];
        
    }
    if (…)
    {
        code using pat sometimes
    }

    delete[] pat;
    delete pst;
}

Es gibt alle Arten von Nissen, die mit dem Beispielcode ausgewählt werden können, aber das Konzept ist (ich hoffe) klar. Die Zeigervariablen werden auf Null initialisiert, sodass die deleteOperationen am Ende der Funktion nicht testen müssen, ob sie im Quellcode nicht Null sind. Der Bibliothekscode führt diese Prüfung trotzdem durch.

Jonathan Leffler
quelle
Ich musste das ein paar Mal lesen, um einen Sinn daraus zu machen. Sie müssen bedeuten, sie am Anfang der Methode auf Null zu initialisieren, oder währenddessen, nicht am Ende, sicher? Andernfalls würden Sie nur die Nullung und das Löschen entfernen.
Marquis von Lorne
1
@EJP: Ein nicht ganz unplausibler Umriss einer Funktion könnte sein : void func(void) { X *x1 = 0; Y *y1 = 0; … x1 = new[10] X; … y1 = new[10] Y; … delete[] y1; delete[] x1; }. Ich habe keine Blockstruktur oder Sprünge gezeigt, aber die delete[]Operationen am Ende sind aufgrund der Initialisierungen am Anfang sicher. Wenn etwas bis zum Ende gesprungen wäre, nachdem x1es zugewiesen wurde und bevor y1es zugewiesen wurde und es keine Initialisierung von gab y1, dann würde es ein undefiniertes Verhalten geben - und während der Code vor den Löschungen auf Null (von x1und y1) testen könnte , besteht keine Notwendigkeit dazu so.
Jonathan Leffler
22

Das Löschen eines Nullzeigers hat keine Auswirkung. Es ist nicht unbedingt ein guter Codierungsstil, weil es nicht benötigt wird, aber es ist auch nicht schlecht.

Wenn Sie nach guten Codierungsmethoden suchen, sollten Sie stattdessen intelligente Zeiger verwenden, sodass Sie dies überhaupt nicht müssen delete.

Brian R. Bondy
quelle
20
Die Leute wollen einen NULL-Zeiger löschen, wenn sie nicht sicher sind, ob er NULL enthält. Wenn sie wüssten, dass er NULL ist, würden sie nicht in Betracht ziehen, ihn zu löschen und daher zu fragen ;-).
Tony Delroy
@ Tony: Mein Punkt war nur, dass es keine Auswirkung hat, und das Vorhandensein eines solchen Codes, der einen Zeiger löscht, der manchmal NULL enthält, ist nicht unbedingt schlecht.
Brian R. Bondy
3
Redundante IMO-Überprüfungen sind sicherlich schlecht für Leistung, Lesbarkeit und Wartbarkeit.
Paulm
@paulm OP spricht sicherlich nicht über diese Art von schlecht, sondern eher über Seg Fault / UB.
Winter
8

Um die Antwort von ruslik zu ergänzen , können Sie in C ++ 14 diese Konstruktion verwenden:

delete std::exchange(heapObject, nullptr);
Ashrasmun
quelle
3

Es ist sicher, es sei denn, Sie haben den Löschoperator überladen. Wenn Sie den Löschoperator überladen haben und die Nullbedingung nicht verarbeiten, ist er überhaupt nicht sicher.


quelle
Können Sie eine Erklärung für Ihre Antwort hinzufügen?
Marcin Nabiałek
-2

Ich habe erlebt, dass es nicht ist sicher ist (VS2010), [] NULL (dh Array-Syntax) zu löschen. Ich bin mir nicht sicher, ob dies dem C ++ - Standard entspricht.

Es ist sicher, NULL (skalare Syntax) zu löschen.

pheest
quelle
6
Das ist illegal und ich glaube es nicht.
Konrad Rudolph
5
Sie sollten in der Lage sein, jeden Nullzeiger zu löschen. Wenn es für Sie kaputt geht, haben Sie wahrscheinlich einen Fehler im Code, der es anzeigt.
Mysticial
3
§5.3.2 In der zweiten Alternative (Array löschen) kann der Wert des Operanden löschen ein Nullzeigerwert oder ein Zeigerwert sein, der aus einem vorherigen Array- Neuausdruck resultiert.
SP2danny
1
@Opux Das in der Antwort behauptete Verhalten von VS2010. Wie in anderen Kommentaren erklärt, ist es sicher delete[] NULL.
Konrad Rudolph
1
@Opux Deshalb habe ich "Ich glaube es nicht" geschrieben und nicht "Das ist falsch". Aber ich weiß es immer noch nicht und es wäre eine ziemlich empörende, dumme Verletzung des Standards. VC ++ ist im Allgemeinen ziemlich gut darin, die Einschränkungen des Standards zu befolgen, und die Stellen, an denen es gegen sie verstößt, sind historisch sinnvoll.
Konrad Rudolph