ein Kapitel zurück                                           ein Kapitel weiter

Jetzt kommen wir zu einem Thema vor dem sich die meisten Programmierer fürchten, den Zeigern oder in Englisch auch Pointer genannt. Man sagt aber auch wenn man das ganze mit den Zeigern verstanden hat ist der Rest des Programmieren lernen nur noch Formsache. Nun ganz so einfach ist es auch wieder nicht aber wenn sie diese Kapitel hier verstanden haben haben sie das Schwierigste in C überstanden.

Im Kapitel 'scanf' habe ich den Adressoperator '&' schon einmal angesprochen. Eine Variable ist eindeutig definiert mit 4 Angaben. Der Adresse, der Größe, des Namens und dem Wert. Zum Beispiel....

int x=5;

...kann man sich so vorstellen...

Zeiger, Pointer


Die Adresse '$55555' ist eine Fantasyadresse von mir auf der sie keinen Einfluss haben. Die legt der Compiler beim übersetzen selbst fest. Die Adresse wird benötigt damit wenn man die Variable benötigt, damit der Rechner weiß von wo er sich den Wert holen kann bzw. wenn ein Wert an die Variable übergeben wird, wohin er den Wert übergeben soll. Der Name ist der Name den sie bei der Definition der Variable selber festgelegt haben. Den Wert 5 haben wir auch schon zu Beginn des Programms festgelegt. Dieser Block oben hat eine Speichergröße von 4 Bytes (int = 4 Byte oder auf 16Bit-Systemen 2 Bytes). Das heißt in den 4 Bytes sind diese Informationen enthalten. Wenn sie jetzt wissen wollen welche Adresse Ihr Rechner beim Übersetzen des Programms der Variablen übergibt, müssen sie den Adressoperator '&' mit angeben...

/*Download:z1.c*/
#include <stdio.h>

int main()
{
int x=5;

printf("Die Adresse von x ist %p \n",&x);

return 0;
}

Hier sehen sie wie sie mit Hilfe des Adressoperator '&' und dem Formatstring '%p' die Adresse der Variablen 'x' bekommen. Das ist auch der Grund der Fehlermeldung wenn sie Ihre 'scanf' - Anweisung ohne dem Adressoperator ausführen wollen....

scanf("%d",x);

...damit kann der Compiler nichts anfangen. Er weiß einfach nicht wohin damit. Das ist das selbe wenn ein Postbote eine Brief verschicken soll auf den keine Adresse darauf steht. Der Brief wird niemals sein Ziel erreichen. Also, jede Variable die wir definieren hat eine Adresse, eine Namen und eine bestimmte Speichergröße (je nach Typ). Der Wert ist das einzige dieser 4 Angaben den sie zur Laufzeit festlegen oder ändern können. Ich schätze das mit den Adressen haben sie jetzt verstanden.

Was jetzt dann folgt wird für sie nicht mehr ganz so verständlich sein (falls sie Anfänger sind). Ich werde versuchen das ganze Thema möglichst leicht und verständlich zu halten.

Wie sieht ein Zeiger bzw. die Zeigerdefinition aus?


int *a;
char *b;
float *c;
double *c;
..........


Hier sehen sie den Syntax wie man Zeiger definiert. Was heißt jetzt eigentlich z.B.....

int *a;

Wir haben hier einen Zeiger mit dem Namen 'a' der auf alles zeigen kann was vom selben Variablentyp ist. In unserem Fall 'int'. Gut das sagt Ihnen jetzt noch gar nichts ;). Was bedeutet unser Sternchen da vor dem 'a'. Das '*' - Zeichen ist der Dereferenzierungsoperator. An diesem könne sie schon mal erkennen das es sich bei dieser Definition um einen Zeiger handelt. Toll werden sie jetzt sagen und was dereferenziert dieser? Ich komme gleich dazu, aber erst mal wieder ein kleines Programmbeispiel dazu....

/*Download:z2.c*/
#include <stdio.h>

int main()
{
int abfrage;
int Kapitel1=5;
int Kapitel2=60;
int Kapitel3=166;
int Nachtrag=233;
int *Verzeichnis;

do{
printf("\tINDEXREGISTER VOM BUCH AKTE NIX\n");
printf("\t*******************************\n\n");
printf("\t-1- Kapitel 1\n");
printf("\t-2- Kapitel 2\n");
printf("\t-3- Kapitel 3\n");
printf("\t-4- Nachtrag\n");
printf("\t-5- Ende\n");
printf("\n");
printf("\tAuswahl : ");
scanf("%d",&abfrage);

switch(abfrage)
{
case 1 : Verzeichnis=&Kapitel1;
printf("Seite %d\n",*Verzeichnis);
break;
case 2 : Verzeichnis=&Kapitel2;
printf("Seite %d\n",*Verzeichnis);
break;
case 3 : Verzeichnis=&Kapitel3;
printf("Seite %d\n",*Verzeichnis);
break;
case 4 : Verzeichnis=&Nachtrag;
printf("Seite %d\n",*Verzeichnis);
break;
default: break;
}
}while(abfrage<5);

return 0;
}

