C-Tutorials.de Logo

zum Inhaltsverzeichnis

C Tutorial: Pointer (Zeiger)


Pointer grundsätzlich:

Das einzigste was es grundsätzlich über Pointer (Englisch für Zeiger) zu sagen gibt, ist folgendes: Ein Pointer ist eine Variable, die eine Adresse im RAM des PCs enthält.

Durch einen Pointer Variablen lesen/verändern:

#include <stdio.h>

int main ()
{
int Zahl = 2;
int *Pointer = &Zahl; //initialisieren des Pointers "Pointer" auf die Variable "Zahl"

  printf("%i %i",Zahl,*Pointer); //Inhalt von "Zahl" und der Variablen ausgeben auf die "*Pointer" zeigt (in diesem Fall "Zahl")

  *Pointer = 4; //Den Inhalt der Variablen auf die "Pointer" zeigt verändern.

  printf("\n%i %i",Zahl,*Pointer);

  getch();
}

Im Zusammenhang mit Pointern sind nur 2 neue Operatoren notwendig. Einmal der &-Operator (Adressoperator) und der *-Operator. Schreibt man den &-Operator vor eine Variable bedeutet das so viel wie „gib mir die Adresse im RAM die diese Variable enthält“.
Der *-Operator sagt einem Pointer „jetzt geht es um den Inhalt der Variablen, die an dieser Stelle im RAM zu finden ist“. Er wird gebraucht um wie im Beispiel einen Pointer zu initialisieren und um den Inhalt der Variablen zu ändern, auf die der Pointer zeigt.
Beim Initialisieren eines Pointers musst du darauf achten, das sein Datentyp dem der Variablen entspricht, auf die der Pointer zeigt! Zur Verdeutlichung hier noch ein kleines Beispielprogramm:

#include <stdio.h>

int main ()
{
int z = 3;
int *p = &z;

  printf("%i\n",z); //gibt ganz normal den Wert von z aus


  printf("%i\n",&z); //Der Adressoperator sorgt dafür, das die physikalische Adresse der Variable im RAM ausgegeben wird


  printf("%i\n",p); //p enthält die Adresse von z


  printf("%i\n",*p); //Hier wird angezeigt welcher Wert an dieser Stelle im RAM steht


  printf("%i\n",*&z); //Blöde Konstruktion, die sinnlos ist, aber hilft um etwas zu verdeutlichen: Zuerst wird die Adresse im RAM von z "zurückgeben" (&z) und dann wird nachgesehen was sich an dieser Adresse im RAM befindet und das ausgegeben.


  getch();
}

Du wirst einige Eingewöhnungszeit brauchen um die an die beiden Operatoren zu gewöhnen, aber irgendwann wirst du sie nicht mehr verwechseln. Außerdem musst du musst aufpassen, das du dir nicht die Adresse eine Pointers liefern lässt! Ein Pointer auf einen Pointer nennt man Handle und da weiß ich selbst nicht wozu man das eigentlich braucht.

Aufgabe:

Schreibe ein kleines Programm mit einer Variablen und einem Pointer darauf, welches durch den Pointer eine mehrstellige Zahl einließt und dann auch wieder ausgibt. Lösung:

Na, bist du gescheitert? Wenn ja: macht nichts. Dann hat es jetzt wenigstens den Learning-by-Doing Effekt.
Grundsätzlich schreibt man scanf so: scanf("%i",&Variable). Hier ist ganz besonders der &-Operator zu beachten! Er sagt dem scanf „hol dir die Adresse der Variablen und schreib an diese Adresse“. In diesem Beispiel haben wir aber bereits die Adresse der Variablen in die wir schreiben wollen als Pointer gespeichert! Also muss es so lauten: scanf("%i",Pointer). Jetzt kannst du es nochmal versuchen. Lösung:
#include <stdio.h>

