Was ist der Unterschied zwischen NULL, '\ 0' und 0?

309

In C scheint es Unterschiede zwischen verschiedenen Werten von Null zu geben - NULL , NULund 0.

Ich weiß, dass das ASCII-Zeichen zu '0'ausgewertet wird48 oder0x30 .

Der NULLZeiger ist normalerweise definiert als:

#define NULL 0

Oder

#define NULL (void *)0

Hinzu kommt der NULCharakter'\0' der ebenfalls zu bewerten scheint 0.

Gibt es Zeiten, in denen diese drei Werte nicht gleich sein können?

Gilt das auch für 64-Bit-Systeme?

Gnavi
quelle
1
Weitere Informationen zu den Unterschieden zwischen 0 und NULL finden Sie unter stackoverflow.com/questions/176989/… .
David Rodríguez - Dribeas
7
Der Bezeichner NULexistiert nicht in der C-Standardsprache oder -Bibliothek (oder, soweit ich weiß, in C ++). Das Nullzeichen wird manchmal als NUL bezeichnet, aber es wird normalerweise nur als C oder C ++ bezeichnet '\0'.
Keith Thompson

Antworten:

351

Hinweis: Diese Antwort gilt für die C-Sprache, nicht für C ++.


Nullzeiger

Das ganzzahlige Konstantenliteral 0hat je nach Kontext, in dem es verwendet wird, unterschiedliche Bedeutungen. In allen Fällen ist es immer noch eine ganzzahlige Konstante mit dem Wert0 , es wird nur auf verschiedene Arten beschrieben.

Wenn ein Zeiger mit dem konstanten Literal verglichen wird, 0wird überprüft, ob der Zeiger ein Nullzeiger ist. Dies0 wird dann als Nullzeigerkonstante bezeichnet. Der C-Standard definiert, dass die 0Umwandlung in den Typ void *sowohl ein Nullzeiger als auch eine Nullzeigerkonstante ist.

Um die Lesbarkeit zu verbessern, wird das Makro hinzugefügt NULL in der Header-Datei bereitgestellt stddef.h. Abhängig von Ihrem Compiler ist es möglicherweise möglich, #undef NULLihn zu etwas Verrücktem neu zu definieren.

Daher gibt es hier einige gültige Möglichkeiten, um nach einem Nullzeiger zu suchen:

if (pointer == NULL)

NULList so definiert, dass es einem Nullzeiger entspricht. Es ist eine Implementierung, die definiert NULList, wie die tatsächliche Definition lautet, solange es sich um eine gültige Nullzeigerkonstante handelt.

if (pointer == 0)

0 ist eine weitere Darstellung der Nullzeigerkonstante.

if (!pointer)

Diese ifAnweisung prüft implizit "ist nicht 0", daher kehren wir das um und bedeuten "ist 0".

Die folgenden Methoden sind UNGÜLTIG, um nach einem Nullzeiger zu suchen:

int mynull = 0;
<some code>
if (pointer == mynull)

Für den Compiler ist dies keine Prüfung auf einen Nullzeiger, sondern eine Gleichheitsprüfung für zwei Variablen. Dies kann funktionieren, wenn mynull den Code nie ändert und die Compiler-Optimierungskonstante die 0 in die if-Anweisung faltet. Dies ist jedoch nicht garantiert und der Compiler muss mindestens eine Diagnosemeldung (Warnung oder Fehler) gemäß C-Standard erstellen.

Beachten Sie, dass dies ein Nullzeiger in der Sprache C ist. Es spielt keine Rolle für die zugrunde liegende Architektur. Wenn die zugrunde liegende Architektur einen Nullzeigerwert hat, der als Adresse 0xDEADBEEF definiert ist, ist es Sache des Compilers, dieses Durcheinander zu beseitigen.

Selbst in dieser lustigen Architektur sind die folgenden Methoden immer noch gültige Methoden, um nach einem Nullzeiger zu suchen:

if (!pointer)
if (pointer == NULL)
if (pointer == 0)

Die folgenden Methoden sind UNGÜLTIG, um nach einem Nullzeiger zu suchen:

