NULL-Zeiger

Disclaimer: Dieser Thread wurde aus dem alten Forum importiert. Daher werden eventuell nicht alle Formatierungen richtig angezeigt. Der ursprüngliche Thread beginnt im zweiten Post dieses Threads.

NULL-Zeiger
Ich hätte eine Frage zur korrekten Wortwahl. Darf oder kann ein Zeiger auf NULL (z.B. int *ptr = NULL) nicht dereferenziert werden? Oder beides? Dass dann ein segFault kommt ist mir klar, aber noch nicht so ganz warum genau.

Danke schonmal!


http://lmgtfy.com/?q=c+dereferencing+null+pointer :slight_smile: Da stößt man recht schnell auf den Begriff “undefined behavior”.


Ein Blick in den C99 Standard(*):

Darf man einen NULL-Pointer nun dereferenzieren? Streng genommen verbietet der Standard es dir nicht, allerdings garantiert er dir auch nicht, was dann passiert („undefined behavior“).

Je nach Compiler, Compiler-Version und aktuellen Compiler-Optionen wird auf Basis des Standards auch mal aggressiv Code entfernt. Beispiel:

void something(struct foo_t *foo) {
    int x = foo->bar;
    if (foo == NULL) {
        // ganz viel code
    }
}

Der Compiler darf hier den gesamten [m]if[/m]-Block entfernen, denn da der Pointer vorher dereferenziert wurde hat er wohl auf ein gültiges Objekt gezeigt und ist damit „offensichtlich“ kein NULL-Pointer.

Kann man einen NULL-Pointer dereferenzieren? Unter normalen Umständen: Nein. Normalerweise hat ein Prozess keinen Speicher an Adresse 0 gemappt (Page table - Wikipedia) und ein versuchter Zugriff resultiert somit in einen Segfault. Selbst ein [m]mmap()[/m] mit Wunschadresse 0 sollte fehlschlagen, da Linux normalerweise in [m]/proc/sys/vm/mmap_min_addr[/m] einen Wert größer als 0 konfiguriert hat.

Wenn du aber deinen Kernel entsprechend konfigurierst und dir mit [m]mmap[/m] erfolgreich eine Speicherseite an Adresse 0 einblendest, dann kannst du auch einen NULL-Pointer dereferenzieren. Allerdings musst du dabei ggf. gegen deinen Compiler kämpfen, der deinen Code dann (aufgrund des Standards zurecht) anders interpretiert als du dir das vorstellst.

(*) Streng genommen geht der Link nur auf das letzte „Arbeitspapier“ vor Herausgabe des Standard-Dokuments. Details siehe ISO/IEC JTC1/SC22/WG14 - C: Approved standards („The latest publicly available version of the C99 standard is the combined C99 + TC1 + TC2 + TC3, WG14 N1256, dated 2007-09-07. This is a WG14 working paper, but it reflects the consolidated standard at the time of issue.“)

1 „Gefällt mir“

Das geht davon aus, dass NULL als Wert tatsächlich 0 ist. Das muss nicht der Fall sein (wurde sogar gestern in #sp diskutiert) :wink: Auf handelsüblichen Systemen sollte das jedoch der Fall sein.

1 „Gefällt mir“

Ja? Wie liest du denn „An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant.“?


[m]if (x == NULL)[/m] und [m]if (x == (void*) 0)[/m] und [m]if (!x)[/m] sind alle äquivalent in C.
Ich meinte „tatsächlich 0“ so, dass im Assembler auch tatsächlich gegen den numerischen Wert 0 geprüft wird. Im C-Quelltext ist ein Prüfen gegen den numerischen Wert 0 nur syntactic sugar für das Prüfen gegen den vom Compiler festgelegten [m]NULL[/m]-Repräsentanten.


Stimmt du hast recht, streng genommen schweigt sich der Standard dazu aus, wie die Integer-Representation eines Pointers (aus C-Sicht) in Speicheradressen umgerechnet wird.

Es ist abgesehen von [m]intptr_t[/m] und [m]uintptr_t[/m] nichteinmal garantiert, dass man einen Pointer in einen Integer und wieder zurück in einen Pointer konvertieren kann, so dass das der neue Pointer gleich dem ursprünglichen ist. Und [m]intptr_t[/m] sowie [m]uintptr_t[/m] sind wohl leider nur optional für eine konkrete C-Implementierung.