int main ()
{
int Eingabe;
int *Pointer = &Eingabe;

  printf("Gib eine beliebige Zahl ein: ");

  scanf("%i",Pointer);

  printf("\nAusgabe der Variable: %i\n",Eingabe);
  printf("Ausgabe ueber Pointer: %i",*Pointer);

  getch();
}
Wenn man bei Pointern nicht aufpasst, kann es ganz schnell passieren, das man irgendeinem Befehl sagt, er solle an eine Adresse schreiben die für eine ganz andere Variable oder möglichweise temporäre Dateien des Betriebsystems oder anderen Programmen überschreibt. Im schlimmsten Falle stützt also das Betriebsystem ab. Du musst bei Pointern also immer höllisch aufpassen!

Pointer in Unterprogramme übergeben:

Vielleicht hast du dich im vorherigen Unterkapitel gefragt, was denn jetzt eigentlich der Sinn von Pointern ist? Ist doch bloß umständlich und bringt nix!? Dann ist dieses Unterkapitel für dich ganz besonders interessant (was nicht heißt, das es die anderen überspringen sollen(dürfen)).
Übergibt man eine Variable in ein Unterprogramm wird diese kopiert. Das heißt die ist zwei mal im RAM vorhanden. Mit Pointern hat die Sache den Vorteil, das man einem Unterprogramm nur die Adresse gibt und es die Daten im main sowie im Unterprogramm aus der selben Zuordnungseinheit im RAM gelesen weredn. Mit einer int Variablen macht die ganze Sache nicht wirklich viel Sinn und spart auch nicht wirklich Platz im RAM, aber man stelle sich bloß mal ein (grafisches) Spiel aus dem Laden vor. Würden die Entwickler dieser Spiele nicht mit Pointern arbeiten, würden wahrscheinlich alle Grafiken doppelt und dreifach im RAM liegen. Das wäre dann nicht wirklich hardwareschonend. Ich hoffe der Existenzgrund von Pointern ist jetzt klar, darum gibt’s jetzt das Beispiel von oben, nur das die Ausgabe in einem Unterprogramm gemacht wird:

#include <stdio.h>

int Ausgabe (int *UebergebenerP) //Dem Unterprogramm mitteilen, dass es sich hier um einen Pointer handelt
{
  *UebergebenerP = 4; //An der übergeben Adresse den Inhalt ändern

  printf("\n%i",*UebergebenerP); //Inhalt der übergeben Adresse ausgeben
}

int main ()
{
int Zahl = 2;
int *Pointer = &Zahl;

  printf("%i",*Pointer);
  Ausgabe(Pointer); /*Hier ist der *-Operator nicht notwendig! Hier soll ja tatsächlich die physikalische Adresse übergeben werden. Würde man hier "Ausgabe(*Pointer)" schreiben könnte man die Variablen auch gleich so übergeben, denn dann würde man den Inhalt der Adresse im RAM übergeben und dann könnte man auch gleich die Variable übergeben. Genau das wollen wir aber eben nicht!*/

  getch();
}

Aufgabe:

Schreibe jetzt dein Programm aus der vorherigen Aufgabe so um, das Initialisieren der Datei weiterhin im main Programm bleibt, die Ein- und Ausgabe aber in einem anderen Unterprogramm stattfindet. Lösung:

#include <stdio.h>

int EinUndAusgabe (int *uebergeben)
{
  printf("Gib eine beliebige Zahl ein: ");

  scanf("%i",uebergeben); //wieder ist der Adressoperator (&) nicht nötig, weil der Pointer breits die Adresse enthält

  printf("Ausgabe ueber Pointer: %i",*uebergeben);
}

int main ()
{
int Eingabe;
int *Pointer = &Eingabe;

  EinUndAusgabe(Pointer);

  getch();
}

Ein weiterer großer Vorteil von Pointern in Unterprogrammen ist, das man theoretisch unendlich „Rückgabevariablen“ für ein Unterprogramm haben kann. Nur werden die Daten nicht per return zurückgegeben, sondern es wird einfach in die Adresse geschrieben, die man übergeben hat. Das heißt, dass man in der vorherigen Aufgabe die Ausgabe aus dem Unterprogramm einfach in der main Programm verschieben kann. Bei „scanf("%i",uebergeben)“ werden die Daten ja in die Variable geschrieben.

Aufgabe:

Verschiebe die Ausgabe ins main Programm. „Lösung“:

#include <stdio.h>