#define MYNULL (void *) 0xDEADBEEF
if (pointer == MYNULL)
if (pointer == 0xDEADBEEF)

da diese von einem Compiler als normale Vergleiche angesehen werden.

Nullzeichen

'\0'wird als Nullzeichen definiert - das ist ein Zeichen, bei dem alle Bits auf Null gesetzt sind. Dies hat nichts mit Zeigern zu tun. Möglicherweise sehen Sie jedoch etwas Ähnliches wie diesen Code:

if (!*string_pointer)

Überprüft, ob der Zeichenfolgenzeiger auf ein Nullzeichen zeigt

if (*string_pointer)

Überprüft, ob der Zeichenfolgenzeiger auf ein Nicht-Null-Zeichen zeigt

Verwechseln Sie diese nicht mit Nullzeigern. Nur weil die Bitdarstellung dieselbe ist und dies einige bequeme Überkreuzungsfälle ermöglicht, sind sie nicht wirklich dasselbe.

Außerdem '\0'ist (wie alle Zeichenliterale) eine Ganzzahlkonstante, in diesem Fall mit dem Wert Null. Ist '\0'also völlig gleichbedeutend mit einer schmucklosen 0Ganzzahlkonstante - der einzige Unterschied besteht in der Absicht , die sie einem menschlichen Leser vermittelt ("Ich verwende dies als Nullzeichen.").

Verweise

Weitere Informationen finden Sie in Frage 5.3 der FAQ zu comp.lang.c. In diesem PDF finden Sie den C-Standard. Lesen Sie die Abschnitte 6.3.2.3 Zeiger, Absatz 3.

Andrew Keeton
quelle
3
Vielen Dank für den Hinweis auf die FAQ-Liste. Siehe aber auch c-faq.com/null/nullor0.html
Sinan Ünür
4
Nein, Sie werden nicht ptrmit All-Bits-Null vergleichen . Dies ist kein memcmp, aber dies ist ein Vergleich mit einem eingebauten Operator. Die eine Seite ist eine Nullzeigerkonstante '\0'und die andere Seite ist ein Zeiger. Aswell wie bei den beiden anderen Versionen mit NULLund 0. Diese drei machen das Gleiche.
Johannes Schaub - litb
6
Sie nehmen den eingebauten Vergleichsoperator als eine Sache, die Bitfolgen vergleichen würde. Aber das ist es nicht. Es vergleicht zwei Werte, die abstrakte Konzepte sind. So ein Null - Zeiger , dass intern dargestellt wird , als 0xDEADBEEFnoch ein Nullzeiger ist, egal was seine Bitstring aussieht, und es wird zu vergleichen , noch gleich NULL, 0, \0und alle anderen Null - Zeiger - Konstante bildet.
Johannes Schaub - Litb
2
Sie machen einen guten Punkt über den Vergleichsoperator. Ich habe C99 aufgefrischt. Es heißt "Ein ganzzahliger Konstantenausdruck mit dem Wert 0 oder ein solcher Ausdruck vom Typ void * wird als Nullzeigerkonstante bezeichnet." Es heißt auch, dass ein Zeichenliteral ein ganzzahliger konstanter Ausdruck ist. Durch die transitive Eigenschaft haben Sie also Recht ptr == '\0'.
Andrew Keeton
2
".... es könnte möglich sein, #undef NULL und es zu etwas Verrücktem neu zu definieren. Jeder, der dies tut, verdient es, erschossen zu werden." das mein guter Herr brachte mich laut zum Lachen ...
oggiemc
34

Es scheint, dass einige Leute die Unterschiede zwischen NULL, '\ 0' und 0 falsch verstehen. Um dies zu erklären und um zu vermeiden, dass die zuvor genannten Dinge wiederholt werden:

Ein konstanter Ausdruck des Typs intmit dem Wert 0 oder einem Ausdruck dieser Art, gegossen , um Typen void *ist ein Nullzeiger Konstante , die in einen Zeiger umgewandelt wird zu einem , wenn Null - Zeiger . Der Standard garantiert, dass der Vergleich mit einem Zeiger auf ein Objekt oder eine Funktion ungleich ist .

