Was bedeutet es, eine “ Nullprüfung ” in C oder C ++ durchzuführen?

Ich habe C ++ gelernt und es fällt mir schwer, Null zu verstehen. In den Tutorials, die ich gelesen habe, wird insbesondere erwähnt, dass eine „Nullprüfung“ durchgeführt wird, aber ich bin nicht sicher, was dies bedeutet oder warum dies erforderlich ist.

  • Was genau ist Null?
  • Was bedeutet es, nach Null zu suchen?
  • Muss ich immer nach Null suchen?

Codebeispiele wären sehr willkommen.

Kommentare

Antwort

In C und C ++ sind Zeiger von Natur aus unsicher, dh wenn Sie einen Zeiger dereferenzieren, Es liegt in Ihrer eigenen Verantwortung, sicherzustellen, dass es auf einen gültigen Punkt verweist. Dies ist Teil der „manuellen Speicherverwaltung“ (im Gegensatz zu den in Sprachen wie Java implementierten automatischen Speicherverwaltungsschemata) , PHP oder die .NET-Laufzeit, mit der Sie ohne großen Aufwand keine ungültigen Referenzen erstellen können.

Eine gängige Lösung, die viele Fehler abfängt, besteht darin, alle Zeiger festzulegen, die auf nichts verweisen als NULL (oder in korrektem C ++ 0) und überprüfen Sie dies, bevor Sie auf den Zeiger zugreifen. Insbesondere ist es üblich, alle Zeiger auf NULL zu initialisieren (es sei denn, Sie haben bereits etwas, auf das Sie sie zeigen können, wenn Sie sie deklarieren) und sie auf NULL zu setzen, wenn Sie delete oder free() sie (es sei denn, sie verlassen unmittelbar danach den Gültigkeitsbereich). Beispiel (in C, aber auch in C ++ gültig):

void fill_foo(int* foo) { *foo = 23; // this will crash and burn if foo is NULL } 

Eine bessere Version:

void fill_foo(int* foo) { if (!foo) { // this is the NULL check printf("This is wrong\n"); return; } *foo = 23; } 

Ohne die Nullprüfung führt das Übergeben eines NULL-Zeigers an diese Funktion zu einem Segfault, und Sie können nichts tun. Das Betriebssystem beendet einfach Ihren Prozess und führt möglicherweise einen Core-Dump durch oder öffnet einen Dialog mit einem Absturzbericht. Wenn die Nullprüfung aktiviert ist, können Sie eine ordnungsgemäße Fehlerbehandlung durchführen und ordnungsgemäß beheben. Beheben Sie das Problem selbst, brechen Sie den aktuellen Vorgang ab, schreiben Sie einen Protokolleintrag, benachrichtigen Sie den Benutzer, was auch immer angemessen ist.

Kommentare

  • @MrLister was meinst du, Nullprüfungen funktionieren in C ++ nicht ‚? Sie müssen den Zeiger nur auf null initialisieren, wenn Sie ihn deklarieren.
  • Ich meine, Sie müssen daran denken, den Zeiger auf NULL zu setzen, sonst hat er ‚ gewonnen t arbeiten. Und wenn Sie sich erinnern, mit anderen Worten, wenn Sie wissen , dass der Zeiger NULL ist, müssen Sie ‚ ohnehin nicht fill_foo aufrufen. fill_foo prüft, ob der Zeiger einen Wert hat, nicht, ob der Zeiger einen gültigen Wert hat. In C ++ ist nicht garantiert, dass Zeiger entweder NULL sind oder einen gültigen Wert haben.
  • Ein assert () wäre hier eine bessere Lösung. Es macht keinen Sinn, ‚ zu versuchen, “ sicher zu sein „. Wenn NULL übergeben wurde, ist ‚ offensichtlich falsch. Warum also nicht einfach explizit abstürzen, um den Programmierer darauf aufmerksam zu machen? (Und in der Produktion spielt es keine Rolle, ‚, weil Sie ‚ bewiesen haben, dass niemand fill_foo aufruft () mit NULL, richtig? Wirklich, es ist ‚ nicht so schwer.)
  • ‚ nicht vergessen um zu erwähnen, dass eine noch bessere Version dieser Funktion Referenzen anstelle von Zeigern verwenden sollte, wodurch die NULL-Prüfung überflüssig wird.
  • Dies ist nicht das, worum es bei der manuellen Speicherverwaltung geht, und ein verwaltetes Programm wird ebenfalls in die Luft jagen ( oder zumindest eine Ausnahme auslösen, genau wie ein natives Programm in den meisten Sprachen), wenn Sie versuchen, eine Nullreferenz dereferenzieren.

Antwort

Die anderen Antworten deckten ziemlich genau Ihre Frage ab. Eine Nullprüfung wird durchgeführt, um sicherzustellen, dass der Zeiger, den Sie erhalten haben, tatsächlich auf eine gültige Instanz eines Typs (Objekte, Grundelemente usw.) verweist.

Ich werde hier meinen eigenen Ratschlag hinzufügen. Vermeiden Sie jedoch Nullprüfungen. 🙂 Nullprüfungen (und andere Formen der defensiven Programmierung) machen den Code unübersichtlich und machen ihn tatsächlich fehleranfälliger als andere Fehlerbehandlungstechniken.

Meine Lieblingstechnik, wenn es darum geht Objektzeiger verwenden das Null-Objektmuster . Dies bedeutet, dass ein (Zeiger – oder noch besser Verweis auf ein) leeres Array oder eine leere Liste anstelle von null oder zurückgegeben wird Rückgabe einer leeren Zeichenfolge („“) anstelle von null oder sogar der Zeichenfolge „0“ (oder etwas, das im Kontext „nichts“ entspricht), wenn Sie erwarten, dass sie in eine Ganzzahl analysiert wird.

Als Bonus ist hier „etwas, das Sie vielleicht nicht über den Nullzeiger gewusst haben, der (zuerst offiziell) von CAR Hoare für die Sprache Algol W im Jahr 1965 implementiert wurde.

Ich nenne es meinen Milliarden-Dollar-Fehler. Es war die Erfindung der Nullreferenz im Jahr 1965. Zu dieser Zeit entwarf ich das erste umfassende Typsystem für Referenzen in einem Objekt orientierte Sprache (ALGOL W). Mein Ziel war es sicherzustellen, dass jede Verwendung von Referenzen absolut sicher ist und die Überprüfung automatisch vom Compiler durchgeführt wird. Aber ich konnte der Versuchung nicht widerstehen, eine Nullreferenz einzugeben, einfach weil es so war einfach zu implementieren. Dies hat zu unzähligen Fehlern, Schwachstellen und Systemabstürzen geführt, die in den letzten vierzig Jahren wahrscheinlich eine Milliarde Dollar an Schmerzen und Schäden verursacht haben.

Kommentare

  • Null-Objekt ist noch schlimmer als nur ein Null-Zeiger. Wenn ein Algorithmus X Daten Y benötigt, die Sie nicht haben, dann ist dies ein Fehler in Ihrem Programm , den Sie einfach verbergen, indem Sie so tun, als ob Sie dies tun.
  • Es hängt davon ab Der Kontext und so oder so das Testen auf “ Datenpräsenz “ schlägt das Testen auf Null in meinem Buch. Wenn ein Algorithmus beispielsweise an einer Liste arbeitet und die Liste leer ist, hat der Algorithmus meiner Erfahrung nach einfach nichts zu tun, und dies wird erreicht, indem nur Standardsteueranweisungen wie for / foreach verwendet werden.
  • Wenn der Algorithmus nichts zu tun hat, warum rufen Sie ihn dann überhaupt auf? Und der Grund, warum Sie es vielleicht zuerst aufrufen wollten, ist , weil es etwas Wichtiges tut .
  • @DeadMG Weil es bei Programmen um Eingabe geht und in der realen Welt anders Hausaufgaben, Eingabe kann irrelevant sein (zB leer). Code wird immer noch so oder so aufgerufen. Sie haben zwei Möglichkeiten: Entweder überprüfen Sie die Relevanz (oder Leere) oder Sie entwerfen Ihre Algorithmen so, dass sie gut lesen und funktionieren, ohne explizit mit bedingten Anweisungen auf Relevanz zu prüfen.
  • Ich bin hierher gekommen, um fast die zu machen gleichen Kommentar, also gab Ihnen stattdessen meine Stimme. Ich möchte jedoch auch hinzufügen, dass dies für ein größeres Problem von Zombie-Objekten repräsentativ ist – immer dann, wenn Sie Objekte mit mehrstufiger Initialisierung (oder Zerstörung) haben, die nicht vollständig lebendig, aber nicht ganz tot sind. Wenn Sie “ sicheren “ Code in Sprachen ohne deterministische Finalisierung sehen, der in jeder Funktion Überprüfungen hinzugefügt hat, um festzustellen, ob das Objekt entsorgt wurde, Es ist dieses allgemeine Problem bei der Aufzucht des Kopfes von ‚. Sie sollten niemals if-null verwenden, sondern mit Zuständen arbeiten, die die Objekte enthalten, die sie für ihre Lebensdauer benötigen.

Antwort

Der Nullzeigerwert repräsentiert ein genau definiertes „Nirgendwo“. Es ist ein ungültiger Zeigerwert, der garantiert ungleich mit jedem anderen Zeigerwert verglichen wird. Der Versuch, einen Nullzeiger zu dereferenzieren, führt zu einem undefinierten Verhalten und führt normalerweise zu einem Laufzeitfehler. Stellen Sie daher sicher, dass ein Zeiger nicht NULL ist, bevor Sie versuchen, ihn zu dereferenzieren. Eine Reihe von C- und C ++ – Bibliotheksfunktionen geben einen Nullzeiger zurück, um eine Fehlerbedingung anzuzeigen. Beispielsweise gibt die Bibliotheksfunktion malloc einen Nullzeigerwert zurück, wenn sie die Anzahl der angeforderten Bytes nicht zuordnen kann, und der Versuch, über diesen Zeiger auf den Speicher zuzugreifen, führt (normalerweise) zu einem Laufzeitfehler:

int *p = malloc(sizeof *p * N); p[0] = ...; // this will (usually) blow up if malloc returned NULL 

Wir müssen also sicherstellen, dass der Aufruf malloc erfolgreich war, indem wir den Wert von überprüfen p gegen NULL:

int *p = malloc(sizeof *p * N); if (p != NULL) // or just if (p) p[0] = ...; 

Halten Sie jetzt eine Minute an Ihren Socken fest, dies wird eine etwas holprig.

Es gibt einen Nullzeiger Wert und eine Nullzeiger Konstante , und die beiden sind nicht unbedingt gleich. Der Nullzeiger value ist der Wert, den die zugrunde liegende Architektur verwendet, um „nirgendwo“ darzustellen. Dieser Wert kann 0x00000000 oder 0xFFFFFFFF oder 0xDEADBEEF oder etwas völlig anderes sein. Nehmen Sie nicht an, dass der Nullzeiger -Wert immer 0 ist.

Die Nullzeiger-Konstante , OTOH, ist immer ein 0-wertiger Integralausdruck. In Bezug auf Ihren Quellcode steht 0 (oder ein beliebiger integraler Ausdruck, der 0 ergibt) für einen Nullzeiger. Sowohl C als auch C ++ definieren das NULL-Makro als Nullzeigerkonstante. Wenn Ihr Code kompiliert wird, wird die Nullzeiger -Konstante durch den entsprechenden Nullzeiger -Wert im generierten Maschinencode ersetzt.

Beachten Sie außerdem, dass NULL nur einer von vielen möglichen ungültigen Zeigerwerten ist. Wenn Sie eine Auto-Zeiger-Variable deklarieren, ohne sie explizit zu initialisieren, z. B.

int *p; 

, ist der ursprünglich in der Variablen gespeicherte Wert unbestimmt . und entspricht möglicherweise nicht einer gültigen oder zugänglichen Speicheradresse. Leider gibt es keine (tragbare) Möglichkeit, festzustellen, ob ein Nicht-NULL-Zeigerwert gültig ist oder nicht, bevor Sie versuchen, ihn zu verwenden. Wenn Sie sich also mit Zeigern befassen, ist es normalerweise eine gute Idee, sie explizit zu initialisieren NULL, wenn Sie sie deklarieren, und um sie auf NULL zu setzen, wenn sie nicht aktiv auf etwas zeigen.

Beachten Sie, dass dies in C eher ein Problem als in C ++ ist. idiomatisches C ++ sollte nicht allzu oft Zeiger verwenden.

Antwort

Es gibt einige Methoden, die alle im Wesentlichen dasselbe tun Sache.

 int *foo = NULL; //sometimes set to 0x00 or 0 or 0L instead of NULL 

Nullprüfung (prüfen, ob der Zeiger null ist), Version A

 if( foo == NULL) 

Nullprüfung, Version B

 if( !foo ) //since NULL is defined as 0, !foo will return a value from a null pointer 

Nullprüfung, Version C

 if( foo == 0 ) 

Von den dreien bevorzuge ich die erste Prüfung, da sie zukünftigen Entwicklern explizit mitteilt, wonach Sie gesucht haben, UND es macht deutlich, dass Sie erwartet haben, dass foo ein Zeiger ist.

Antwort

Sie tun es nicht. Der einzige Grund für die Verwendung eines Zeigers in C ++ besteht darin, dass Sie ausdrücklich das Vorhandensein von Nullzeigern wünschen. Andernfalls können Sie eine Referenz verwenden, die sowohl semantisch einfacher zu verwenden ist als auch Nicht-Null garantiert.

Kommentare

  • @James: ‚ neu ‚ im Kernelmodus?
  • @James: Eine Implementierung von C ++, die die Funktionen darstellt, die eine signifikante Mehrheit von C ++ bietet Programmierer genießen. Dies umfasst alle C ++ 03-Sprachfunktionen (außer export) und alle C ++ 03-Bibliotheksfunktionen und TR1 und ein guter Teil von C ++ 11.
  • Ich wünschte, die Leute würden ‚ nicht sagen, dass “ Referenzen garantieren Nicht-Null. “ Sie geben nicht ‚ t. Es ist so einfach, eine Nullreferenz wie einen Nullzeiger zu generieren, und sie verbreiten sich auf die gleiche Weise.
  • @Stargazer: Die Frage ist 100% redundant, wenn Sie die Tools nur so verwenden, wie es die Sprachdesigner und gut tun Die Praxis schlägt vor, dass Sie dies tun sollten.
  • @DeadMG, es spielt keine Rolle, ob es redundant ist. ‚ Sie haben ‚ die Frage nicht beantwortet. Ich ‚ sage es noch einmal: -1.

Antwort

Wenn Sie den NULL-Wert nicht überprüfen, insbesondere wenn dies ein Zeiger auf eine Struktur ist, ist möglicherweise eine Sicherheitslücke aufgetreten – NULL-Zeiger-Dereferenzierung. Die NULL-Zeiger-Dereferenzierung kann zu einigen anderen schwerwiegenden Sicherheitslücken führen, z. B. Pufferüberlauf. Race-Bedingung … die es Angreifern ermöglichen kann, die Kontrolle über Ihren Computer zu übernehmen.

Viele Softwareanbieter wie Microsoft, Oracle, Adobe, Apple … veröffentlichen Software-Patches, um diese Sicherheitslücken zu beheben. Ich denke, Sie sollten dies tun Überprüfen Sie den NULL-Wert jedes Zeigers 🙂

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.