it-swarm.com.de

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. Insbesondere in den von mir gelesenen Tutorials wird erwähnt, dass eine "Nullprüfung" durchgeführt wird, aber ich bin mir 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?

Alle Codebeispiele wären sehr dankbar.

21
kdi

In C und C++ sind Zeiger von Natur aus unsicher. Wenn Sie einen Zeiger dereferenzieren, liegt es in Ihrer eigenen Verantwortung, sicherzustellen, dass er auf eine gültige Stelle verweist. Dies ist ein Teil dessen, worum es bei der "manuellen Speicherverwaltung" geht (im Gegensatz zu den automatischen Speicherverwaltungsschemata, die in Sprachen wie Java, PHP oder der .NET-Laufzeit implementiert sind und die es Ihnen nicht ermöglichen, ungültige Referenzen ohne erheblichen Aufwand zu erstellen).

Eine häufige Lösung, die viele Fehler abfängt, besteht darin, alle Zeiger, die auf nichts verweisen, als NULL (oder in korrektem C++ als 0) Zu setzen und dies zu überprüfen, 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 sie delete oder free() _ (es sei denn, sie verlassen unmittelbar danach den Anwendungsbereich). 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 Absturzbericht-Dialog. Mit der Nullprüfung 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.

27
tdammers

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 jedoch meinen eigenen Rat hinzufügen. Vermeiden Sie Nullprüfungen. :) Null überprüft den Code (und andere Formen der defensiven Programmierung) und macht ihn fehleranfälliger als andere Fehlerbehandlungstechniken.

Meine bevorzugte Technik, wenn es um Objektzeiger geht, ist die Verwendung des Null-Objekt-Musters . Das bedeutet, dass ein (Zeiger - oder noch besser ein Verweis auf ein) leeres Array oder eine leere Liste anstelle von null oder eine leere Zeichenfolge ("") anstelle von null oder sogar die Zeichenfolge "0" (oder etwas, das "nichts" entspricht) zurückgegeben wird "im Kontext), wo Sie erwarten, dass es auf eine Ganzzahl analysiert wird.

Als Bonus gibt es hier etwas, das Sie möglicherweise nicht über den Nullzeiger gewusst haben, der (zuerst formal) von C.A.R. Hoare für die Sprache ALGOL W im Jahr 1965.

Ich nenne es meinen Milliardenfehler. Es war die Erfindung der Nullreferenz im Jahr 1965. Zu dieser Zeit entwarf ich das erste umfassende Typsystem für Referenzen in einer objektorientierten Sprache (ALGOL W). Mein Ziel war es sicherzustellen, dass jede Verwendung von Referenzen absolut sicher ist, wobei die Überprüfung automatisch vom Compiler durchgeführt wird. Aber ich konnte der Versuchung nicht widerstehen, eine Nullreferenz einzufügen, einfach weil es so einfach zu implementieren war. 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.

7
Yam Marcovic

Der Nullzeigerwert repräsentiert ein genau definiertes "Nirgendwo"; Es ist ein ngü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 von malloc erfolgreich war, indem wir den Wert von p gegen NULL prüfen:

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

Jetzt haltet euch eine Minute an euren Socken fest, das wird ein bisschen holprig.

Es gibt einen Nullzeiger Wert und einen Nullzeiger Konstante, und die beiden sind nicht unbedingt gleich. Der Nullzeiger Wert 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.

Der Nullzeiger Konstante, OTOH, ist immer ein 0-wertiger Integralausdruck. Für Ihr Quellcode steht 0 (oder ein ganzer 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 der Nullzeiger Konstante durch den entsprechenden Nullzeiger Wert im generierten Maschinencode ersetzt.

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

int *p;

der ursprünglich in der Variablen gespeicherte Wert ist nbestimmt und entspricht möglicherweise keiner 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 also mit Zeigern arbeiten, ist es normalerweise eine gute Idee, sie explizit auf NULL zu initialisieren, wenn Sie sie deklarieren, und 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.

4
John Bode

Es gibt ein paar Methoden, die alle im Wesentlichen dasselbe tun.

 Int * foo = NULL; // manchmal auf 0x00 oder 0 oder 0L anstatt auf NULL setzen 

nullprüfung (prüfen Sie, ob der Zeiger null ist), Version A.

 If (foo == NULL) 

nullprüfung, Version B.

 if (! foo) // da NULL als 0 definiert ist, gibt! Foo einen Wert von einem Nullzeiger zurück 

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.

3
user53019

Das tust du 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 garantiert, dass sie nicht null ist.

2
DeadMG