NULList ein Makro, das als Nullzeigerkonstante definiert ist .

\0ist eine Konstruktion, die zur Darstellung des Nullzeichens verwendet wird und zum Beenden einer Zeichenfolge verwendet wird.

Ein Nullzeichen ist ein Byte, dessen Bits auf 0 gesetzt sind.

amaterasu
quelle
14

Alle drei definieren die Bedeutung von Null in unterschiedlichen Zusammenhängen.

  • Zeigerkontext - NULL wird verwendet und bedeutet, dass der Wert des Zeigers 0 ist, unabhängig davon, ob er 32 Bit oder 64 Bit ist (ein Fall 4 Bytes, die anderen 8 Bytes Nullen).
  • Zeichenfolgenkontext - Das Zeichen, das die Ziffer Null darstellt, hat einen Hex-Wert von 0x30, während das NUL-Zeichen einen Hex-Wert von 0x00 hat (zum Beenden von Zeichenfolgen).

Diese drei sind immer unterschiedlich, wenn Sie sich die Erinnerung ansehen:

NULL - 0x00000000 or 0x00000000'00000000 (32 vs 64 bit)
NUL - 0x00 or 0x0000 (ascii vs 2byte unicode)
'0' - 0x20

Ich hoffe das klärt es.

Nasko
quelle
8
Nasko: Bewerten sizeof('\0')und überrascht sein.
Café
3
@Nasko: Ich war wirklich überrascht: mit gcc in C: sizeof ('\ 0') == sizeof ('a') == 4, während mit g ++ in C ++: sizeof ('\ 0') == sizeof ('a') == 1
David Rodríguez - Dribeas
1
@Nasko: Aus dem C-Standard (Entwurf, n1124): 'Eine ganzzahlige Zeichenkonstante hat den Typ int', daher ist '\ 0' in C tatsächlich vom Typ int, und daher beträgt sizeof ('\ 0') in meiner Architektur 4 (Linux, 32bit)
David Rodríguez - Dribeas
@dribeas - Ich habe es nicht als Konstante beschrieben, sondern als Teil der Zeichenfolge. Ich hätte es definitiv explizit machen können. Danke
Nasko
@ DavidRodríguez-dribeas Undid edit "Korrigierter ASCII-Wert '0' auf 0x20 (32. Dezember)"
chux - Monica wiederherstellen
6

Wenn NULL und 0 als Nullzeigerkonstanten äquivalent sind, welche soll ich verwenden? In der C-FAQ-Liste wird auch dieses Problem behoben:

C-Programmierer müssen das verstehen NULLund 0sind in Zeigerkontexten austauschbar, und dass ein Uncasting 0 durchaus akzeptabel ist. Jede Verwendung von NULL (im Gegensatz zu 0) sollte als sanfte Erinnerung daran betrachtet werden, dass ein Zeiger beteiligt ist. Programmierer sollten sich nicht darauf verlassen (weder für ihr eigenes Verständnis noch für das des Compilers), um Zeiger 0von Ganzzahlen zu unterscheiden 0.

Nur in Zeigerkontexten sind NULLund 0gleichwertig. NULLsollte nicht verwendet werden, wenn eine andere Art von 0erforderlich ist, auch wenn dies möglicherweise funktioniert, da dies die falsche stilistische Botschaft aussendet. (Darüber hinaus erlaubt ANSI die Definition von NULLsein ((void *)0), was in Nicht-Zeiger-Kontexten überhaupt nicht funktioniert.) Insbesondere nicht verwendenNULL wenn das ASCII-Nullzeichen ( NUL) gewünscht wird. Geben Sie Ihre eigene Definition an

#define NUL '\0'

wenn du musst.

Sinan Ünür
quelle
5

Was ist der Unterschied zwischen NULL, '\ 0' und 0?

"Nullzeichen (NUL)" ist am einfachsten auszuschließen. '\0'ist ein Zeichenliteral. In C ist es so implementiert int, dass es dasselbe ist wie 0, was von ist INT_TYPE_SIZE. In C ++ wird das Zeichenliteral als char1 Byte implementiert . Dies unterscheidet sich normalerweise von NULLoder0 .

