Pointer (= Zeiger)
Was ist ein
Pointer?
Ein Pointer ist im übertragenen Sinn ein Pfeil, der
auf einen Bereich im Hauptspeicher zeigt. Während ein Programm
ausgeführt wird, werden bekanntlich Variablen erzeugt. Diese
Variablen werden alle im Hauptspeicher gehalten. Und genau dorthin
zeigen auch die Pointer.
Was kann ein
Pointer?
Ein Pointer kann den Wert abrufen vom Speicherbereich, auf
den er zeigt (=Indirektion). Er kann quasi den Inhalt dort
herauslesen.
Aber er kann auch einfach nur die Speicheradresse ausgeben
vom Speicherbereich, auf den er zeigt.
Wie deklariere
ich einen Pointer?
Zur Erinnerung, was Deklaration überhaupt bedeutet:
Die Deklaration ist ein Vorgang, bei dem ich dem Programm mitteilen
will, dass irgendwo im Programm das deklarierte Objekt benutzt
wird. Das Programm soll also darauf „gefasst“ sein,
dass ich vorhabe eine Variable mit dem angegebenen Namen zu verwenden.
Je nachdem was die deklarierte Variable speichern soll, muss Speicher
reserviert werden. Das hängt ganz vom Typ der Variable ab.
Nun zur eigentlichen Deklaration eines Pointers:
Jeder Zeiger zeigt auf eine Speicherzelle, wo aber nur ein
bestimmter Typ gespeichert werden kann. Das heißt ein Zeiger,
der jetzt auf eine Adresse zeigt, wo ein Integer Wert gespeichert
ist, kann im nächsten Moment nicht auf eine Adresse zeigen, die einen
String enthält.
Man gibt deshalb bei der Deklaration eines Zeigers an, auf
welchen Typ von Wert er zeigen wird, mit folgender
Syntax:
Syntax:
Typ* zeigerName;
Beispiele:
int* neuerIntZeiger;
String* hierNochEiner;
char* fuerEinzelneZeichen;
float* zahl;
Für gewöhnlich stellt man vor den Zeigernamen
allerdings noch ein „p“ (als Kürzel für
„Pointer“) voran, damit, wenn ein anderer Programmierer
den Code zum ersten Mal liest, dieser auch gleich weiß, wo
überall ein Zeiger zu sehen ist. Das heißt unsere
Beispiele sollten ganz korrekt wie folgt aussehen:
Beispiele:
int* pneuerIntZeiger;
String* phierNochEiner;
char* pfuerEinzelneZeichen;
float* pzahl;
Wie benutze ich
einen Pointer?
Wie bereits erwähnt, zeigen Zeiger auf
Speicheradressen. Das heißt, wenn man einen Zeiger auf die
Adresse einer Variablen zeigen lassen möchte, kann man nicht
einfach den Variablennamen dem Zeiger zuweisen.
Beispiel:
int x = 5;
int* pzeigtAufZahl;
//Deklaration eines
Int-Zeigers
pzeigtAufZahl = x;
//F A L S C H
pzeigtAufZahl =
&x;
//richtig
Nun, wofür steht dieses seltsame &-Zeichen? Wenn
wir nur „x“ stehen hätten, würden wir auf den
Wert von x zugreifen. Wir wollen aber nicht den Wert, da ein
Zeiger stets auf eine Adresse im Speicher zeigen muss. Wir wollen also die Adresse der Variablen x.
Man bekommt ja manchmal von Windows die Fehlermeldung zu
lesen: „Fehler beim Zugriff auf Speicheradresse
xxx0EFFF-x00EAAB0“ – und genau so eine Speicheradresse
liefert uns der &-Operator.
Durch den &-Operator erhalten wir demnach
die Speicheradresse der Variablen x und weisen diese
Adresse dem Zeiger pzeigtAufZahl zu.
Ja und jetzt??
Jetzt zeigt pzeigtAufZahl auf eine Adresse im
Speicher, aber damit können wir ja nichts anfangen?! Was
sollen wir schon mit irgendeiner kryptischen Adresse im
Hauptspeicher sinnvoll tun?? Um den Wert aus dieser Adresse zu
manipulieren, müssen wir direkt auf die Daten dort zugreifen. In unserem Beispiel ist dort eine
Integer-Zahl gespeichert (=5). Und wie gelangen wir nun an diese
Integer-Zahl? Wir müssen den Wert an dieser Adresse auslesen.
Halten wir nochmals fest: Wir haben zunächst aus dem
Wert der Variablen x eine Adresse
bezogen, indem wir den &-Operator vorangestellt haben. Nun
wollen wir wieder durch die Adresse zurück an den
Wert gelangen. Dazu benutzen wir den
*-Operator.
Beispiel:
int x = 5;
int* pzeigtAufZahl;
//Deklaration eines
Int-Zeigers
pzeigtAufZahl =
&x;
//pzeigtAufZahl zeigt nun auf
die Speicheradresse von x
int a = *pzeigtAufZahl +
2; //a = 7 (ergibt sich aus 5 +
2)
Hier wird auf den Wert des Zeigers pzeigtAufZahl
zugegriffen, indem ein * vorangestellt wird. Diesen Vorgang nennt
man Dereferenzierung.
Zum Wert von pZeigtAufZahl wird 2 addiert, was 7
ergibt.
Man kann nun stets überall dort *pzeigtAufZahl verwenden, wo man früher
x verwendet hätte. Denn *pzeigtAufZahl nimmt
immer den Wert des Speichers, wo x abgelegt ist –
somit denselben Wert an. Wenn man *pzeigtAufZahl
verändert, wird auch x verändert. Denn durch das
Verändern von *pzeigtAufZahl manipuliert man
den Speicherbereich. Auf der anderen Seite verändert man durch
das Ändern von x aber auch *pzeigtAufZahl. Das
heißt die beiden (x und * pzeigtAufZahl) sind jetzt
Synonyme.
Kurz zusammengefasst:
Deklaration eines
Zeigers durch: *
Objekt/Wert à Adresse
durch: &
Adresse
à
Objekt/Wert durch:
*
Wichtig ist, dass vor der ersten Benutzung von
*pzeigtAufZahl, dem Zeiger pzeigtAufZahl eine
Speicheradresse zugewiesen wurde (z.B. durch Anweisung: pzeigtAufZahl = &x;). Ansonsten
würde die Dereferenzierung zu einem Zufallsergebnis
führen und man würde indirekt auf irgendeinen
Speicherbereich zugreifen und das würde in Folge vermutlich zu
einer Speicherverletzung führen. Windows müsste dann das
Programm aufgrund einer Speicherverletzung
schließen…ja so kommt es zu Abstürzen von
Programmen.
|