Kann ich NULL als Ersatz für den Wert 0 verwenden?

73

Darf ich den NULLZeiger als Ersatz für den Wert von verwenden 0?

Oder ist daran etwas falsch?


Wie zum Beispiel:

int i = NULL;

als Ersatz für:

int i = 0;

Als Experiment habe ich folgenden Code kompiliert:

#include <stdio.h>

int main(void)
{
    int i = NULL;
    printf("%d",i);

    return 0;
}

Ausgabe:

0

In der Tat gibt es mir diese Warnung, die für sich genommen völlig richtig ist:

warning: initialization makes integer from pointer without a cast [-Wint-conversion] 

aber das Ergebnis ist immer noch gleichwertig.


  • Überquere ich damit "Undefiniertes Verhalten"?
  • Ist eine Verwendung NULLauf diese Weise zulässig ?
  • Ist etwas falsch daran, NULLeinen numerischen Wert in arithmetischen Ausdrücken zu verwenden?
  • Und was ist das Ergebnis und Verhalten in C ++ für diesen Fall?

Ich habe die Antworten liest Was ist der Unterschied zwischen NULL, ‚\ 0‘ und 0 zu wissen , was der Unterschied zwischen NULL, \0und 0ist, aber ich habe nicht die prägnanten Informationen von dort erhalten, wenn es durchaus zulässig ist und auch Recht auf Verwendung NULLals Wert, mit dem in Aufgaben und anderen arithmetischen Operationen gearbeitet werden soll.

RobertS unterstützt Monica Cellio
quelle
Kommentare sind nicht für eine ausführliche Diskussion gedacht. Dieses Gespräch wurde in den Chat verschoben .
Samuel Liew
Es wäre wirklich besser, zwei separate Fragen zu stellen, eine für C und eine für C ++.
Konrad Rudolph

Antworten:

82

Darf ich den NULL-Zeiger als Ersatz für den Wert 0 verwenden?

Nein , das ist nicht sicher. NULList eine Nullzeigerkonstante, die einen Typ haben könnteint , aber typischer einen Typ void *(in C) hat oder auf andere Weise nicht direkt einer int(in C ++> = 11) zuweisbar ist . In beiden Sprachen können Zeiger in Ganzzahlen konvertiert werden, sie sehen jedoch nicht vor, dass solche Konvertierungen implizit durchgeführt werden (obwohl einige Compiler dies als Erweiterung bereitstellen). Obwohl es üblich ist, einen Nullzeiger in eine Ganzzahl zu konvertieren, um den Wert 0 zu erhalten, garantiert der Standard dies nicht. Wenn Sie eine Konstante mit Typ intund Wert 0 möchten, buchstabieren Sie sie 0.

  • Könnte ich damit in undefiniertes Verhalten übergehen?

Ja, bei jeder Implementierung, bei NULLder ein Wert mit dem Typ void *oder einer anderen nicht direkt zuweisbaren erweitert wird int. Der Standard definiert nicht das Verhalten Ihrer Zuweisung bei einer solchen Implementierung, daher ist sein Verhalten undefiniert.

  • Ist es zulässig, auf diese Weise mit NULL zu arbeiten?

Es ist ein schlechter Stil und wird auf einigen Systemen und unter bestimmten Umständen kaputt gehen. Insofern Sie anscheinend GCC verwenden, würde es in Ihrem eigenen Beispiel brechen, wenn Sie mit der -WerrorOption kompilieren .

  • Ist etwas falsch daran, NULL als numerischen Wert in arithmetischen Ausdrücken zu verwenden?

Ja. Es ist nicht garantiert, dass überhaupt ein numerischer Wert vorliegt. Wenn Sie 0 meinen, schreiben Sie 0, was nicht nur gut definiert, sondern auch kürzer und klarer ist.

  • Und wie ist das Ergebnis in C ++ in diesem Fall?

Die C ++ - Sprache ist bei Konvertierungen strenger als C und hat andere Regeln für NULL, aber auch dort können Implementierungen Erweiterungen bereitstellen. Wenn du wieder 0 meinst, solltest du das schreiben.

