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

Inhaltsverzeichnis
Vorwort
Vorwort des Gutachters
1 Einstieg in C
2 Das erste Programm
3 Grundlagen
4 Formatierte Ein-/Ausgabe mit »scanf()« und »printf()«
5 Basisdatentypen
6 Operatoren
7 Typumwandlung
8 Kontrollstrukturen
9 Funktionen
10 Präprozessor-Direktiven
11 Arrays
12 Zeiger (Pointer)
13 Kommandozeilenargumente
14 Dynamische Speicherverwaltung
15 Strukturen
16 Ein-/Ausgabe-Funktionen
17 Attribute von Dateien und das Arbeiten mit Verzeichnissen (nicht ANSI C)
18 Arbeiten mit variabel langen Argumentlisten – <stdarg.h>
19 Zeitroutinen
20 Weitere Headerdateien und ihre Funktionen (ANSI C)
21 Dynamische Datenstrukturen
22 Algorithmen
23 CGI mit C
24 MySQL und C
25 Netzwerkprogrammierung und Cross–Plattform-Entwicklung
26 Paralleles Rechnen
27 Sicheres Programmieren
28 Wie geht’s jetzt weiter?
A Operatoren
B Die C-Standard-Bibliothek
Stichwort

Jetzt Buch bestellen
Ihre Meinung?

Spacer
<< zurück
C von A bis Z von Jürgen Wolf
Das umfassende Handbuch
Buch: C von A bis Z

C von A bis Z
3., aktualisierte und erweiterte Auflage, geb., mit CD und Referenzkarte
1.190 S., 39,90 Euro
Rheinwerk Computing
ISBN 978-3-8362-1411-7
Pfeil 14 Dynamische Speicherverwaltung
Pfeil 14.1 Das Speicherkonzept
Pfeil 14.2 Speicherallokation mit »malloc()«
Pfeil 14.3 Das NULL-Mysterium
Pfeil 14.3.1 NULL für Fortgeschrittene
Pfeil 14.3.2 Was jetzt – NULL, 0 oder \0 ... ?
Pfeil 14.3.3 Zusammengefasst
Pfeil 14.4 Speicherreservierung und ihre Probleme
Pfeil 14.5 »free()« – Speicher wieder freigeben
Pfeil 14.6 Die Freispeicherverwaltung
Pfeil 14.6.1 Prozessinterne Freispeicherverwaltung
Pfeil 14.7 Dynamische Arrays
Pfeil 14.8 Speicher dynamisch reservieren mit »realloc()« und »calloc()«
Pfeil 14.9 Speicher vom Stack anfordern mit »alloca()« (nicht ANSI C)
Pfeil 14.10 »free()« – Speicher wieder freigeben
Pfeil 14.11 Zweidimensionale dynamische Arrays
Pfeil 14.12 Wenn die Speicherallokation fehlschlägt
Pfeil 14.12.1 Speicheranforderung reduzieren
Pfeil 14.12.2 Speicheranforderungen aufteilen
Pfeil 14.12.3 Einen Puffer konstanter Größe verwenden
Pfeil 14.12.4 Zwischenspeichern auf Festplatte vor der Allokation
Pfeil 14.12.5 Nur so viel Speicher anfordern wie nötig


Rheinwerk Computing - Zum Seitenanfang

14.3 Das NULL-Mysterium Zur nächsten ÜberschriftZur vorigen Überschrift

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.


Rheinwerk Computing - Zum Seitenanfang

14.3.1 NULL für Fortgeschrittene Zur nächsten ÜberschriftZur vorigen Überschrift

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

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

Sicherlich gibt es noch einige Antworten mehr 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 geprüft 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 gegebenenfalls bei der Übersetzung die Arbeit abgenommen wird. Allerdings sollten Sie – egal wie NULL nun implementiert ist – auf eine Typumwandlung 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, das zeigt, 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, die 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 unbedingt eine Typumwandlung nach char* durchführen – 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 sie hier mit execl() vorliegt) wichtig. Denn hier funktioniert alles (auch ohne ein Type-Casting) 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 Typumwandlung erforderlich. Da es sowieso kein Fehler ist, den NULL-Zeiger als Funktionsargument einer expliziten Typumwandlung 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« Funktionsprototypen hingegen werden die Argumente weiterhin anhand ihrer zugehörigen Parameter im Prototyp umgewandelt. Verwenden Sie hingegen keinen Funktionsprototyp im selben Gültigkeitsbereich, wird auch hier eine explizite Umwandlung benötigt.


Rheinwerk Computing - Zum Seitenanfang

14.3.2 Was jetzt – NULL, 0 oder \0 ... ? Zur nächsten ÜberschriftZur vorigen Überschrift

Ob Sie jetzt NULL oder 0 verwenden, ist eine Frage des Stils und des Progammierers. Viele Programmierer halten NULL für eine sinnlose Neuerung, auf die man gern 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;

Dies ist ein klarer Regelverstoß und somit unzulässig.

Irgendwie bringen dann die Anfänger noch das Terminierungszeichen \0 (NULL) mit in diese Geschichte ein (ich begreife zwar nicht wie, aber es geschieht ...). Vielleicht wird dabei das ASCII-Zeichen NUL mit NULL verwechselt. Allerdings kann man \0 nicht mit NULL vergleichen. \0 garantiert beispielsweise laut Standard, dass alle Bits auf 0 gesetzt sind (NULL tut das nicht).


Rheinwerk Computing - Zum Seitenanfang

14.3.3 Zusammengefasst topZur vorigen Überschrift

Aufgrund der häufigen Nachfragen zu NULL habe ich mich bewusst entschieden, dass Thema etwas breiter aufzurollen. Vielleicht ist der eine oder andere jetzt total verwirrt. Daher folgen hier zwei Regeln zum Umgang mit NULL, die Sie einhalten sollten, damit Sie auf der sicheren Seite sind:

  • Wollen Sie im Quelltext einen NULL-Zeiger verwenden, dann können Sie entweder eben diesen Null-Zeiger mit der Konstante 0 oder eben das Makro NULL verwenden.
  • Verwenden Sie hingegen 0 oder NULL als Argument eines Funktionsaufrufes, wenden Sie am besten die von der Funktion erwartete explizite Typumwandlung an.


Ihre Meinung

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.

<< zurück
  
  Zum Rheinwerk-Shop
Zum Rheinwerk-Shop: C von A bis Z

 C von A bis Z
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das Openbook gefallen?
Ihre Meinung

 Buchtipps
Zum Rheinwerk-Shop: C/C++






 C/C++


Zum Rheinwerk-Shop: Einstieg in C






 Einstieg in C


Zum Rheinwerk-Shop: Schrödinger programmiert C++






 Schrödinger
 programmiert C++


Zum Rheinwerk-Shop: C++ Handbuch






 C++ Handbuch


Zum Rheinwerk-Shop: IT-Handbuch für Fachinformatiker






 IT-Handbuch für
 Fachinformatiker


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
InfoInfo




Copyright © Rheinwerk Verlag GmbH 2009
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.


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern