FAQ:Null-Zeiger

[ FAQ in de.comp.lang.c ]


Diese FAQ bezieht sich in ihrer Gänze auf den inzwischen nicht mehr aktuellen ISO-C Standard 9899:1990, vielfach auch als C90 bezeichnet. Der seit Dezember 1999 existierende neue ISO 9899:1999 Standard (oder auch C99) wird nicht berücksichtigt.


[ Inhalt ][ Index ][ ZurÜck ][ Weiter ]


Frage 1.1: Was ist denn nun eigentlich dieser verflixte Null-Zeiger?

Antwort: Die Sprachdefinition legt fest, dass es für jeden Zeiger-Typ einen bestimmten Wert gibt, der von allen anderen Zeigerwerten verschieden ist und der nicht die Adresse irgendeines Objektes oder irgendeiner Funktion enthält: der "Null-Zeiger" eben. Das heißt also: der Adress-Operator & liefert niemals einen Null-Zeiger - ebensowenig wie ein erfolgreicher Aufruf von malloc(). malloc() liefert ja bei Mißerfolg einen Null-Zeiger zurück - womit wir beim typischen Anwendungsfall für Null-Zeiger wären: Wir haben damit einen "besonderen" Zeiger-Wert, der eine besondere Aussage trifft - normalerweise "kein Speicher beschafft" oder "ich zeige noch auf nichts".

Es besteht ein großer Unterschied im Konzept des Null-Zeigers und dem des nicht initialisierten Zeigers: für den Null-Zeiger ist garantiert, dass er nirgendwohin zeigt, ein nicht initialisierter Zeiger hingegen kann überallhin zeigen (im schlimmsten Fall sogar an eine Stelle, die dem eigenen Programm gehört, wo der Fehler also nicht sofort auffällt. Außerdem kann auf einen nicht initialisierten (ungültigen) Zeiger nicht getestet werden). Vgl. auch Frage 3.1, 3.13 und 17.1.

Wie oben erwähnt, gibt es für jeden Zeiger-Typ einen eigenen Null-Zeiger mit einer möglicherweise unterschiedlichen internen Repräsentation. Allerdings ist garantiert, dass man zwei Null-Zeiger beliebig zwischen verschiedenen Zeiger-Typen umwandeln kann, vergleicht man sie dann, muß das Ergebnis wieder ihre Gleichheit sein. Während dem Programmierer deshalb der interne Wert eines Null-Zeigers gleichgültig sein kann, muß die Umgebung stets wissen, welche Art von Null-Zeiger benötigt wird, damit er wenn nötig einen Unterschied machen kann (s. unten).

Siehe: K&R 1, 5.4; K&R 2, 5.4; H&S 5.3; ANSI 3.2.2.3; Rationale 3.2.2.3; ISO 6.2.2.3; P&B S. 49, 105.


Frage 1.2: Wie erzeuge ich einen Null-Zeiger in meinen Programmen?

Antwort: Laut Sprachdefinition wird ein integraler konstanter Ausdruck mit dem Wert 0 zu einem Null-Zeiger, wenn er einem Zeiger zugewiesen oder auf Gleichheit mit einem Zeiger verglichen wird (Äquivalenzvergleich). Die Umgebung (in aller Regel wohl die Übersetzungsumgebung) muß in einem solchen Fall feststellen, dass ein Null-Zeiger benötigt wird und einen Wert für den entsprechenden Typ eintragen. Deshalb sind die folgenden Code-Fragmente einwandfrei:

	    char *z = 0;
	    if (z != 0)

Hingegen ist bei einem Funktionsargument nicht notwendig ein Zeiger-Kontext feststellbar. Die Umgebung kann also u.U. nicht feststellen, dass der Programmierer mit einer einfachen 0 einen Null-Zeiger meint. Der Unix Systemaufruf "execl" erwartet eine Liste variabler Länge von Zeigern auf char, die mit einem Null-Zeiger abgeschlossen werden. Um im Umfeld eines Funktionsaufrufes einen Null-Zeiger zu erzeugen, ist normalerweise eine ausdrückliche Typumwandlung nötig - erst dadurch wird 0 in einen Zeiger-Kontext gestellt:

	    execl ("/bin/sh", "sh", "-c", "ls", (char *) 0);