Unseren Zeiger werden sie oben im Programm schon entdeckt haben...

int *Verzeichnis;

Hiermit haben wir einen Zeiger definiert mit dem Namen 'Verzeichnis' . Bis zum 'switch(abfrage)' so weit nichts neues mehr für sie. Aber dann in der ersten case - Anweisung gibt es was neues.....

Verzeichnis=&Kapitel1;

Sie sehen hier das wir unserem Zeiger 'Verzeichnis' die Adresse von der Variablen Kapitel 1 übergeben. Das erkennen sie, na woran wohl, richtig am Adressoperator '&' der vor unserer Variablen 'Kapitel1' steht. Sollten sie den Adressoperator vor der Variablen 'Kapitel1' vergessen wird Ihr Compiler das Programm nicht übersetzen da unser Zeiger eine Adresse will und keinen Wert. Und dass ist das 'schwierigste' an den Zeigern. Sehen wir mal an wie wir uns diese Adressübergabe oben vorstellen können....



Hier in unserer Tabelle sehen sie, genauso wie wir es weiter oben durchgegangen haben, unsere Variable 'Kapitel1' und unseren Pointer 'Verzeichnis'. Die obere Tabelle soll unsere beiden Variablen bei der Initialisierung zeigen und die unter Tabelle soll die Adressübergabe (Verzeichnis=&Kapitel1) demonstrieren. Doch halt was haben wir den hier? Wieso steht in dem Pointer nicht, wie wahrscheinlich vermutet der Wert 5, sondern die Adresse von der Variablen Kapitel1? Nun anstatt wie eine normale Variable erwartet unser Pointer anstatt einen Wert eine Adresse! Ja aber wieso gibt dann....

printf("Seite %d\n",*Verzeichnis);

....Wert von 'Kapitel1' und nicht die Adresse auf dem Bildschirm? Nun, jetzt kommt unser Dereferenzierungsoperator '*' ins Spiel. Dieser dereferenziert den Wert der Adresse den wir unserem Zeiger zuvor mit....

Verzeichnis=&Kapitel1;

...übergeben haben. Lesen sie diesen Satz ganz langsam durch den des ist eigentlich das einzige ganze Problem bei Zeigern. Lassen sie mal bei unserer Ausgabe den Derefernzierungsoperator '*' weg....

printf("Seite %d\n",Verzeichnis);   //ohne *

Und jetzt übersetzen sie das Programm erneut. Und lassen sich das 'Verzeichnis' von 'Kapitel1' ausgeben. Und...? Es wird irgendeine Zahl ausgegeben nur nicht die Zahl 5. Warum..? Überlegen sie mal... gut geben sie mal nach 'scanf("%d",&abfrage);' ein...

printf("Adresse von Kapitel 1 : %p\n",&Kapitel1);

Ich glaube jetzt wird es langsam etwas heller im dunklen ;) Den mit 'Verzeichnis=&Kapitel1' haben wir ja nur die Adresse übergeben. Und in unserem Zeiger selbst steht auch nur die Adresse von 'Kapite1' (siehe unsere Tabelle oben). Ohne unserem Dereferenzierungoperator ist unser Zeiger nutzlos. Denn nur dieser zeigt uns auf welchen Wert er zeigt. Noch ein kleines Programm zum besseren Verständnis....

/*Download:z3.c*/
#include <stdio.h>

int main()
{
int x=5;
int *y;

printf("Adresse x=%p, Wert x=%d\n",&x,x);
printf("Adresse *y=%p, Wert *y=%d(unsinn)\n",&y,*y);

printf("\ny=&x;\n\n");
y=&x; /*y hat jetzt die Adresse von x*/

printf("Adresse x=%p, Wert x=%d\n",&x,x);
printf("Adresse *y=%p, Wert *y=%d\n",&y,*y);

printf("\nAdresse auf die y zeigt ist %p\n",y);
printf("und das ist die Adresse von x = %p\n",&x);

printf("\nACHTUNG!!!\n\n");

*y=10;
printf("*y=10\n\n");
printf("Adresse x=%p, Wert x=%d\n",&x,x);
printf("Adresse *y=%p, Wert *y=%d\n",&y,*y);

printf("\nAdresse auf die y zeigt ist %p\n",y);
printf("weiterhin die von x (%p)\n",&x);

return 0;
}

Das Programm ist eigentlich bei Ausführung selbstklärend. Aber ich schätze mal die Ausgabe ab ACHTUNG!!! wird sie wieder verwirren. Und sie dachten schon es wäre ja gar nicht so schwer?! Aber keine Sorge sie haben nichts missverstanden wie dieses kurze Programm zeigt....

/*Download:z4.c*/
#include <stdio.h>

int main()
{
int *y;
*y=10;
printf("Der Wert von *y ist %d\n",*y);
return 0;
}

Um es gleich vorwegzunehmen diese Programm funktioniert nicht es kann sein das sie eine 'allgemeine Schutzverletzung' produzieren oder ihr PC abstürzt. Aber bei unserem Programm oben hat....

