686 Stimmen

Was genau ist nullptr?

Wir haben jetzt C++11 mit vielen neuen Funktionen. Eine interessante und verwirrende (zumindest für mich) ist die neue nullptr .

Nun, kein Grund mehr für das unangenehme Makro NULL .

int* x = nullptr;
myclass* obj = nullptr;

Dennoch verstehe ich nicht, wie nullptr Werke. Zum Beispiel, Wikipedia-Artikel sagt:

C++11 korrigiert dies durch die Einführung einer neuen Stichwort als unterscheidbare Null-Zeiger-Konstante zu verwenden: nullptr. Sie ist von Typ nullptr_t der implizit konvertierbar und mit jedem Zeigertyp oder Zeiger-auf-Glied-Typ vergleichbar ist. Er ist nicht implizit konvertierbar oder vergleichbar mit ganzzahligen Typen, mit Ausnahme von bool.

Inwiefern ist es ein Schlüsselwort und eine Instanz eines Typs?

Haben Sie noch ein weiteres Beispiel (neben dem aus Wikipedia), in dem nullptr ist besser als die gute alte 0 ?

0voto

Red.Wave Punkte 1774

0 war früher der einzige Integer-Wert, der als Cast-freier Initialisierer für Zeiger verwendet werden konnte: Sie können Zeiger nicht mit anderen Integer-Werten ohne Cast initialisieren. Sie können 0 als ein consexpr Singleton betrachten, das syntaktisch einem Integer-Literal ähnelt. Es kann jeden Zeiger oder jede ganze Zahl initialisieren. Überraschenderweise hat es aber keinen eigenen Typ: Es ist ein int . Wie kommt es also, dass 0 Zeiger initialisieren kann und 1 nicht? Eine praktische Antwort war, dass wir eine Möglichkeit brauchen, den Nullwert eines Zeigers zu definieren und eine direkte implizite Umwandlung von int auf einen Zeiger ist fehleranfällig. So wurde 0 zu einem echten Freak aus der prähistorischen Ära. nullptr wurde vorgeschlagen, ein echtes Singleton constexpr Darstellung von Null-Wert zu initialisieren Zeiger sein. Es kann nicht verwendet werden, um Integer direkt zu initialisieren und beseitigt Mehrdeutigkeiten, die mit der Definition von NULL in Form von 0. nullptr konnte als Bibliothek mit std-Syntax definiert werden, schien aber semantisch eine fehlende Kernkomponente zu sein. NULL ist jetzt veraltet zugunsten von nullptr es sei denn, eine Bibliothek definiert es als nullptr .

0voto

l.k Punkte 199

Hier ist der LLVM-Header.

// -*- C++ -*-
//===--------------------------- __nullptr --------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef _LIBCPP_NULLPTR
#define _LIBCPP_NULLPTR

#include <__config>

#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
#pragma GCC system_header
#endif

#ifdef _LIBCPP_HAS_NO_NULLPTR

_LIBCPP_BEGIN_NAMESPACE_STD

struct _LIBCPP_TEMPLATE_VIS nullptr_t
{
    void* __lx;

    struct __nat {int __for_bool_;};

    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR nullptr_t() : __lx(0) {}
    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR nullptr_t(int __nat::*) : __lx(0) {}

    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR operator int __nat::*() const {return 0;}

    template <class _Tp>
        _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
        operator _Tp* () const {return 0;}

    template <class _Tp, class _Up>
        _LIBCPP_INLINE_VISIBILITY
        operator _Tp _Up::* () const {return 0;}

    friend _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR bool operator==(nullptr_t, nullptr_t) {return true;}
    friend _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR bool operator!=(nullptr_t, nullptr_t) {return false;}
};

inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR nullptr_t __get_nullptr_t() {return nullptr_t(0);}

#define nullptr _VSTD::__get_nullptr_t()

_LIBCPP_END_NAMESPACE_STD

#else  // _LIBCPP_HAS_NO_NULLPTR

namespace std
{
    typedef decltype(nullptr) nullptr_t;
}

#endif  // _LIBCPP_HAS_NO_NULLPTR

#endif  // _LIBCPP_NULLPTR