Ließe man die Typumwandlung nach (char *) weg, wüßte die Umgebung nicht, dass ein Zeiger übergeben werden soll und würde sich in diesem Fall für eine 0 als ganze Zahl entscheiden. (Beachte, dass eine ganze Reihe von Handbüchern bei diesem Beispiel einen Fehler machen.)

Liegt ein Funktions-Prototyp vor, werden die Argumente - wie bei einer Zuweisung - an Hand der zugehörigen Parameter im Protoyp umgewandelt. Bei variablen Argumentlisten funktioniert dies natürlich nur bis zum Ende der explizit festgelegten Parameter - alles was danach kommt wird nach den Regeln für die Typerweiterung behandelt, d.h. eine ausdrückliche Typumwandlung wird erforderlich. Es ist bestimmt kein Fehler, wenn man Null-Zeiger-Konstanten als Funktionsargumente immer einer expliziten Typumwandlung unterzieht. Wenn es zur Gewohnheit wird, vergißt man es nicht so leicht und ist dann auch bei Funktionen mit variabler Argument-Anzahl und den immer noch zulässigen Deklarationen im alten Stil auf der sicheren Seite.

Zusammenfassung:

	Die "nackte" Konstante 0        ausdrückliche Typumwandlung
	ist zulässig bei:               erforderlich bei:
	_______________________________________________________________

	der Initialisierung eines       einem Funktionsaufruf ohne
	Zeigers                         Prototyp im selben
	(char *z = 0;)                  Gültigkeitsbereich

	der Zuweisung an einen          einem variablen Argument eines
	Zeiger                          Funktionsaufrufes mit variabler
	(z = 0;)                        Argumenten-Anzahl

	einem Äquivalenzvergleich
	(if (z != 0), if (z == 0))

	einem Funktionsaufruf mit
	fester Argumenten-Anzahl und
	einem Prototyp im selben
	Gültigkeitsbereich

Siehe: K&R 1, A.7.7, A.14, K&R 2, A.7.10, A.7.17; H&S 4.6.3; ANSI 3.2.2.3; ISO 6.2.2.3


Frage 1.3: Was ist NULL und wie sieht sein #define aus?

Antwort: Viele Programmierer sind der Meinung, dass es kein besonders guter Stil ist, unbenannte Konstanten überall im Programm herumfahren zu lassen: besser man nutzt die Fähigkeit des Präprozessors, symbolische Konstanten auszutauschen (zu "erweitern"), bevor das Programm übersetzt und gebunden wird. NULL ist nun ein solches Makro, dessen #define in <stddef.h> oder <stdio.h> zu finden ist. Die Beschreibung der Standardbibliothek legt fest, dass dieses Makro zu einer Null-Zeiger-Konstanten erweitert wird, die von der Implementierung definiert ist.

Die Sprachbeschreibung definiert den Begriff Null-Zeiger-Konstante als konstanten integralen Ausdruck mit dem Wert 0, oder einen entsprechenden Ausdruck, dessen Typ nach (void *) umgewandelt wurde. Damit ergeben sich die folgenden möglichen #defines:

	#define NULL 0
	#define NULL 0L
	#define NULL (void *) 0

Andere konstante integrale Ausdrücke mit dem Wert 0 sind natürlich ebenfalls möglich, sinvoll sind sie jedoch nicht.

Nun kann man also - wenn man 0 als Integer und 0 im Zeigerkontext unterscheiden will - NULL verwenden, wenn ein Null-Zeiger benötigt wird (vgl. 1.2). Das bleibt aber (wie viele Aspekte des Themas "Makro") eine reine Stilfrage: die Makroerweiterung erfolgt bereits in einer frühen Phase der Übersetzung: zu dem Zeitpunkt, an dem ein maschinenabhängiger Wert für einen Null-Zeiger eingetragen muß, ist nur noch 0, 0L oder (void *) 0 zu sehen.