John Bollinger
quelle
4
Sie sollten darauf hinweisen, dass "der Typ typischer ist void *" nur für C void *gilt. Dies ist kein zulässiger Typ für C ++ (da Sie void*keinem anderen Zeigertyp zuweisen können). In C ++ 89 und C ++ 03 NULL muss es tatsächlich vom Typ sein int, in späteren Versionen kann es jedoch sein (und ist es normalerweise) nullptr_t.
Martin Bonner unterstützt Monica
Sie sind auch falsch, dass das Konvertieren void*in intein undefiniertes Verhalten ist. Es ist nicht; Es handelt sich um ein implementierungsspezifisches Verhalten.
Martin Bonner unterstützt Monica
@MartinBonnersupportsMonica, In den Kontexten, in denen C angibt, dass ein Zeiger in eine Ganzzahl konvertiert wird, ist das Ergebnis der Konvertierung tatsächlich implementierungsspezifisch, aber das ist nicht das, worüber ich spreche. Es ist die Zuweisung eines Zeigers zu einem Wert vom Typ Integer (ohne explizite Konvertierung über eine Umwandlung), der ein undefiniertes Verhalten aufweist. Die Sprache definiert dort keine automatische Konvertierung.
John Bollinger
@MartinBonnersupportsMonica, ich habe bearbeitet, um C ++ - Überlegungen besser zu berücksichtigen. In jedem Fall gilt das zentrale Thema für beide Sprachen gleichermaßen: Wenn Sie eine Ganzzahl 0 möchten, schreiben Sie diese explizit als Ganzzahlkonstante des entsprechenden Typs.
John Bollinger
31

NULList eine Nullzeigerkonstante. In C könnte es sich um einen ganzzahligen konstanten Ausdruck mit Wert 0oder einen solchen Ausdruck handeln void*, wobei letzterer wahrscheinlicher ist. Das heißt, Sie können nicht davon ausgehen, dass Sie es NULLaustauschbar mit Null verwenden. Zum Beispiel in diesem Codebeispiel

char const* foo = "bar"; 
foo + 0;

Das Ersetzen 0durch NULList kein garantiertes C-Programm, da das Hinzufügen zwischen zwei Zeigern (geschweige denn von verschiedenen Zeigertypen) nicht definiert ist. Es wird eine Diagnose aufgrund einer Einschränkungsverletzung ausgegeben. Die zu addierenden Operanden sind ungültig .


Bei C ++ sieht das etwas anders aus. Das Fehlen einer impliziten Konvertierung von void*in andere Objekttypen bedeutete, dass dies NULLhistorisch wie 0in C ++ - Code definiert wurde. In C ++ 03 könnten Sie wahrscheinlich damit durchkommen. Aber seit C ++ 11 kann es legal als nullptrSchlüsselwort definiert werden . Jetzt wird wieder ein Fehler erzeugt, da std::nullptr_tZeigertypen möglicherweise nicht hinzugefügt werden.

Wenn NULLdefiniert als nullptrdann wird sogar Ihr Experiment ungültig. Es erfolgt keine Konvertierung von std::nullptr_tin eine Ganzzahl. Aus diesem Grund wird es als sicherere Nullzeigerkonstante angesehen.

Geschichtenerzähler - Unslander Monica
quelle
Der Vollständigkeit halber ist 0L auch eine Nullzeigerkonstante und kann wie NULLin beiden Sprachen verwendet werden.
Eerorika
1
@jamesqf Der Standard besagt, dass eine Ganzzahlkonstante mit dem Wert 0 eine Nullzeigerkonstante ist. Daher ist 0L eine Nullzeigerkonstante.
Eerorika
1
@eerorika: Genau das, was die Welt braucht, Standards, die die Realität ignorieren :-) Denn wenn ich mich richtig an meine 80286-Assembly erinnere, können Sie nicht einmal einen Fernzeiger als einzelne Operation zuweisen, sodass die Compiler-Autoren etwas Besonderes benötigen würden -Fall es.
Jamesqf
2
@jamesqf Gemäß den C-FAQ wird 0eine Nullzeigerkonstante erneut festgelegt: "Offensichtlich als Sop für den gesamten vorhandenen schlecht geschriebenen C-Code, der falsche Annahmen getroffen hat"
Andrew Henle
3
@jamesqf, dass jede beliebige Ganzzahlkonstante mit dem Wert 0 eine Nullzeigerkonstante (in C) ist, hat nichts mit Hardwarezeigerimplementierungen zu tun. Beachten Sie auch, dass Standard C in keinem Fall eine Unterscheidung zwischen Nah- und Fernzeigern erkennt, jedoch Zuweisungen von Zeiger zu Zeiger unterstützt. Es werden auch (einige) Zeigervergleiche unterstützt, die interessante Probleme für segmentierte Adressierungsformate wie die 286 darstellen.
John Bollinger
21

Darf ich den NULL-Zeiger als Ersatz für den Wert 0 verwenden?

int i = NULL;

Die Regeln variieren zwischen den Sprachen und ihren Versionen. In einigen Fällen Sie können und in anderen, können Sie nicht. Egal, du solltest nicht . Wenn Sie Glück haben, warnt Ihr Compiler, wenn Sie es versuchen, oder noch besser, Sie können nicht kompilieren.

In C ++ vor C ++ 11 (Zitat aus C ++ 03):

[lib.support.types]

NULL ist eine implementierungsdefinierte C ++ - Nullzeigerkonstante in diesem internationalen Standard.