(Vieles lässt sich mit einer schnellen Recherche aufdecken grep -r /usr/include/*` )

Eine Sache, die auffällt, ist der Betreiber * Überladung (0 zurückzugeben ist viel freundlicher als Segfaulting...). Eine andere Sache ist, dass es nicht kompatibel mit dem Speichern einer Adresse aussieht überhaupt . Verglichen mit dem Schleudern von void*'s und der Übergabe von NULL-Ergebnissen an normale Zeiger als Sentinel-Werte würde das natürlich den "Vergiss nicht, es könnte eine Bombe sein"-Faktor verringern.

0voto

jignatius Punkte 5953

Nach Angaben von cpp-Referenz , nullptr ist ein Schlüsselwort, das:

bezeichnet die Zeigerliteral . Es handelt sich um einen pr-Wert vom Typ std::nullptr_t . Es gibt implizite Konvertierungen von nullptr auf den Nullzeigerwert von beliebiger Zeigertyp y jeder Zeiger auf einen Mitgliedstyp . Ähnliche Konvertierungen gibt es für jede Nullzeigerkonstante, also auch für Werte des Typs std::nullptr_t sowie das Makro NULL .

Also nullptr ist ein Wert eines bestimmten Typs std::nullptr_t , nicht int . Er konvertiert implizit in den Null-Zeigerwert eines beliebigen Zeigertyps. Dieser Zauber geschieht unter der Haube für Sie und Sie müssen sich nicht um die Implementierung kümmern. NULL ist jedoch ein Makro und eine implementierungsdefinierte Nullzeigerkonstante. Sie wird oft wie folgt definiert:

#define NULL 0

d.h. eine ganze Zahl.

Dies ist ein subtiler, aber wichtiger Unterschied, der Mehrdeutigkeiten vermeiden kann.

Zum Beispiel:

int i = NULL;     //OK
int i = nullptr;  //error
int* p = NULL;    //OK
int* p = nullptr; //OK

und wenn Sie zwei Funktionsüberladungen wie diese haben:

void func(int x);   //1)
void func(int* x);  //2)

func(NULL) fordert 1) weil NULL ist eine ganze Zahl. func(nullptr) fordert 2) weil nullptr konvertiert implizit in einen Zeiger des Typs int* .

Auch wenn Sie eine Aussage wie diese sehen:

auto result = findRecord( /* arguments */ );

if (result == nullptr)
{
 ...
}

und Sie können nicht einfach herausfinden, was findRecord zurückgibt, können Sie sicher sein, dass result muss ein Zeigertyp sein; nullptr macht dies besser lesbar.

In einem abgeleiteten Kontext funktionieren die Dinge ein wenig anders. Wenn Sie eine Vorlagenfunktion wie diese haben:

template<typename T>
void func(T *ptr)
{
    ...
}

und Sie versuchen, es mit nullptr :

func(nullptr);

erhalten Sie einen Compilerfehler, weil nullptr ist vom Typ nullptr_t . Sie müssen entweder explizit die Option nullptr auf einen bestimmten Zeigertyp zu übertragen oder eine Überladung/Spezialisierung für func con nullptr_t .


Vorteile der Verwendung von nulptr:

  • Vermeidung von Mehrdeutigkeiten zwischen Funktionsüberladungen
  • ermöglicht Ihnen die Spezialisierung von Vorlagen
  • sicherer, intuitiver und aussagekräftiger Code, z. B. if (ptr == nullptr) 代わりに if (ptr == 0)

-1voto

Axel Schweiß Punkte 21

NULL muss nicht 0 sein. Solange Sie immer NULL und nie 0 verwenden, kann NULL ein beliebiger Wert sein. Angenommen, man programmiert einen von Neuman Mikrocontroller mit flachem Speicher, der seine Interrupt-Vektoren auf 0 hat. Wenn NULL 0 ist und etwas auf einen NULL-Zeiger schreibt, stürzt der Mikrocontroller ab. Wenn NULL, sagen wir, 1024 ist und an 1024 eine reservierte Variable steht, stürzt der Schreibvorgang nicht ab, und man kann NULL-Zeiger-Zuweisungen innerhalb des Programms erkennen. Auf PCs ist dies sinnlos, aber für Raumsonden, militärische oder medizinische Geräte ist es wichtig, dass sie nicht abstürzen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X