Die Definition von NULL als (void *) 0 hat hauptsächlich den Vorteil, dass sie u.U. der Übersetzungsumgebung Arbeit abnimmt. Der Programmierer sollte sich hüten, auf die in Frage 1.2 erwähnten Typumwandlungen zu verzichten - schließlich ist auch

	#define NULL 0

strikt konform - und dann fehlt die Typumwandlung im Funktionsaufruf.

Ebensowenig sollte NULL irgendwo anders als im Zeiger-Kontext verwendet werden: ist es als (void *) definiert, wird seine anderweitige Verwendung zum Risiko:

	int a = 3;
	if (a > NULL)

könnte dann fehlschlagen.

Siehe: K&R 1, 5.4; K&R 2 5.4; H&S 13.1, ANSI 4.1.5, 3.2.2.3; Rationale 4.1.5; ISO 7.1.6, 6.2.2.3; P&B S. 49.


Frage 1.4: Wie sollte das #define für NULL auf einer Maschine aussehen, auf der Null-Zeiger intern nicht mit einem Bitmuster aus lauter Nullen dargestellt werden?

Antwort: ANSI beschreibt die Ausführungsumgebung als einen "abstrakten Automaten", über dessen interne Arbeitsweise nichts ausgesagt wird, und der sich nach außen so zu verhalten hat, als ob er jede Anforderung des Standards genau erfüllt - überspitzt formuliert könnte dies auch eine Sekretärin sein, die den Code liest und auf die schriftliche Eingabe:

	puts ("Hello World!");

mit der Ausgabe der entsprechenden Zeile auf ihrer Schreibmaschine reagiert.

Deshalb muß sich ein Programmierer über die interne Repräsentation eines Null-Zeigers auch keine Gedanken machen: die Umgebung nimmt sich dieser Frage so an, dass es aus der Sicht des Programmes nicht zu ermitteln ist, welcher Wert wirklich vorliegt.

Ein #define gehört jedoch zum Code des Programmes und muß, sollen damit strikt konforme Programme erzeugt werden können, selbst strikt konform sein. Die Umgebung - der solche engen Grenzen nicht auferlegt sind - muß dann dafür sorgen, dass aus NULL oder 0 im Quelltext im ausführbaren Programm-Image im Zeiger-Kontext ein Null-Zeiger wird.

Deshalb bleibt es bei den in Frage 1.3 genannten #defines.

Siehe: ISO 4, 5, 5.1.1.2, 5.1.2.3


Frage 1.5: Wenn NULL etwa so definiert wäre:

	#define NULL ((char *)0)

würde das nicht das Problem mit den fehlenden Typumwandlungen bei Funktionsaufrufen lösen?

Antwort: Nein. Das war schon in "Vor-ANSI-Zeiten" problematisch, und ist durch die im Standard alternativ vorgesehene Definition als

	#define NULL (void *) 0

ganz überflüssig geworden. Eigentlich bringt aber auch diese Definition nicht viel, außer vielleicht, dass sie hilft, Fehler wie:

	char a[]="Hallo Welt!";
	a[5] = NULL;

(wo eigentlich ASCII-NUL/'\0' gemeint war) zu entdecken. Hat der eine Compiler eine solche Typumwandlung eingebaut, so muß es beim nächsten noch lange nicht so sein, denn eine einfache 0 ist ja weiterhin eine gültige Null-Zeiger-Konstante. Darauf zu vertrauen, dass NULL als (void *) 0 definiert ist, erzeugt also unportabele Programme.

Abhilfe würde hier im besten Falle ein eigener Header "meinnull.h" bieten, der etwa so aussehen könnte

	#ifdef NULL
	  #undef NULL
	#endif
	#define NULL (void *) 0

bieten.

Siehe: Rationale 4.1.5.


Frage 1.6: Ich verwende das Präprozessor-Makro

	      #define nullzeiger(typ) (typ *)0

