Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

 << zurück
C von A bis Z von Jürgen Wolf
Das umfassende Handbuch für Linux, Unix und Windows
– 2., aktualisierte und erweiterte Auflage 2006
Buch: C von A bis Z

C von A bis Z
1.116 S., mit CD, Referenzkarte, 39,90 Euro
Galileo Computing
ISBN 3-89842-643-2
gp Kapitel 16 Dynamische Speicherverwaltung
  gp 16.1 Das Speicherkonzept
  gp 16.2 Speicheralloziierung mit malloc()
  gp 16.3 Die Mysterie von NULL
    gp 16.3.1 NULL für Fortgeschrittene
    gp 16.3.2 Was jetzt – NULL, 0 oder \0 ... ?
    gp 16.3.3 Zusammengefasst
  gp 16.4 Speicherreservierung und ihre Probleme
  gp 16.5 free() – Speicher wieder freigeben
  gp 16.6 Die Freispeicherverwaltung
    gp 16.6.1 Prozessinterne Freispeicherverwaltung
  gp 16.7 Dynamisches Array
  gp 16.8 Speicher dynamisch reservieren mit realloc und calloc
  gp 16.9 Speicher vom Stack anfordern mit alloca (nicht ANSI C)
  gp 16.10 free – Speicher wieder freigeben
  gp 16.11 Zweidimensionale dynamische Arrays
  gp 16.12 Wenn die Speicheralloziierung fehlschlägt
    gp 16.12.1 Speicheranforderung reduzieren
    gp 16.12.2 Speicheranforderungen aufteilen
    gp 16.12.3 Einen Puffer konstanter Größe verwenden
    gp 16.12.4 Zwischenspeichern auf Festplatte vor der Alloziierung
    gp 16.12.5 Nur so viel Speicher anfordern wie nötig


Galileo Computing - Zum Seitenanfang

16.3 Die Mysterie von NULL  downtop

Ein NULL-Zeiger wird zurückgeliefert, wenn malloc() nicht mehr genügend zusammenhängenden Speicher finden kann. Der NULL-Zeiger ist ein vordefinierter Zeiger, dessen Wert sich von einem regulären Zeiger unterscheidet. Er wird vorwiegend bei Funktionen zur Anzeige und Überprüfung von Fehlern genutzt, die einen Zeiger als Rückgabewert zurückgeben.


Galileo Computing - Zum Seitenanfang

16.3.1 NULL für Fortgeschrittene  downtop

Sicherlich haben Sie sich schon mal gefragt, was es mit NULL auf sich hat. Wenn Sie dann in einem Forum nachgefragt haben, könnten Sie hier bspw. dreierlei Antworten zurückbekommen haben:

gp  NULL ist ein Zeiger auf 0.
gp  NULL ist ein typenloser Zeiger auf 0.
gp  Es gibt keinen NULL-Zeiger, sondern nur NULL und das ist eben 0.

Sicherlich gibt es noch einige mehr Antworten hierzu. Aber es ist doch ziemlich verwirrend, ob jetzt NULL eben 0 ist oder ein Zeiger auf 0 und wo ist dabei eigentlich der Unterschied?

Ein integraler konstanter Ausdruck mit dem Wert 0 wird zu einem NULL-Zeiger, wenn dieser einem Zeiger zugewiesen oder auf Gleichheit mit einem Zeiger verglichen wird. Damit ergeben sich die folgenden möglichen defines für NULL:

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

Am häufigsten sieht man das Makro NULL als (void *) 0 implementiert, was den Vorteil hat, dass einem ggf. bei der Übersetzung die Arbeit abgenommen wird. Allerdings sollten Sie – egal wie NULL nun implementiert ist – auf eine Typenumwandlung von NULL verzichten, denn schließlich kann NULL ja auch nur 0 oder 0L sein.

Der Compiler muss zur Übersetzungszeit selbst feststellen, wann ein NULL-Zeiger benötigt wird und eben den entsprechenden Typ dafür eintragen. Somit ist es ohne weiteres möglich, folgende Vergleiche zu verwenden (beide Versionen erfüllen denselben Zweck):

/* null_ptr1.c */
#include <stdio.h>
#include <stdlib.h>
int main(void) {
   char *ptr1 = NULL;
   char *ptr2 = 0;
   if(ptr1 != NULL){
      /* ... */
   }
   if(ptr2 != 0) {
      /* ... */
   }
   return EXIT_SUCCESS;
}

Bei einem Funktionsargument allerdings ist ein solcher Zeiger-Kontext nicht unbedingt feststellbar. Hier kann ein Compiler eventuell nicht feststellen, ob der Programmierer hier 0 (als Zahl) oder eben den NULL-Zeiger meint. Hier ein Beispiel, worauf ich hinaus will:

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

