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 14 Zeiger (Pointer)
  gp 14.1 Zeiger deklarieren
  gp 14.2 Zeiger initialisieren
    gp 14.2.1 Speichergröße von Zeigern
  gp 14.3 Zeigerarithmetik
  gp 14.4 Zeiger, die auf andere Zeiger verweisen
    gp 14.4.1 Subtraktion zweier Zeiger
  gp 14.5 Typensicherung bei der Dereferenzierung
  gp 14.6 Zeiger als Funktionsparameter (call–by–reference)
    gp 14.6.1 Zeiger als Rückgabewert
  gp 14.7 Array und Zeiger
  gp 14.8 Zeiger auf Strings
    gp 14.8.1 Zeiger auf konstante Objekte (Read-only-Zeiger)
  gp 14.9 Zeiger auf Zeiger und Stringtabellen
    gp 14.9.1 Stringtabellen
  gp 14.10 Zeiger auf Funktionen
  gp 14.11 void-Zeiger
  gp 14.12 Äquivalenz zwischen Zeigern und Arrays


Galileo Computing - Zum Seitenanfang

14.11 void-Zeiger  toptop

Ein Zeiger auf void ist ein typenloser und vielseitiger Zeiger. Wenn der Datentyp des Zeigers noch nicht feststeht, wird der void-Zeiger verwendet. void-Zeiger haben den Vorteil, dass Sie diesem eine beliebige Adresse zuweisen können. Außerdem kann ein void-Zeiger durch eine explizite Typenumwandlung in jeden anderen beliebigen Datentyp umgewandelt werden. Beispielsweise:

/* void_ptr1.c */
#include <stdio.h>
#include <stdlib.h>
int main(void) {
   int a = 10;
   char *string = "void-Zeiger";
   void *ptr;
   /* void-Zeiger auf Variable int a */
   ptr = (int *)&a;
   printf("ptr = %p a=%p\n",ptr,&a);
   /* void-Zeiger auf string */
   ptr = (char *)string;
   printf("ptr = %p string = %p\n",ptr,string);
   return EXIT_SUCCESS;
}

Natürlich sollten Sie darauf achten, dass Sie für das Casting einen Zeiger angeben und nicht etwa einen Datentyp:

/* Richtig */
ptr=(typ *)&ptr2;
/* Falsch: typ ist kein Zeiger, sondern eine Variable */
ptr=(typ)&ptr2;

Zwar wurde hier im Beispiel ein Cast von void * nach datentyp * gemacht, aber dies ist in C nicht unbedingt nötig – C++ allerdings macht sehr wohl einen Unterschied und braucht einen Cast von void * nach datentyp *.

Würden Sie im Beispiel oben die Casts entfernen und das Beispiel als C++-Projekt übersetzen (was bei vielen Compilern unter MS-Windows häufig voreingestellt ist), würde der Compiler eine Warnung ausgeben wie bspw.:

[Warning]:
In function int main : invalid conversion from `void*' to ` int*`

Und genau diese Warnmeldung lässt viele Programmierer vermuten, dass etwas am Listing falsch ist und man müsse den void-Pointer immer casten. Sofern Sie Ihren Compiler nicht davon überzeugen können, dass Sie gerne ein C-Projekt schreiben würden, sollten Sie meiner Meinung nach dem sanften Druck des Compilers nachgeben (auch wenn das Programm tut, was es tun soll und ohne Problem ausgeführt werden kann) und ihm sein Cast geben – da dies ja auch nicht unbedingt »falsch« ist. Denn, zu einem der oberen Gebote eines Programmierers zählt, dass man niemals Warnmeldungen eines Compilers ignorieren soll.

Vorwiegend findet ein void-Zeiger Anwendung in Funktionen, die mit unterschiedlichen Zeigern aufgerufen werden können. Beispielsweise ist die Funktion memcmp() in der Headerdatei string.h folgendermaßen angegeben:

int memcmp (const void*, const void*, size_t);

Somit kann diese Funktion mit unterschiedlichen Zeigertypen verwendet werden, wie das folgende Beispiel zeigt:

/* void_ptr2.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
   char str1[]="Hallo";
   char str2[]="Hallo";
   int num1[] = { 1,2,3,4,5,6 };
   int num2[] = { 1,3,5,7,9,1 };
   int cmp;
   /* Casts sind nicht unbedingt nötig */
   cmp=memcmp( (char *)str1, (char *)str2, sizeof(str1));
   if(cmp ==0)
      printf("Beide Strings sind gleich\n");
   else
      printf("Die Strings sind nicht gleich\n");
   /* Casts sind nicht unbedingt nötig */
   cmp=memcmp((int *)num1,(int *)num2, sizeof(num1)/sizeof(int));
   if(cmp == 0)
      printf("Der Inhalt der beiden Zahlenarrays ist gleich\n");
   else
      printf("Die Zahlenarrays sind unterschiedlich\n");
   return EXIT_SUCCESS;
}

Die Umwandlung in einen entsprechenden Zeigertyp findet mit einem einfachen Typencasting statt – was auch hier nicht unbedingt nötig gewesen wäre (siehe Abschnitt vor dem Listing).

Für einige ist es verwirrend, wie ein leerer Zeiger (void, dt. leer) einfach so in irgendeinen Datentyp gecastet werden kann. Das deshalb, weil Sie gelernt haben, dass int vier Bytes Speicher hat, double acht Bytes Speicher und void eben keinen. Wobei void eigentlich auch nicht ganz leer ist. Verwenden Sie damit den sizeof-Operator, erfahren Sie, dass void einen Byte an Speicher benötigt.

Aber wenn Sie sich nochmals an den Anfang des Kapitels erinnern, bei dem Sie den sizeof-Operator auf alle Typen von Zeigern verwendet haben: Alle Zeiger, egal welchen Typs, haben einen Speicherbedarf von vier Bytes (32 Bit). Mehr ist auch nicht erforderlich, um eine Speicheradresse zu speichern. Ebenso sieht es mit dem void-Zeiger aus. Dieser benötigt wie alle anderen Zeiger vier Byte an Speicherplatz.

/* void_ptr3.c */
#include <stdio.h>
#include <stdlib.h>
int main(void) {
   void *void_ptr;
   printf("%d Byte\n", sizeof(void_ptr));
   return EXIT_SUCCESS;
}

Wollen Sie den Typ, auf den der void-Zeiger verweist, dereferenzieren, wird die Sache ein wenig komplizierter. Dafür benötigen Sie einen weiteren Zeiger:

/* void_ptr4.c */
#include <stdio.h>
#include <stdlib.h>
int main(void) {
   void *void_ptr;
   int wert = 10;
   void_ptr=(int *)&wert;
   *(int *)void_ptr = 100;
   printf("%d\n",wert);   /* 100 */
   return EXIT_SUCCESS;
}

Da der gecastete void-Zeiger allein noch nicht dereferenziert werden kann, wird hier einfach ein weiterer Zeiger verwendet:

*(int *)void_ptr = 100;

Jetzt denken Sie sicherlich darüber nach, welchen Vorteil eigentlich ein void-Zeiger hat? Bei dem Beispiel, in welchem die Funktion memcmp() verwendet wurde, ist der Vorteil eigentlich schon klar. Anstatt für jeden Datentyp eine eigene Funktion zu schreiben, wird einfach der void-Zeiger verwendet, und der Funktion kann es egal sein, mit welchem Datentyp sie verwendet wird. Wichtig ist dabei nur, dass die Funktion (logischerweise) entsprechend universell geschrieben wurde. Sie können nicht einfach einer Funktion, welche mit der Funktion strcmp() einzelne Strings vergleicht, als Argument die Anfangsadresse eines int-Arrays übergeben.

 << 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