Es ist wenig sinnvoll, eine Nullzeigerkonstante als Ganzzahl zu verwenden. Jedoch...

[conv.ptr]

Eine Nullzeigerkonstante ist ein integraler Konstantenausdruck (5.19) rWert vom Integer-Typ, der zu Null ausgewertet wird.

Es würde also technisch funktionieren, selbst wenn es unsinnig wäre. Aufgrund dieser Technik können Sie auf schlecht geschriebene Programme stoßen, die missbrauchen NULL.

Seit C ++ 11 (Zitat aus dem neuesten Entwurf):

[conv.ptr]

Ein Nullzeiger Konstante eine Ganzzahl - Literal ([lex.icon]) mit dem Wert Null oder ein prvalue vom Typ std :: nullptr_t .

A std​::​nullptr_­tist nicht in eine Ganzzahl konvertierbar, daher würde die Verwendung NULLals Ganzzahl nur bedingt funktionieren, abhängig von den von der Sprachimplementierung getroffenen Entscheidungen.

PS nullptrist ein Wert vom Typ std​::​nullptr_­t. Sofern Sie Ihr Programm nicht zum Kompilieren in Pre-C ++ 11 benötigen, sollten Sie immer nullptranstelle von verwenden NULL.


C ist etwas anders (Zitate aus C11 Entwurf N1548):

6.3.2.3 Sprache / Konvertierungen / Andere Operanden / Zeiger

3 Ein ganzzahliger Konstantenausdruck mit dem Wert 0 oder ein solcher Ausdruck, der in einen Typ umgewandelt wirdvoid * , wird als Nullzeigerkonstante bezeichnet. ...

Der Fall ähnelt also dem nach C ++ 11, dh der Missbrauch von NULLWerken hängt von den Entscheidungen ab, die von der Sprachimplementierung getroffen werden.

Eerorika
quelle
10

Ja , obwohl Sie je nach Implementierung möglicherweise eine Besetzung benötigen. Aber ja, sonst ist es 100% legitim.

Obwohl es wirklich, wirklich, wirklich schlechter Stil ist (unnötig zu sagen?).

NULList oder war eigentlich nicht C ++, es ist C. Der Standard hat jedoch, wie für viele C-Vermächtnisse, zwei Klauseln ([diff.null] und [support.types.nullptr]), die NULLC ++ technisch machen . Es ist eine implementierungsdefinierte Nullzeigerkonstante . Selbst wenn es ein schlechter Stil ist, ist es technisch so C ++ wie es nur sein kann.
Wie in der darauf hingewiesen Fußnote könnten mögliche Implementierungen sein 0oder 0Laber nicht (void*)0 .

NULLkönnte natürlich sein (der Standard sagt es nicht ausdrücklich, aber es ist so ziemlich die einzige Wahl, die nach 0oder bleibt 0L) nullptr. Das ist fast nie der Fall, aber es ist eine rechtliche Möglichkeit.

Die Warnung, die der Compiler Ihnen angezeigt hat, zeigt, dass der Compiler tatsächlich nicht kompatibel ist (es sei denn, Sie haben im C-Modus kompiliert). Weil, laut der Warnung, ein Nullzeiger konvertiert wurde (nicht nullptrder von nullptr_t, der verschieden wäre), so ist anscheinend die Definition von NULLtatsächlich (void*)0, was es möglicherweise nicht ist.

In beiden Fällen gibt es zwei mögliche legitime Fälle (dh Compiler nicht defekt). Entweder (der realistische Fall) NULList so etwas wie 0oder 0L, dann haben Sie "null oder eins" Konvertierungen in eine Ganzzahl, und Sie können loslegen.

Oder NULList in der Tat nullptr. In diesem Fall haben Sie einen eindeutigen Wert, der Vergleichsgarantien sowie klar definierte Konvertierungen von Ganzzahlen bietet, aber leider nicht in Ganzzahlen. Es hat jedoch eine klar definierte Konvertierung in bool(resultierend in false) und booleine klar definierte Konvertierung in eine Ganzzahl (resultierend in 0).

Leider sind das zwei Conversions, also nicht innerhalb von "null oder eins", wie in [conv] ausgeführt. Wenn Ihre Implementierung NULLals definiert ist nullptr, müssen Sie eine explizite Umwandlung hinzufügen, damit Ihr Code korrekt ist.

Damon
quelle
6

Aus der C-FAQ:

F: Wenn NULL und 0 als Nullzeigerkonstanten äquivalent sind, welche sollte ich verwenden?

