Ich habe gestern einen Mann für eine Position im mittleren Software-Engineering interviewt, und er erwähnte, dass NULL in C nicht immer Null ist und dass er Implementierungen von C gesehen hat, bei denen NULL nicht Null ist. Ich finde das sehr verdächtig, aber ich möchte sicher sein. Weiß jemand, ob er recht hat?
(Die Antworten haben keinen Einfluss auf mein Urteil über diesen Kandidaten, ich habe meine Entscheidung bereits meinem Vorgesetzten übermittelt.)
Ich nehme an, Sie meinen den Nullzeiger. Es ist garantiert gleich zu vergleichen 0
.1 Aber es muss nicht mit All-Null-Bits dargestellt werden.2
Siehe auch die comp.lang.c FAQ auf Nullzeigern.
- Siehe C99, 6.3.2.3.
- Es gibt keinen expliziten Anspruch; aber siehe die Fußnote für C99, 7.20.3 (Dank an @birryree in den Kommentaren).
§ 6.3.2.3 des C99-Standards sagt
Ein ganzzahliger konstanter Ausdruck mit dem Wert 0 oder ein solcher in den Typ void * umgewandelter Ausdruck wird als Nullzeigerkonstante bezeichnet Vergleichen Sie ungleich mit einem Zeiger auf ein Objekt oder eine Funktion.
§ 7.17 sagt auch
[…] NULL, das zu einer implementierungsdefinierten Nullzeigerkonstante erweitert wird […]
Die Adresse des NULL-Zeigers kann von 0 abweichen, verhält sich aber in den meisten Fällen so.
(Dies sollte das gleiche sein wie in älteren C-Standards, die ich gerade nicht zur Hand habe)
Der Nullzeiger Konstante ist immer 0. Die NULL
Makro kann von der Implementierung als nackt definiert werden 0
oder ein Cast-Ausdruck wie (void *) 0
oder ein anderer nullwertiger ganzzahliger Ausdruck (daher die “implementierungsdefinierte” Sprache im Standard).
Der Nullzeiger Wert kann etwas anderes als 0 sein. Wenn eine Nullzeigerkonstante angetroffen wird, wird sie in den richtigen Nullzeigerwert konvertiert.
In C gibt es einen und nur einen Kontext, in dem es notwendig ist, eine Nullzeigerkonstante explizit in einen bestimmten Zeigertyp umzuwandeln, damit das Programm korrekt funktioniert. Dieser Kontext leitet einen Nullzeiger durch eine nicht typisierte Funktionsargumentliste. Im modern C passiert dies nur, wenn Sie einen Nullzeiger an eine Funktion übergeben müssen, die eine variable Anzahl von Argumenten akzeptiert. (In Legacy C passiert es mit jeder Funktion, die nicht mit einem Prototyp deklariert ist.) Das paradigmatische Beispiel ist execl
wobei das allerletzte Argument ein NULL-Zeiger sein muss, in den explizit umgewandelt wird (char *)
:
execl("/bin/ls", "ls", "-l", (char *)0); // correct
execl("/bin/ls", "ls", "-l", (char *)NULL); // correct, but unnecessarily verbose
execl("/bin/ls", "ls", "-l", 0); // undefined behavior
execl("/bin/ls", "ls", "-l", NULL); // ALSO undefined behavior
Ja, das letzte Beispiel hat undefiniertes Verhalten selbst wenn NULL
ist definiert als ((void *)0)
Weil void *
und char *
sind nicht implizit ineinander umwandelbar, wenn sie durch eine nicht typisierte Argumentliste geleitet werden, obwohl sie sich sonst überall befinden. (Es gibt eine Sprache in C2011, die sie implizit interkonvertierbar macht, wenn sie durchlaufen werden va_arg
aber sie haben vergessen anzugeben, dass von der Implementierung bereitgestellte Bibliotheksfunktionen auf verschiedene Argumente zugreifen, als ob sie aufgerufen würden va_arg
, daher können Sie sich nur für verschiedene Funktionen, die Teil Ihres Programms sind, darauf verlassen. Jemand sollte wahrscheinlich eine DR einreichen.)
„Unter der Haube“ ist hier das Problem nicht nur mit dem Bitmuster, das für einen Nullzeiger verwendet wird, aber dass der Compiler möglicherweise den genauen konkreten Typ jedes Arguments kennen muss, um einen Aufrufrahmen korrekt einzurichten. (Betrachten Sie den MC68000 mit seinen separaten Adress- und Datenregistern; einige ABIs spezifizierten Zeigerargumente, die in Adressregistern übergeben werden sollen, aber ganzzahlige Argumente in Datenregistern. Betrachten Sie auch alle ABI wo int
und void *
sind nicht gleich groß. Und es ist heutzutage verschwindend selten, aber C sieht immer noch explizit vor void *
und char *
nicht die gleiche Größe haben. [EDIT: I’m not sure, but this may no longer be permitted.]) Wenn ein Funktionsprototyp vorhanden ist, kann der Compiler diesen verwenden, aber Funktionen ohne Prototypen und variadische Argumente bieten keine solche Unterstützung.
C++ ist komplizierter und ich fühle mich nicht qualifiziert zu erklären, wie.
Bei einigen Implementierungen ist die Größe des Zeigers nicht die gleiche wie die Größe der Ganzzahl. NULL im Integer-Kontext ist 0, aber das tatsächliche binäre Layout muss nicht nur 0 sein.
bytes.com/topic/c/answers/213647-null-c
– Soandos
27. März 2012 um 16:50 Uhr
verwandter stackoverflow.com/questions/459743/is-null-always-false
– Makabral
27. März 2012 um 16:52 Uhr
c-faq.com/null/index.html
– Jouni K. Seppänen
27. März 2012 um 16:53 Uhr
Nein, aber Null ist immer
NULL
.– Philipp
27. März 2012 um 17:08 Uhr
@ Philipp:
int x = 0; void *p = (void *) x;
Hier,x
hat den Wert Null, aberx
ist nicht die wörtliche Null, alsop
ist nicht garantiertNULL
und auf einigen bizarren Plattformen wird es das tatsächlich nicht seinNULL
. Auf der anderen Seite,void *q = 0;
immer zuweistNULL
zuq
, egal auf welcher Plattform. „Wörtlich“ hat in diesem Zusammenhang eine technische Bedeutung. Suchen Sie nach “ganzzahliges Literal”.– Dietrich Ep
6. November 2012 um 23:08 Uhr