um korrekt typisierte Null-Zeiger zu erhalten.

Antwort: Schön, aber es bringt nichts. Die Umgebung muß auf jeden Fall selbst für die korrekte Umwandlung einer Null-Zeiger-Konstanten in einen Null-Zeiger sorgen (vgl. Frage 1.2).

Dieses #define vermittelt dem Leser lediglich den Eindruck, dass der Verfasser nicht so recht weiß, wie das mit den Null-Zeigern eigentlich funktioniert. Es erfordert deutlich mehr Tippaufwand und ist zusätzlich potentiell fehlerträchtig: wird beispielsweise der Typ eines Zeigers nachträglich geändert, muß das auch an jeder Stelle, an der dieser Zeiger gegen den Null-Zeiger getestet wird, geschehen (vgl. auch Frage 8.1).


Frage 1.7: Ist die Abkürzung "if(z)" als Test auf Nicht-Null-Zeiger zulässig? Was, wenn die interne Repräsentation für Null-Zeiger nicht 0 ist?

Antwort: Laut Sprachdefinition wird die erste Unteranweisung bei if() genau dann ausgeführt, wenn der Kontrollausdruck ungleich 0 ist.

Also kann man für

	      if (Ausdruck)

wobei von Ausdruck nur gefordert wird, dass sein Typ skalar ist (skalare Typen sind ganze Zahlen, Gleitkommazahlen und Zeiger), ohne Probleme:

	      if (Ausdruck != 0)

schreiben.

Genau das macht auch die Umgebung: sie überprüft, ob Ausdruck den Wert 0 hat oder nicht - egal ob man das ausdrücklich hinschreibt. "!= 0" ist hier also nur eine Formulierung, die dem menschlichen Leser klarer machen soll, was gemeint ist, und deshalb von manchen bevorzugt wird.

Wenn wir nun den einfachen Zeigerausdruck "z" für "Ausdruck" einsetzen (und das dürfen wir, denn ein Zeiger ist ein skalarer Typ) stellen wir fest, dass

	if (z)        

äquivalent zu

	if (z != 0)

ist. Damit steht eine 0 in einem Äquivalenzvergleich mit einem Zeiger und muß von der Umgebung regelgerecht in den passenden Null-Zeiger umgewandelt werden.

Die interne Repräsentation des Null-Zeigers spielt hier wiederum absolut keine Rolle.

Noch deutlicher wird das beim logischen Negations-Operator !. Die Sprachdefinition legt fest, dass !Ausdruck äquivalent zu (Ausdruck==0) ist.

	if (!z)

ist also exakt dasselbe wie

	if (z == 0).

Vgl. auch Frage 8.2

Siehe: K&R 2, A.7.4.7; H&S 5.3, ANSI 3.3.3.3, 3.3.9, 3.3.13, 3.3.14, 3.3.15, 3.6.4.1, 3.6.5; ISO 6.1.2.5, 6.3.3.3.


Frage 1.8: Wenn NULL und 0 dasselbe sind, was soll ich denn dann nun verwenden?

Antwort: Das ist hauptsächlich eine stilistische Frage und deshalb schwer zu beantworten. Viele Programmierer sind der Meinung, man solle grundsätzlich keine namenlosen Konstanten (auch keine 0 in anderem Zusammenhang) an allen möglichen Stellen im Programm stehen haben, andere gehen nicht so weit, finden es aber gut, zu unterscheiden, wann sie einen Zeiger meinen und wann nicht. Eine weitere Gruppe ist der Meinung, dass es ein völliger Unsinn war, 0 hinter einem #define zu verstecken (weil es die Leute nur verwirrt), und schreiben konsequent 0 "ohne allen Schnickschnack".

Eine einfache Grundregel lautet:

- NULL kann immer durch 0 ersetzt werden. - 0 kann nicht immer durch NULL ersetzt werden.