Nächster, NULL wird ein Zeigerwert angegeben, der angibt, dass eine Variable nicht auf einen Adressraum verweist. Abgesehen von der Tatsache, dass es normalerweise als Nullen implementiert wird, muss es in der Lage sein, den gesamten Adressraum der Architektur auszudrücken. In einer 32-Bit-Architektur beträgt NULL (wahrscheinlich) 4 Byte und in einer 64-Bit-Architektur 8 Byte. Dies liegt an der Implementierung von C.

Schließlich ist das Literal 0vom Typ int, der von Größe ist INT_TYPE_SIZE. Der Standardwert von INT_TYPE_SIZEkann je nach Architektur unterschiedlich sein.

Apple schrieb:

Das von Mac OS X verwendete 64-Bit-Datenmodell wird als "LP64" bezeichnet. Dies ist das gängige Datenmodell, das von anderen 64-Bit-UNIX-Systemen von Sun und SGI sowie von 64-Bit-Linux verwendet wird. Das LP64-Datenmodell definiert die primitiven Typen wie folgt:

  • Ints sind 32-Bit
  • Longs sind 64-Bit
  • Long-Longs sind auch 64-Bit
  • Zeiger sind 64-Bit

Wikipedia 64-Bit :

Der VC ++ - Compiler von Microsoft verwendet das LLP64-Modell.

64-bit data models
Data model short int long  long long pointers Sample operating systems
LLP64      16    32  32    64        64       Microsoft Win64 (X64/IA64)
LP64       16    32  64    64        64       Most Unix and Unix-like systems (Solaris, Linux, etc.)
ILP64      16    64  64    64        64       HAL
SILP64     64    64  64    64        64       ?

Bearbeiten : Mehr zum Zeichenliteral hinzugefügt.

#include <stdio.h>

int main(void) {
    printf("%d", sizeof('\0'));
    return 0;
}

Der obige Code gibt 4 auf gcc und 1 auf g ++ zurück.

Eugene Yokota
quelle
2
Nein, '\0'ist kein 1-Byte-Wert. Es ist ein Zeichenliteral, das ein ganzzahliger konstanter Ausdruck ist. Wenn also gesagt werden kann, dass es eine Größe hat, dann ist es die Größe von a int(die mindestens 2 Bytes betragen muss). Wenn Sie mir nicht glauben, bewerten Sie sizeof('\0')und überzeugen Sie sich selbst. '\0', 0Und 0x0sind völlig gleichwertig.
Café
@caf es kommt auf die sprache an. Wenn Sie mir nicht glauben, probieren Sie sizeof('\0')einen C ++ - Compiler aus.
Eugene Yokota
2
Sie sollten "% zu" verwenden, wenn Sie sizeof (etwas) drucken
Unbenutzt
4

Ein One-L NUL, es beendet eine Zeichenfolge.

Ein Zwei-L-NULL zeigt auf nichts.

Und ich werde einen goldenen Stier wetten

Dass es keine Drei-L-NULL gibt.

Wie gehst du mit NUL um?

EvilTeach
quelle
4

Ein gutes Stück, das mir hilft, wenn ich mit C beginne (entnommen aus der Expert C-Programmierung von Linden)

Das Eine 'l' nul und das Zwei 'l' null

Merken Sie sich diesen kleinen Reim, um die korrekte Terminologie für Zeiger und ASCII-Null abzurufen:

The one "l" NUL ends an ASCII string,

The two "l" NULL points to no thing.

Apologies to Ogden Nash, but the three "l" nulll means check your spelling. 

Das ASCII-Zeichen mit dem Bitmuster Null wird als "NUL" bezeichnet. Der spezielle Zeigerwert, der bedeutet, dass der Zeiger nirgendwo hin zeigt, ist "NULL". Die beiden Begriffe sind in ihrer Bedeutung nicht austauschbar.

dlmeetei
quelle
Viel einfacher: NUList ein Steuercode wie BEL, VT, HT, SOTusw. und hat somit max. 3 Zeichen.
glglgl
2