*y=10;

funktioniert? Nun Zeiger heißen auch nicht umsonst Zeiger. Unser Zeiger(*y) hat doch die Adresse von x bekommen (y=&x) und wir haben gelernt das wir mit unserem Dereferenzierungoperator '*' auf den Wert zeigen dessen Adresse unser Zeiger bekommen hat. Also hier der Verlauf.....



Hiermit haben wir den Wert von x verändert! Also ich glaube jetzt müsste das sitzen. Denn wenn wir mit dem Dereferenzierungsoperator einen Wert einer anderen Variablen herankommen dann können wir logischerweise auch mit dem Dereferenzierungsoperator '*' diese Variable ändern können. Das wichtigste an dieser Geschichte ist das sie Verstehen das ein Zeiger kein Wert übergeben wird sondern Adressen um Anschließend mit dem Wert dieser Adresse zu arbeiten.......

Falls es jetzt welche gibt die das ganze nicht kapiert haben, kann ich es vielleicht noch ein bisschen anderes erklären. Nehmen sie ein Buch übers C-Programmieren oder ähnliches zur Hand. Schlagen sie das Buch ziemlich am Schluss das STICHWORTVERZEICHNIS auf. Suchen sie nach einem Wort wie 'Pointer' oder 'Zeiger'. Und was sehen sie da in diesem Verzeichnis? Die Seite auf dem der Inhalt des Thema 'Pointer' oder 'Zeiger' verweist. Wissen sie was ich damit meine? Diese Stichwortverzeichnis stellt nichts anderes als einen Zeiger dar wie wir es in diesem Kapitel lernen. Das Stichwortverzeichnis sagt nichts über den Inhalt aus sondert repräsentiert nur die Seite wo sich der Inhalt befindet. Ich hoffe das Ihnen das eine kleine Hilfe darstellt. Noch schnell ein kleiner Überblick....

int *ptr;
int var;

ptr=&var;    //*ptr bekommt die Adresse von var
*ptr=100;   //var bekommt den Wert 100 zugewiesen
*ptr+=100; //var hat jetzt den Wert 200
*ptr++;      //var hat jetzt den Wert 201
*ptr--;        //var hat wieder den Wert 200
printf("%d",*ptr);    //gibt Wert von var aus
printf("%p",&pa);    //gibt Adresse von ptr aus
printf("%p",ptr)      //gibt Adresse von var aus


Ein weitere Möglichkeit zur Behandlung von Pointer muss ich hierbei auch noch erwähnen, nämlich...

ptr1=ptr2;

Hier das Programmbeispiel dazu.....

/*Download:z5.c*/
#include <stdio.h>

int main()
{
int wert=10;
int *ptr1;
int *ptr2;

ptr1=&wert;
ptr2=ptr1;

printf("Wert=%d Adresse in *ptr1=%p\n",*ptr1,ptr1);
printf("Adresse von *ptr1=%p\n",&ptr1);

printf("Wert=%d Adresse in *ptr2=%p\n",*ptr2,ptr2);
printf("Adresse von *ptr2=%p\n",&ptr2);

printf("Adresse von int wert=%p\n",&wert);

return 0;
}

Bei diesem Programm zeigen beide Zeiger auf den selben Wert, nämlich den von 'wert' = 10. Somit zeigen beide auch auf die selbe Adresse. Mit 'ptr2=ptr1' bekommt 'ptr2' die selbe Adresse wie auf die 'ptr1' zeigt. Wenn sie jetzt in unserem Programm einfügen....

*ptr1=11;

ändern wir den Wert von der Variablen 'wert' auf 11 und somit wird unsere Ausgabe immer der Wert 11 sein. Wenn wir...

wert=20;

schreiben, werden auch unsere beiden Zeiger als Wert '20' ausgeben das sie weiterhin auf unsere Variable 'wert' zeigen.

Wichtig ist es auch zu verstehen das Pointer in C typisiert sind. Damit ist gemeint das ein Zeiger vom Datentyp int nicht auf einen Datentyp von double verweisen kann...

/*Download:z6.c*/
#include <stdio.h>

int main()
{
int *int_ptr;
double double_wert=999.999;

int_ptr=&double_wert;
printf("*int_ptr=%d double_wert=%f\n",*int_ptr, double_wert);

return 0;
}

Die Ausgabe des Refernzen des int-Zeigers wird irgendwelchen Murks ausgeben. Der Sinn und Zweck von Zeigern besteht ja im Prinzip nicht daraus auf einfachen Datentypen zu zeigen, sondern hauptsächlich Speicher dynamisch zu reservieren.

Bevor sie zum nächsten Kapitel zum 2. Teil der Zeiger wechseln sollten sie unbedingt dieses Kapitel verstanden haben. Wenn irgendetwas unklar sein sollte, schicken sie mir eine E-Mail was sie nicht verstehen, damit ich das Kapitel so umschreiben kann damit es jeder einfach (soweit das bei diesem Thema geht) versteht.

ein Kapitel zurück          nach oben           ein Kapitel weiter


© 2001,2002 Jürgen Wolf