![]() |
![]() |
Zeiger |
G.Eichelsdörfer
Staatliche Technikerschule Weilburg |
Das Thema Zeiger ist bei vielen Einsteigern in C/C++ eher unbeliebt. Wohl nicht zuletzt aus diesem Grund wurden in der C++ ähnlichen Programmiersprache Java Zeiger völlig vermieden. In C/C++ ist jedoch die Kenntnis von Zeigern unverzichtbar, also gehen wir das Thema an.
Ein Zeiger beinhaltet eine Referenz auf einen Datenwert oder mehrere Datenwerte. Ein Zeiger beinhaltet also keinen Datenwert sondern verweist auf einen solchen. Ein Zeiger zeigt dahin, wo ein solcher Datenwert im Arbeitsspeicher steht. Also beinhaltet ein Zeiger eine Adresse, über die auf einen Datenwert zugegriffen werden kann. Diese Adresse stellt eine Referenz zum Zugriff auf den referenzierten Datenwert dar.
Beispiel: | Zeiger | Auschnitt aus dem Arbeitsspeicher |
Adressen | |||||||||||||||||
|
|
|
In der obigen Darstellung ist P ein Zeiger, der das große M im Arbeitsspeicher referenziert.
Somit muss der Zeiger P die Adresse 0x00105A05 beinhalten.
Um auf das referenzierte Zeichen zuzugreifen, muss dem Zeiger P der so genannte
Dereferenzierungsoperator, das Asterisk *
, vorangestellt werden:
*P
liefert z.B. das Zeichen 'M'
Die obige Situation erhalten wir beispielsweise mit folgendem C++-Code:
char *P = "Mac";
Darin ist "Mac" eine Stringkonstante und P ist eine Zeigervariable,
die ein Zeichen referenziert.
Hier referenziert P das erste Zeichen der Stringkonstanten "Mac".
In diesem Beispiel wird der char-Zeiger P mit der Anfangsadresse einer Stringkonstanten
initialisiert. Man kann einen Zeiger wie jede andere Variable auch ohne Initialisierung
anlegen. Dies sähe beispielsweise so aus:
char *P;
Hier ist P eine Zeigervariable, die eine undefinierte (zufällige) Adresse beinhaltet.
Die Syntax zum Anlegen einer Zeigervariablen lässt sich wie folgt erklären:
Das dereferenzierte P ist ein Zeichen.
Somit beinhaltet P eine Referenz auf ein Zeichen und ist deshalb ein Zeiger auf ein Zeichen.
Der im ersten Beispiel verwendete Zeiger kann auch wie ein Datenfeld verwendet werden.
Wir können auf die Zeichen wie folgt zugreifen lassen:
P[0]
greift auf das 'M' zu,
P[1]
auf das 'a' und
P[2]
auf das 'c'.
Der Zugriff auf die einzelnen Zeichen gelingt auch mit Hilfe der Zeigerarithmetik:
*P
greift auf das 'M' zu,
*(P+1)
auf das 'a' und
*(P+2)
auf das 'c'.
Hier wird der Zeiger P
, der um eine Stelle größere Zeiger (P+1)
und der um zwei Stellen größere Zeiger (P+2)
verwendet.
Beachten Sie, dass (P+i)
ein Zeiger ist und *(P+i)
der Wert,
auf welchen (P+i)
zeigt. *P+i
hingegen ist der von P referenzierte
Wert (*P
), welcher um i vergößert wird. Der Dereferenzierungsoperator bindet
stärker als der Additionsoperator.
(P+1)
ergibt im obigen Beispiel die Adresse 0x00005A06. Dies liegt jedoch
ausschließlich daran, dass ein char-Wert ein Byte beansprucht.
Allgemein referenziert der Zeiger (P+1)
den auf *P nachfolgenden Wert im
Arbeitsspeicher. Dies soll das folgende Beispiel verdeutlichen:
int Feld[] = {9,8,7}, *IP = Feld;
Hier sind
cout << IP << ": " << *IP << endl
<< (IP+1) << ": " << *(IP+1) << endl
<< (IP+2) << ": " << *(IP+2) << endl;
Feld
ein mit drei Ganzzahlen initialisiertes Datenfeld und
IP
ein Zeiger auf einen int-Wert. IP
wird mit der Anfangsadresse
von Feld
initialisiert. Unter der Voraussetzung, dass int
-Werte
32 Bit bzw. 4 Byte beanspruchen, liefert diese Sequenz beispielsweise die folgende
Ausgaben:
0012FF74: 9 0012FF78: 8 0012FF7C: 7 |
Links steht jeweils der Inhalt des Zeigers IP, (IP+1), (IP+2). Hinter dem Doppelpunkt folgt der durch den Zeiger referenzierte Wert. Die Adressen der Zeiger liegen jeweils um 4 auseinander, weil ein int 4 Bytes beansprucht. |