A: Nur in Zeigerkontexten sind NULLund 0gleichwertig. NULLsollte nicht verwendet werden, wenn eine andere Art von 0 erforderlich ist, obwohl dies möglicherweise funktioniert, da dies die falsche stilistische Nachricht sendet. (Darüber hinaus ermöglicht ANSI die Definition von NULL ((void *)0), was in Nicht-Zeiger-Kontexten überhaupt nicht funktioniert.) Insbesondere nicht verwenden, NULLwenn das ASCII-Nullzeichen (NUL) gewünscht wird. Geben Sie Ihre eigene Definition an

http://c-faq.com/null/nullor0.html

phuclv
quelle
5

Haftungsausschluss: Ich kenne C ++ nicht. Meine Antwort soll nicht im Kontext von C ++ angewendet werden

'\0'ist ein intmit dem Wert Null, nur 100% genau wie 0.

for (int k = 10; k > '\0'; k--) /* void */;
for (int k = 10; k > 0; k--) /* void */;

Im Zusammenhang mit Zeigern , 0und NULLsind zu 100% entspricht:

if (ptr) /* ... */;
if (ptr != NULL) /* ... */;
if (ptr != '\0') /* ... */;
if (ptr != 0) /* ... */;

sind alle 100% gleichwertig.


Hinweis zu ptr + NULL

Der Kontext von ptr + NULList nicht der von Zeigern. Es gibt keine Definition für das Hinzufügen von Zeigern in der C-Sprache. Zeiger und ganze Zahlen können addiert (oder subtrahiert) werden. In ptr + NULLwenn entweder ptroder NULList ein Zeiger, der andere muss eine ganze Zahl, so ptr + NULLeffektiv (int)ptr + NULLoder ptr + (int)NULLje nach den Definitionen ptrund NULLeinige Verhaltensweisen zu rechnen: sie alle arbeitet, Warnung für die Konvertierung zwischen Zeigern und integer, Versagen zu kompilieren, .. .

pmg
quelle
Ich habe schon mal gesehen #define NULL (void *)0. Sind Sie sicher, dass NULL und Plain 0 zu 100% gleichwertig sind?
machine_1
2
Im Zusammenhang mit Zeiger, ja ... Bedingung in meiner Antwort betont, danke
pmg
@phuclv: Ich habe absolut keine Ahnung von C ++. Meine Antwort (außer dem Bit in Klammern) ist ungefähr C
pmg
@phuclv ptr + NULLwird nicht NULLim Zusammenhang mit Zeigern verwendet
pmg
3
@JesperJuhl: Im Kontext von Zeigern sind sie 100% äquivalent. Ich habe keine Ahnung, was nullptrist, aber ((void*)0)und 0(oder '\0') sind im Zusammenhang mit Zeigern gleichwertig ...if (ptr == '\0' /* or equivalent 0, NULL */)
pmg
5

Nein, nicht mehr bevorzugt zu verwenden NULL(alte Art der Zeigerinitilisierung).

Seit C ++ 11:

Das Schlüsselwort nullptrbezeichnet das Zeigerliteral. Es ist ein Wert vom Typ std :: nullptr_t. Es gibt implizite Konvertierungen von nullptrin einen Nullzeigerwert eines beliebigen Zeigertyps und eines beliebigen Zeigers in einen Elementtyp. Ähnliche Konvertierungen gibt es für jede Nullzeigerkonstante, die sowohl Werte vom Typ std::nullptr_tals auch das Makro enthält NULL.

https://en.cppreference.com/w/cpp/language/nullptr

Tatsächlich ist std :: nullptr_t der Typ des Nullzeigerliteral , nullptr. Es ist ein eindeutiger Typ, der selbst kein Zeigertyp oder Zeiger auf den Elementtyp ist.

#include <cstddef>
#include <iostream>

void f(int* pi)
{
   std::cout << "Pointer to integer overload\n";
}

void f(double* pd)
{
   std::cout << "Pointer to double overload\n";
}

void f(std::nullptr_t nullp)
{
   std::cout << "null pointer overload\n";
}

int main()
{
    int* pi; double* pd;

    f(pi);
    f(pd);
    f(nullptr);  // would be ambiguous without void f(nullptr_t)
    // f(0);  // ambiguous call: all three functions are candidates
    // f(NULL); // ambiguous if NULL is an integral null pointer constant 
                // (as is the case in most implementations)
}

Ausgabe:

Pointer to integer overload
Pointer to double overload
null pointer overload
Mannoj
quelle
Die Frage betrifft die Zuweisung von NULL zu 0 für Ganzzahlen. In diesem Sinne ändert sich mit nullptr nichts anstelle von NULL.
ivan.ukr
Er benutzte Wörter als "NULL-Zeiger".
Mannoj
Übrigens hat C ++ nach C ++ 11 nicht das NULL-Konzept. Der Autor ist möglicherweise verwirrt über die Verwendung von constexpr oder die Definition der Initialisierung auf alte Weise. en.cppreference.com/w/cpp/language/default_initialization
Mannoj