Der (UNIX)-Systemaufruf execl() erwartet eine Liste variabler Länge mit Zeigern auf char, welche mit einem NULL-Zeiger abgeschlossen werden. Der Compiler kann hierbei allerdings nicht feststellen, ob der Programmierer hier mit 0 den NULL-Zeiger meint; daher wird sich der Compiler in diesem Fall für die Zahl 0 entscheiden. In diesem Fall müssen Sie auf jeden Fall eine Typenumwandlung nach char* machen – oder eben den NULL-Zeiger verwenden:

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

Dieser Fall ist besonders bei einer variablen Argumentenliste (wie es hier mit execl() der Fall ist) wichtig. Denn hier funktioniert alles (auch ohne einem Typencasting) bis zum Ende der explizit festgelegten Parameter. Alles was danach folgt, wird nach den Regeln für die Typenerweiterung behandelt und es wird eine ausdrückliche Typenumwandlung erforderlich. Da es sowieso kein Fehler ist, den NULL-Zeiger als Funktionsargument einer expliziten Typenumwandlung zu unterziehen, ist man bei regelmäßiger Verwendung (eines expliziten Casts) immer auf der sicheren Seite, wenn dann mal Funktionen mit einer variablen Argument-Anzahl verwendet werden.

Bei einem »normalen« Funktions-Prototypen hingegen werden die Argumente weiterhin an Hand ihrer zugehörigen Parameter im Prototypen umgewandelt. Verwenden Sie hingegen keinen Funktionsprototypen im selben Gültigkeitsbereich, wird auch hier eine explizite Umwandlung benötigt.


Galileo Computing - Zum Seitenanfang

16.3.2 Was jetzt – NULL, 0 oder \0 ... ?  downtop

Ob Sie jetzt NULL oder 0 verwenden, ist eine Frage des Stils und des Progammierers. Viele Programmierer halten NULL für eine sinnlose Einführung, worauf man gerne verzichten kann (wozu soll man eine 0 hinter NULL verstecken) – andere wiederum entgegnen dem, dass man mit NULL besser unterscheiden kann, ob denn nun ein Zeiger gemeint ist oder nicht. Denn Folgendes wird der Compiler bemängeln, wenn NULL als (void *)0 implementiert ist:

/* null_ptr2.c */
#include <stdio.h>
#include <stdlib.h>
int main(void) {
   int *ptr = NULL;
   int i = NULL;      // Falsch
   return EXIT_SUCCESS;
}

Intern nach dem Präprozessorlauf würde aus der Zeile

int i = NULL;

Folgendes gemacht

int i = (void *) 0;

Also ein klarer Regelverstoß und somit unzulässig.

Irgendwie bringen dann die Anfänger noch das Terminierungszeichen \0 (NULL) mit in diese Geschichte ein (was mir eigentlich unverständlich ist wie, aber ...). Irgendwie scheint dabei das ASCII-Zeichen NUL mit NULL verwechselt zu werden. Allerdings kann man \0 nicht mit NULL vergleichen. \0 garantiert bspw. laut Standard, dass alle Bits auf 0 gesetzt sind (NULL nicht).


Galileo Computing - Zum Seitenanfang

16.3.3 Zusammengefasst  toptop

Aufgrund der häufigen Nachfragen zu NULL habe ich mich bewusst entschieden, dass Thema etwas breiter aufzurollen. Die negative Seite könnte hierbei sein, dass der eine oder andere jetzt total verwirrt vom NULL-Zeiger ist. Daher folgende zwei Regeln, welche Sie einhalten sollten, damit Sie auf der sicheren Seite sind:

gp  Wollen Sie im Quelltext einen NULL-Zeiger verwenden, dann können Sie entweder eben den Null-Zeiger Konstante 0 oder eben das Makro NULL verwenden.
gp  Verwenden Sie hingegen 0 oder NULL als Argument eines Funktionsaufrufes, wenden Sie am besten die von der Funktion erwartete explizite Typenumwandlung an.
 << zurück
  
  Zum Katalog
Zum Katalog: C von A bis Z
C von A bis Z
bestellen
 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchtipps
Zum Katalog: Shell-Programmierung






 Shell-Programmierung


Zum Katalog: Linux-UNIX-Programmierung






 Linux-UNIX-Programmierung


Zum Katalog: C/C++






 C/C++


Zum Katalog: UML 2.0






 UML 2.0


Zum Katalog: Reguläre Ausdrücke






 Reguläre Ausdrücke


Zum Katalog: Linux






 Linux


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo





Copyright © Galileo Press 2006
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de