int Eingabe (int *uebergeben)
{
  printf("Gib eine beliebige Zahl ein: ");

  scanf("%i",uebergeben); //wieder ist der Adressoperator (&) nicht nötig, weil der Pointer breits die Adresse enthält
}

int main ()
{
int Eingabe;
int *Pointer = &Eingabe;

  Eingabe(Pointer);

  printf("Ausgabe ueber Pointer: %i",*Pointer);

  getch();
}

Arrays und Pointer:

Dieser Teil wird der schwerste sein. Du musst dir sagen lassen, das du im Prinzip schon längst mit Pointern gearbeitet hast, bevor du dieses Kapitel angefasst hast – in Kapitel 9 über Arrays. Arrays sind nämlich Pointer. Ich brauch garnicht so viel darüber schreiben, weil sich im Prinzip nichts zum vorherigen Kapitel ändert:

#include <stdio.h>

int Ausgabe (int *uebergeben)
{
  printf("%i",uebergeben[0]); // gibt den Wert der übergeben Adresse aus
  printf(" %i",uebergeben[1]); // gibt den Wert der übergeben Adresse + "eine Adresse weiter im RAM" aus
  printf(" %i",uebergeben[2]); // gibt den Wert der übergeben Adresse + "zwei Adressen weiter im RAM" aus
}

int main ()
{
int Zahl[3] = {2,4,7};

  Ausgabe(Zahl); //Hier wird ausschließlich die Adresse des ersten Elements des Arrays übergeben!

  getch();
}

Das war ja noch einfach, aber hier kommt die Geschichte mit zweidimensionalen Arrays:

#include <stdio.h>

int Ausgabe (int *uebergeben)
{
  printf("%i",uebergeben[0*4+1]); // Zahl[0][1] ausgeben
  printf(" %i",uebergeben[0*4+2]); // Zahl[0][2] ausgeben
  printf(" %i",uebergeben[0*4+3]); // Zahl[0][3] ausgeben
  printf(" %i",uebergeben[1*4+1]); // Zahl[1][1] ausgeben
  printf(" %i",uebergeben[1*4+3]); // Zahl[1][3] ausgeben
}

int main ()
{
int Zahl[2][4] = {{11,12,13,14},{21,22,23,24}};

  Ausgabe(Zahl); //Hier wird ausschließlich die Adresse des ersten Elements des Arrays übergeben!

  getch();
}

Schwierig? Nein, ganz einfach! Du musst dir nur die „Formel“ in den [] Klammern ansehen. Beispiel:

printf(" %i",uebergeben[1*4+3]); // Zahl[1][3] ausgeben

Hier wird das zweite Array (1) der ersten Dimension ausgewählt. Da jedes Array der zweiten Dimension 4 Elemente hat, rechnen wir *4. Zuletzt wählen wir noch aus welches Element der zweiten Dimension wir haben haben wollen. In diesem Fall das vierte, also *3. Natürlich kann man dieses Spielchen unendlich fortsetzen. Dann wird es allerdings sehr kompliziert. Deshalb bekommst du hier deine Aufgabenstellung um dieses Kapitel abzuschließen:

Aufgabe:

Erstelle ein Programm, das in ein [3][3] Array nacheinander 9 Werte einließt und dann den ersten und letzten ausgibt:

#include <stdio.h>

int main ()
{
int Zahl[3][3];

  scanf("%i",Zahl[0*3+0]);
  scanf("%i",Zahl[0*3+1]);
  scanf("%i",Zahl[0*3+2]);
  scanf("%i",Zahl[1*3+0]);
  scanf("%i",Zahl[1*3+1]);
  scanf("%i",Zahl[1*3+2]);
  scanf("%i",Zahl[2*3+0]);
  scanf("%i",Zahl[2*3+1]);
  scanf("%i",Zahl[2*3+2]);

  printf("%i",*Zahl[0*3+0]); //achte darauf, dass du dem PC sagen müsst, dass er an dieser Stelle nicht die Adresse sondern derren Inhalt ausgeben soll!
  printf(" %i",*Zahl[2*3+2]);

  getch();
}

zum Inhaltsverzeichnis