Letzteres liegt daran, dass die Sprachbeschreibung auch (void *) 0 als #define für NULL zuläßt. In diesem Fall ist natürlich ein:

	int i = NULL; das dann zu:
	int i = (void *) 0;

erweitert wird, unzulässig.

Ein weiterer häufiger Fehler ist der Einsatz von NULL, wenn eigentlich das ASCII-Zeichen NUL gemeint ist. Wenn sich dessen Verwendung absolut nicht vermeiden läßt, sollte man es selbst definieren:

	#define NUL '\0'

(Dies ist aber in der Regel keine gute Idee, denn für das Null-Zeichen '\0' ist vom Standard garantiert, das es vorhanden ist und dass alle seine Bits auf 0 gesetzt sind. Das Zugrundeliegen eines ASCII-Zeichensatzes ist jedoch definitiv nicht garantiert. Wer NUL definiert signalisiert also nur, dass er das nicht weiß.)

Siehe: K&R 2, 5.2, ISO 5.2.1.


Frage 1.9: Ist es nicht besser, NULL statt 0 zu verwenden, falls sich der Wert von NULL einmal ändert - beispielsweise auf Maschinen mit Null-Zeigern ungleich 0?

Antwort: Nein. Man verwendet symbolische Konstanten zwar oft an Stellen, an denen sich der zugrundeliegende Wert ändern könnte. Das ist aber bei NULL nicht der Fall.

Um es nochmals zu wiederholen: die Sprachbeschreibung garantiert, dass die Konstante 0 unter den genannten Bedingungen (Frage 1.2) in einen Null-Zeiger umgewandelt wird.

Dadurch wird die Verwendung von NULL zur reinen Stilfrage.


Frage 1.10: Jetzt bin ich aber etwas durcheinander: bei NULL ist der Wert garantiert 0, beim Null-Zeiger aber nicht?

Antwort: Das liegt daran, dass viele Leute NULL sofort mit dem Begriff Null-Zeiger gleichsetzen. Wenn man "Null" aber sprachlich nicht exakt verwendet, kann damit einiges gemeint sein:

1. Das Konzept des Null-Zeigers. Das abstakte sprachliche Konzept des Null-Zeigers wurde in Frage 1.1 diskutiert. Es wird durch die folgenden zwei Konzepte umgesetzt:

2. Die interne Repräsentation des Null-Zeigers zur Laufzeit auf einer bestimmten Maschine. Dies muß nicht den Wert 0 haben und kann für verschiedene Zeigertypen unterschiedlich sein; es kann sogar etwas sein, das mit dem Zeigerkonzept von C gar nichts zu tun hat. Das sollte aber nur für Leute von belang sein, deren Aufgabe es ist, einen Compiler zu bauen. "Normalsterbliche" C-Programmierer sehen diese Werte nie; sie verwenden:

3. Die für Quelltexte verbindliche Syntax - also einfach den Buchstaben "0" oder die Zeichenkette "(void *) 0". Diese werden oft durch ein Präprozessor-Makro verborgen:

4. NULL. Für dieses sind folgende #defines möglich:

	#define NULL 0
	#define NULL 0L
	#define NULL (void *) 0

	   Dann gibt es noch zwei Anwendungen von Null, die vom Thema nur
	   ablenken und nichts damit zu tun haben:

5. Das ASCII Nullzeichen NUL. Dies ist die ASCII-Entsprechung des C-Nullzeichens '\0', bei dem garantiert alle Bits auf 0 gesetzt sind und das deshalb auch den Wert 0 hat. Ähnlichkeiten zum Null-Zeiger sind nicht beabsichtigt und rein zufällig.

6. Die "Null-Zeichenkette" als Synonym für die leere Zeichenkette (""). Sie enthält ein einzelnes Nullzeichen, erzeugt aber keinen Null-Zeiger. Das Fragment

	       char *zk = "";
	       if (zk    == 0) puts ("Null-Zeiger");
	       if (zk[0] == 0) puts ("Nullzeichenkette");

	   sollte den Unterschied verdeutlichen.

Dieser Artikel verwendet den Begriff "Null-Zeiger" in der Bedeutung 1, das Schriftzeichen "0" für 3. und "NULL" in Großbuchstaben in der in 4. definierten Bedeutung.


Frage 1.11: Warum ist die Unsicherheit in Bezug auf Null-Zeiger eigentlich so groß? Warum werden diese Fragen so oft gestellt?

Antwort: Nun, wer in C programmiert, weiß in der Regel viel über die Maschinen, auf denen er arbeitet, oder ist dabei es zu lernen - meist mehr, als eigentlich nötig wäre.

Dazu kommt, dass viele Leute grundsätzlich unterschiedliche Bereiche in der Sprachdefinition nicht sauber auseinanderhalten: Zum einen Programm und Implementierung, zum anderen aber Übersetzungsumgebung und Ausführungsumgebung

Programme können beispielsweise strikt konform sein - das bedeutet allerdings auch, dass die verwendeten Header strikt konform sein müssen - denn die sind Teil des Programmes -, was wiederum eine Beschränkung auf die im Standard spezifizierten Sprachelemente bedeutet.

Eine Implementierung kann nur konform sein - nicht strikt konform. Sie darf - ohne dieses Attribut zu verlieren - Erweiterungen einführen, die allerdings das Verhalten eines strikt konformen Programmes nicht beeinflussen dürfen.

Die Übersetzungsumgebung erzeugt aus den Quelltexten eines Programmes ein "program image" das durch die Ausführungsumgebung ausgeführt werden kann. Dabei könnnen Null-Zeigerkonstanten in einer der letzten Phasen zu Null-Zeigern werden.

Es wäre es theoretisch denkbar, dass auf einer fiktiven Maschine die tatsächliche Umwandlung einer Null-Zeigerkonstante in einen Null-Zeiger erst zur Laufzeit (in der Ausführungsumgebung) stattfinden kann - wenn etwa die Architektur keine festen Werte für diesen Zweck kennt.

Die Verwendung eines Präprozessor-Makros erweckt oft den Eindruck, der dahinter verborgene Wert könne bei Bedarf geändert werden. Das ist im Fall des Makros NULL nicht so. NULL muß immer eine Null-Zeigerkonstante sein - die Implementierung darf sich nur noch eine der im Standard festgelegten Varianten aussuchen. Täte sie dies nicht und würde stattdessen

	#define NULL (void *) 1234567890

definieren, verlöre sie den Status einer konformen Implementierung, weil sie das Verhalten eines strikt konformen Programmes damit ändern könnte.

Ein großer Teil der Fragen beruht jedoch einfach darauf, dass die unterschiedliche Semantik des Begriffes "Null" (wie in 1.10 aufgeführt) übersehen wird.


Frage 1.12: Ich begreife es immer noch nicht: wie soll ich eigentlich mit diesen Null-Zeigern umgehen?

Antwort: Dafür gibt es zwei ganz einfache Regeln:

1. Wenn im Quelltext ein Null-Zeiger benötigt wird, verwendet man die Null-Zeiger-Konstanten 0 oder NULL.

2. Wenn "0" oder "NULL" Argument eines Funktionsaufrufes ist, wendet man die von der Funktion erwartete Typumwandlung an.

Der Rest der Diskussion dreht sich um Mißverständnisse, die interne Repräsentation des Null-Zeigers (die für die Sprache an sich vollkommen belanglos ist) oder um die ANSI-C Erweiterung (void *) 0.

Wenn man die Fragen 1.1, 1.2, 1.3 verstanden hat - und über 1.8 und 1.11 nachgedacht - sollte es eigentlich ganz gut gehen.


Frage 1.13: Bei all dem Durcheinander, das mit dem Begriff Null-Zeiger einhergeht - wäre es nicht wirklich einfacher zu verlangen, dass sie intern durch 0 dargestellt werden?

Antwort: Damit würde man Implementierungen unnötig einschränken. Der Zugriff auf eine bestimmte Adresse kann auf einer bestimmten Maschine eine Ausnahmebedingung erzeugen, die durchaus beabsichtigt sein kann, um ungültige Zugriffe abzufangen.

