Kann ich if (Zeiger) anstelle von if (Zeiger! = NULL) verwenden?


171

Ist es sicher, einen Zeiger darauf zu überprüfen, dass man nicht NULLeinfach schreibt, if(pointer)oder muss ich ihn verwenden if(pointer != NULL)?


13
Die Wahrheit ist, wenn Sie eine explizite Prüfung verwenden, ist es genauso effektiv - und oft bevorzugt -, gegen 0oder zu testen nullptr. ( NULList ein C'ism und erfordert das Einfügen einer Header-Datei.)
CHA

5
@danijar Sie können nullptr in modernem C ++ verwenden.
SurvivalMachine

9
@cHao Wo ist der Punkt in "Streben nach Kompatibilität mit C"?
QDII

5
@danijar: Ja, Sie sollten NULLab diesem Zeitpunkt nicht mehr in C ++ verwenden, da NULLes sich um ein implementierungsabhängiges Makro handelt, das möglicherweise zu mehrdeutigen Verhaltensweisen führt.
Alok Save

3
Obwohl dies nicht der Fall ist, lesen Sie in dieser Ideone-Live-Demo nach, warum Sie "NULL" und "0" für Zeiger in C ++ vermeiden sollten: ideone.com/tbvXNs
kfsone

Antworten:


197

Sie können; Der Nullzeiger wird implizit in einen booleschen Wert false umgewandelt, während Nicht-Nullzeiger in true konvertiert werden. Aus dem C ++ 11-Standard, Abschnitt über Boolesche Konvertierungen:

Ein Wert von arithmetischer, nicht skalierter Aufzählung, Zeiger oder Zeiger auf Elementtyp kann in einen Wert vom Typ konvertiert werden bool. Ein Nullwert, ein Nullzeigerwert oder ein Nullelementzeigerwert wird in konvertiert false. Jeder andere Wert wird in konvertiert true . Ein Wert vom Typ std::nullptr_t kann in einen Wert vom Typ konvertiert werden bool . der resultierende Wert ist false .


42

Ja du könntest.

  • Ein Nullzeiger wird implizit in false konvertiert
  • Ein Nicht-Null-Zeiger wird in true konvertiert.

Dies ist Teil der C ++ - Standardkonvertierung, die in die Boolesche Konvertierungsklausel fällt :

§ 4.12 Boolesche Konvertierungen

Ein Wert von arithmetischer, nicht skalierter Aufzählung, Zeiger oder Zeiger auf Elementtyp kann in einen Wert vom Typ bool konvertiert werden. Ein Nullwert, ein Nullzeigerwert oder ein Nullelementzeigerwert wird in false konvertiert. Jeder andere Wert wird in true konvertiert. Ein Wert vom Typ std :: nullptr_t kann in einen Wert vom Typ bool konvertiert werden. Der resultierende Wert ist falsch.


29

Ja, du kannst. Tatsächlich bevorzuge ich die Verwendung, if(pointer)weil das Lesen und Schreiben einfacher ist, wenn man sich erst einmal daran gewöhnt hat.

Beachten Sie auch, dass C ++ 11 eingeführt wurde, nullptrdas gegenüber bevorzugt wird NULL.


10
Ein Zeiger ist kein boolescher Ausdruck. Es ist implizit konvertiert. Wenn es besser ist zu lesen, wenn Sie sich an diese Konvertierung erinnern müssen, um zu verstehen, ist Ihre Meinung. Es ist nur eine Art von Codierungsstil.
Harper

7
@harper Man kann sagen, es ist ein Codierungsstil. Aber Sie können dieselbe Logik auf if(som_integer)vs if(some_integer != 0)anwenden, weil Ganzzahlen auch keine Booleschen Werte sind, oder? Ich bevorzuge es zu vermeiden 0oder NULLin einer if-Anweisung.
Yu Hao

13
Ich würde zustimmen, dass es einfach um den Codierungsstil geht. Ich bin gekommen, um if (pointer)mich selbst zu bevorzugen , aber if (ptr != nullptr)scheint mir vollkommen legitim zu sein. Wenn ich andererseits jemanden in meinem Team sehen if (some_integer)würde, der geschrieben hat, würde ich ihn dazu bringen, es zu ändern if (some_integer != 0). Ich werde jedoch nicht so tun, als wäre dies keine relativ willkürliche Präferenz meinerseits - ich ziehe es einfach vor, Zeiger und ganze Zahlen nicht gleich zu behandeln.
Joel

1
@YuHao Und da es sich um einen Codestil handelt, würde ich nicht "es wird bevorzugt" angeben, sondern "ich bevorzuge".
Harper

5
@ franji1 Wie wäre es dann if(isReady) if(filePtr) if(serviceNo)? Absichtlich schlechte Variablennamen zu erstellen, bedeutet in diesem Fall nicht viel. Wie auch immer, ich habe Ihren Standpunkt bereits verstanden und verstanden, aber ich kann darauf bestehen, meinen eigenen Codierungsstil selbst zu verwenden, OK?
Yu Hao

13

Die Frage wird beantwortet, aber ich möchte meine Punkte hinzufügen.

Ich werde immer lieber if(pointer)anstelle von if(pointer != NULL)und if(!pointer)anstelle von if(pointer == NULL):

  • Es ist einfach, klein
  • Weniger Chancen auf einen fehlerhaften Code zu schreiben, nehme an, wenn ich Gleichheitsprüfung Operator falsch geschrieben ==mit =
    if(pointer == NULL)kann falsch geschrieben , if(pointer = NULL)damit ich es vermeiden, am besten ist einfach if(pointer).
    (Ich schlug auch eine Yoda-Bedingung in einer Antwort vor , aber das ist eine andere Sache)

  • In ähnlicher Weise while (node != NULL && node->data == key)werde ich einfach schreiben while (node && node->data == key), was für mich offensichtlicher ist (zeigt, dass Kurzschluss verwendet wird).

  • (kann ein dummer Grund sein) Da NULL ein Makro ist, nehmen wir an, dass jemand versehentlich mit einem anderen Wert neu definiert.

6
Die Verwendung von = anstelle von == generiert fast immer eine Compiler-Warnung in den Tagen, in denen dies nicht der
Fall war,

@ Paulm, dass ich gerade diesen Punkt hinzugefügt habe, der Yoda-Zustand genannt wird. Einige Leute mögen ihn nicht, da er weniger lesbar ist.
Grijesh Chauhan

2
(boolean expression)? true : falseist völlig sinnlos. Der Ausdruck wertet entweder zu trueoder zu aus false; Was Sie sagen, ist "wenn es wahr ist, geben Sie mir wahr, wenn es falsch ist, geben Sie mir falsch". Kurz gesagt: Es entspricht vollständig dem booleschen Ausdruck selbst. Beachten Sie, dass dies node == NULLein boolescher Ausdruck ist. Übrigens geben Ihre beiden Implementierungen genau das Gegenteil voneinander zurück. Entweder Sie wollen !=in der ersten oder nur eine !in der zweiten.
Celtschk

Übrigens besteht ein möglicher Schutz =statt ==darin, Ihre Variablen zu consterstellen , wann immer dies möglich ist. Sie könnten beispielsweise Ihre Funktion als definieren isEmnpy(node* const head) { ... }, und dann würde der Compiler die Kompilierung ablehnen, wenn Sie versehentlich node = NULLstatt schreiben würden node == NULL. Das funktioniert natürlich nur für Variablen, die Sie wirklich nicht ändern müssen.
Celtschk

2
Denn die Smart-Pointer-Klassen müssen T* get() conststattdessen operator T*() constimplizite Konvertierungen vermeiden. Sie haben jedoch eine operator bool() const.
StellarVortex

13

Das explizite Überprüfen auf NULL kann dem Compiler einen Hinweis darauf geben, was Sie versuchen, was dazu führen kann, dass er weniger fehleranfällig ist.

Geben Sie hier die Bildbeschreibung ein


8

Ja, du kannst. Die Möglichkeit, Werte implizit mit Nullen zu vergleichen, wurde von C geerbt und ist in allen Versionen von C ++ vorhanden. Sie können auch if (!pointer)Zeiger auf NULL prüfen.


2

Die relevanten Anwendungsfälle für Nullzeiger sind

  • Umleitung zu einem tieferen Baumknoten, der möglicherweise nicht vorhanden ist oder noch nicht verknüpft wurde. Das ist etwas, das Sie immer eng in einer speziellen Klasse zusammenfassen sollten, damit Lesbarkeit oder Prägnanz hier kein so großes Problem darstellen.
  • Dynamische Besetzungen. Das Umwandeln eines Basisklassenzeigers in eine bestimmte abgeleitete Klasse (etwas, das Sie erneut vermeiden sollten, das Sie jedoch manchmal für erforderlich halten) ist immer erfolgreich, führt jedoch zu einem Nullzeiger, wenn die abgeleitete Klasse nicht übereinstimmt. Eine Möglichkeit, dies zu überprüfen, ist

    Derived* derived_ptr = dynamic_cast<Derived*>(base_ptr);
    if(derived_ptr != nullptr) { ... }

    (oder vorzugsweise auto derived_ptr = ...). Dies ist schlecht, da der (möglicherweise ungültige, dh null) abgeleitete Zeiger außerhalb des ifBereichs des Sicherheitsschutzblocks bleibt . Dies ist nicht erforderlich, da Sie in C ++ boolesche konvertierbare Variablen in eine ifBedingung einführen können :

    if(auto derived_ptr = dynamic_cast<Derived*>(base_ptr)) { ... }

    Das ist nicht nur kürzer und bereichssicher, sondern auch viel klarer in seiner Absicht: Wenn Sie in einer separaten if-Bedingung nach null suchen, fragt sich der Leser: "OK, derived_ptrdarf hier also nicht null sein ... nun, warum sollte es ist null? " Während die einzeilige Version sehr deutlich sagt : „Wenn Sie sicher werfen können base_ptrzu Derived*, dann verwenden Sie es für ...“.

    Das Gleiche funktioniert genauso gut für alle anderen Operationen mit möglichen Fehlern, die einen Zeiger zurückgeben. IMO sollten Sie dies jedoch generell vermeiden: Es ist besser, so etwas wie boost::optionalden "Container" für Ergebnisse möglicherweise fehlgeschlagener Operationen zu verwenden, als Zeiger.

Wenn also der Hauptanwendungsfall für Nullzeiger immer in einer Variation des impliziten Cast-Stils geschrieben werden sollte, würde ich sagen, dass es aus Konsistenzgründen gut ist , diesen Stil immer zu verwenden, dh ich würde mich für if(ptr)mehr einsetzen if(ptr!=nullptr).


Ich fürchte, ich muss mit einer Anzeige enden: Die if(auto bla = ...)Syntax ist eigentlich nur eine etwas umständliche Annäherung an die eigentliche Lösung solcher Probleme: Pattern Matching . Warum sollten Sie zuerst eine Aktion erzwingen (z. B. einen Zeiger werfen) und dann bedenken, dass möglicherweise ein Fehler vorliegt? Ich meine, es ist lächerlich, nicht wahr? Es ist wie, Sie haben etwas zu essen und möchten Suppe machen. Sie geben es Ihrem Assistenten mit der Aufgabe, den Saft zu extrahieren, falls es sich um ein weiches Gemüse handelt. Du siehst es dir nicht zuerst an. Wenn Sie eine Kartoffel haben, geben Sie sie immer noch Ihrem Assistenten, aber er schlägt sie Ihnen mit einem Fehlerbericht ins Gesicht. Ah, zwingende Programmierung!

Viel besser: Betrachten Sie sofort alle Fälle, auf die Sie stoßen könnten. Dann handeln Sie entsprechend. Haskell:

makeSoupOf :: Foodstuff -> Liquid
makeSoupOf p@(Potato{..}) = mash (boil p) <> water
makeSoupOf vegetable
 | isSoft vegetable  = squeeze vegetable <> salt
makeSoupOf stuff  = boil (throwIn (water<>salt) stuff)

Haskell hat auch spezielle Werkzeuge für den Fall, dass wirklich ernsthafte Fehler möglich sind (sowie für eine ganze Reihe anderer Dinge): Monaden. Aber das ist nicht der Ort, um diese zu erklären.

⟨/Inserat⟩


1
Ich kann nur einen Satz in diesem endlosen Estrich sehen, der die Frage tatsächlich beantwortet.
Marquis von Lorne

@EJP: Wenn Sie die Frage wörtlich nehmen (" kann ich verwenden"), wird sie überhaupt nicht explizit beantwortet (die Antwort lautet einfach "Ja"). Ich habe versucht , Gründe für die geben , warum der OP sollte in der Tat Gebrauch if(ptr)statt if(ptr != nullptr), zu der es ein bisschen mehr zu sagen.
links um den

1

ja natürlich! In der Tat ist das Schreiben von if (Zeiger) eine bequemere Schreibweise als if (Zeiger! = NULL), weil: 1. es leicht zu debuggen ist 2. leicht zu verstehen ist 3. wenn versehentlich der Wert von NULL definiert wird, dann stürzt auch der Code nicht ab




-1

Ja, Sie können dies immer tun, da die 'IF'-Bedingung nur dann ausgewertet wird, wenn die darin enthaltene Bedingung erfüllt ist. C hat keinen booleschen Rückgabetyp und gibt daher einen Wert ungleich Null zurück, wenn die Bedingung wahr ist, während 0 zurückgegeben wird, wenn sich die Bedingung in 'IF' als falsch herausstellt. Der standardmäßig zurückgegebene Wert ungleich Null ist 1. Somit sind beide Arten des Schreibens des Codes korrekt, während ich immer den zweiten vorziehen werde.


1
Der Wert ungleich Null ist standardmäßig undefiniert, wenn ich mich richtig erinnere.
Marquis von Lorne

-1

Ich denke als Faustregel, ob Ihr if-Ausdruck als neu geschrieben werden kann

const bool local_predicate = *if-expression*;
if (local_predicate) ...

so dass es KEINE WARNUNGEN verursacht, sollte DAS der bevorzugte Stil für den if-Ausdruck sein . (Ich weiß, dass ich Warnungen bekomme, wenn ich einem C ++ ein altes C BOOL( #define BOOL int) zuweise bool, geschweige denn Zeiger.)


-1

"Ist es sicher..?" ist eine Frage zum Sprachstandard und zum generierten Code.

"Ist das eine gute Praxis?" ist eine Frage, wie gut die Aussage von einem beliebigen menschlichen Leser der Aussage verstanden wird. Wenn Sie diese Frage stellen, deutet dies darauf hin, dass die "sichere" Version für zukünftige Leser und Autoren weniger klar ist.


Ich wollte fragen, ob es sicher ist. Deshalb habe ich diesen Wortlaut verwendet. Was Sie hier geschrieben haben, ist jedoch keine Antwort auf die Frage. Stattdessen sollte es ein Kommentar unter der Frage sein. Sie können die Antwort löschen und dann einen Kommentar unter der Frage hinzufügen.
Danijar

@danijar Erinnerst du dich nicht, als du neu bei StackOverflow warst und erfolglos nach dem Abschnitt 'Kommentar' gesucht hast? Jemand mit 7 Ruf kann das nicht.
Broxzier

@ JimBalter Was sehr verwirrend ist, da Sie sehen können, wie andere dies tun. Als ich neu bei SO war, hat mich jemand dafür verantwortlich gemacht.
Broxzier

@ JimBalter Ich morde und stehle nicht. Ich sagte Danijar, dass Fred Mitchell ein neuer Benutzer sei und keine Kommentare posten könne.
Broxzier

@ JimBalter Was du heute angefangen hast. Auch du bist derjenige, der stattdessen nicht versteht. Dieser Kommentar unterstützt nur die Verwirrung davon.
Broxzier
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.