"NUL" ist nicht 0, sondern bezieht sich auf das ASCII-NUL-Zeichen. Zumindest habe ich das so gesehen. Der Nullzeiger wird häufig als 0 definiert. Dies hängt jedoch von der Umgebung ab, in der Sie ausgeführt werden, und von der Spezifikation des von Ihnen verwendeten Betriebssystems oder der verwendeten Sprache.

In ANSI C wird der Nullzeiger als ganzzahliger Wert 0 angegeben. Jede Welt, in der dies nicht zutrifft, ist also nicht ANSI C-konform.

Peterb
quelle
1

Ein Byte mit dem Wert von 0x00ist in der ASCII-Tabelle das Sonderzeichen NULoder NULL. Da Sie in C keine Steuerzeichen in Ihren Quellcode einbetten sollten, wird dies in C-Zeichenfolgen mit einer maskierten 0 dargestellt, d. H.\0 . H.

Aber ein wahrer NULL ist kein Wert. Es ist das Fehlen eines Wertes. Für einen Zeiger bedeutet dies, dass der Zeiger nichts hat, auf das er zeigen kann. In einer Datenbank bedeutet dies, dass ein Feld keinen Wert enthält (was nicht bedeutet, dass das Feld leer, 0 oder mit Leerzeichen gefüllt ist).

Der tatsächliche Wert, den ein bestimmtes System- oder Datenbankdateiformat zur Darstellung von a verwendet, NULList nicht unbedingt erforderlich 0x00.

richardtallent
quelle
0

NULList nicht garantiert 0 - sein genauer Wert ist architekturabhängig. Die meisten großen Architekturen definieren es so (void*)0.

'\0' wird immer gleich 0 sein, da Byte 0 auf diese Weise in einem Zeichenliteral codiert wird.

Ich erinnere mich nicht, ob C-Compiler ASCII verwenden müssen - wenn nicht, sind sie '0'möglicherweise nicht immer gleich 48. Unabhängig davon ist es unwahrscheinlich, dass Sie jemals auf ein System stoßen, das einen alternativen Zeichensatz wie EBCDIC verwendet, es sei denn, Sie arbeiten sehr daran obskure Systeme.

Die Größen der verschiedenen Typen unterscheiden sich auf 64-Bit-Systemen, aber die ganzzahligen Werte sind gleich.


Einige Kommentatoren haben Zweifel geäußert , dass NULL gleich 0 sein, aber nicht sein Null. Hier ist ein Beispielprogramm zusammen mit der erwarteten Ausgabe auf einem solchen System:

#include <stdio.h>

int main () {
    size_t ii;
    int *ptr = NULL;
    unsigned long *null_value = (unsigned long *)&ptr;
    if (NULL == 0) {
        printf ("NULL == 0\n"); }
    printf ("NULL = 0x");
    for (ii = 0; ii < sizeof (ptr); ii++) {
        printf ("%02X", null_value[ii]); }
    printf ("\n");
    return 0;
}

Dieses Programm könnte drucken:

NULL == 0
NULL = 0x00000001
John Millikin
quelle
2
OP fragte nach '\ 0' (dem NUL-Zeichen), nicht nach '0' (dem Null-Zeichen)
Chris Lutz
2
@Chris: '\ 0' ist nicht NULL, sondern Byte 0, das in einem Zeichenliteral oktal codiert ist.
John Millikin
2
In C ++ garantiert der Standard, dass die Konvertierung vom ganzzahligen Wert 0 in einen Zeiger immer einen Nullzeiger ergibt. In C ++ ist 0 garantiert ein Nullzeiger, während NULL ein Makro ist und ein böswilliger Codierer es als etwas anderes neu definieren könnte.
David Rodríguez - Dribeas
6
Und NULL ist garantiert 0. Das Bitmuster eines NULL-Zeigers ist nicht garantiert alle Nullen, aber die NULL-Konstante ist und bleibt immer 0.
Jalf
2
Ihr erster Satz ist falsch - NULL kann in C ++ nicht als (void *) 0 definiert werden, da keine implizite Konvertierung von void * in einen anderen Zeiger erfolgt (im Gegensatz zu C).
-2

(void *) 0 ist NULL und '\ 0' steht für das Ende einer Zeichenfolge.

Shinxg
quelle