Die Festlegung auf Nullbits würde in diesem Fall einen eigentlich beabsichtigten Zweck des Null-Zeigers ausschalten - und das ohne jeglichen Nutzen für den Programmierer.

Es gibt nichts, was dadurch zu gewinnen wäre.


Frage 1.14: Nun mal im Ernst: gibt es überhaupt irgendwelche Maschinen, die Null-Zeiger ungleich 0 oder unterschiedliche Darstellungen für Zeiger unterschiedlichen Typs verwenden?

Antwort: Die Prime 50 Serie verwendete für den Null-Zeiger Segment 07777, Offset 0 - mindestens für PL/I. Spätere Modelle setzten für C-Null-Zeiger Segment 0, Offset 0 ein. Dies machte allerdings neue Anweisungen - etwa TCNP (Test C Null Pointer) - notwendig, natürlich als Zugeständnis an bereits bestehenden, miserabelen Quellcode, der von falschen Annahmen ausgegangen war. Ältere Prime-Computer mit Wort-Adressierung waren dafür bekannt, dass die (char *)-Zeiger größer als die (int *)-Zeiger waren.

Die Eclipse MV-Serie von Data General unterstützt 3 Zeigerformate: Wort, Byte und Bit. Davon werden 2 von C verwendet: Byte-Zeiger für char * und void *, Wort-Zeiger für den ganzen Rest.

Manche Honeywell-Bull Mainframes verwenden die Bitfolge 06000 für die interne Darstellung der Null-Zeiger.

Die CDC CDC-Cyber 180 Serie hat 48-Bit-Zeiger, die aus Ring, Segment und Offset bestehen. Die meisten Benutzer (in Ring 11) haben interne Null-Zeiger mit dem Wert 0xB00000000000.

Die Symbolics Lisp Maschine, bei der Speicheradressen eine Kennung des gespeicherten Wertes besitzen ("tagged architecture"), besitzt nicht einmal konventionelle numerische Zeiger, dort wird ein Paar <NIL, 0> (im Prinzip ein nicht existierendes <Objekt, Offset> Handle) als C Null-Zeiger verwendet.

Je nach verwendetem "Speichermodell" verwenden 80*86 Prozessoren 16-Bit-Daten- und 32-Bit-Funktionszeiger - oder umgekehrt.

Die alte HP 3000 Serie verwendet verschiedene Adressierungsmodi für Byte- und Wort-Adressen. Deshalb haben char- und void-Zeiger intern eine andere Darstellung als andere Zeiger auf die selbe Adresse.


Frage 1.15: Was bedeutet die Fehlermeldung: "null pointer assignment" und wie kann ich die Ursache isolieren?

Antwort: Diese Meldung gibt es nur unter MS-DOS (siehe deshalb Abschnitt 16).

Microsoft- und Borland-Compiler verwenden zur Ermittlung eines illegalen Zugriffes über einen Null-Zeiger eine heuristische Methode: beim Programmstart wird der Inhalt der Speicherstelle Null abgespeichert und als Teil des Exitcodes wird der so gewonnene Wert nochmals überprüft - hat er sich geändert, ist mit großer Wahrscheinlichkeit über einen Null-Zeiger darauf zugegriffen worden.

Als Abhilfe bietet sich an, mit dem Debugger einen Breakpoint auf die Adresse Null zu setzen, oder ähnlich zu verfahren wie der Compiler. Letzteres sollte man aber besser unterlassen, denn Debug-Code, der die Speicherbelegung ändert, erschwert in diesem Fall nur die Fehlersuche.

Siehe: van der Linden (D), S. 63.

[ Inhalt ][ Index ][ ZurÜck ][ Weiter ]


[ FAQ Logo ]   © 1997-2004 Jochen Schoof (joscho@bigfoot.de)
Diese Version wurde am 14. März 2004 erzeugt. Sie wird zukünftig nicht weiter gepflegt.