Advanced Services | Pointer |
Ein Pointer ist eine Variable, die die Speicheradresse einer anderen Variablen enthält. Im folgenden wird in der ersten Zeile eine Variable zahl definiert und in der zweiten Zeile eine Variable poi. poi ist ein Pointer (erkennbar am Stern *) und erhält die Adresse der Variablen zahl, erkennbar am Adreßoperator &. In dieser Situation sagt man "poi zeigt auf zahl".
int zahl = 42; int *poi = &zahl; // (1) Deklaration mit Initialisierung
Im einleitenden Beispiel wird die Variable poi definiert und sofort mit der Adresse von zahl initialisiert. Auf der Ebene der Deklaration/Definition kennzeichnet der Stern eine Pointervariable. Initialisiert man den Pointer in einem Statement darf der Stern nicht mehr verwendet werden, weil er dann eine andere Bedeutung hat.
int zahl = 42; int *poi ; // Deklaration bzw. Definition poi = &zahl; // (2) Statement, Pointervariable ohne Stern
(1) und (2) machen exakt dasselbe. Sie initialisieren die Variable poi mit der Adresse von zahl.
Wird der Stern vor einer Pointervariablen in einem Statement gesetzt, so bedeutet dies einen Zugriff auf die Variable, in diesem Fall die Variable zahl über die im Pointer gespeicherte Adresse von zahl. Man sagt, der Pointer wird dereferenziert.
printf("%d", *poi); // ergibt 42 // Dereferenzierung
Auch ein schreibender Zugriff auf zahl über poi ist möglich:
*poi = 43; printf("%d", zahl); // ergibt 43
Pointer bekommen beim Deklarieren einen Typ. Es ist der Typ der Variable auf die sie zeigen. Eine Ausnahme stellt der void-Pointer dar. void-Pointer werden im übernächsten Abschnitt vorgestellt.
int *ipoi, zahl, zahl2, *ipoi2;
Will man Pointer und normale Variablen in einer Zeile deklarieren, so geschieht das wie im obigen Codeschnipsel. Das Schema gilt für jeden beliebigen Typ.
double *dpoi, dpoi2, dou, dou2;
Mit dem Schlüsselwort const wird angezeigt, daß etwas konstant ist. Aber was? Sobald man Pointer verwendet, kann es in Verbindung mit const leicht zu Verwechslungen kommen.
Ohne Pointer ist die Sache sehr einfach. Im folgenden führt das zweite Statement zu einem Fehler, da zahl durch const zu einer Readonlyvariablen wird. Das gilt natürlich für jeden Typ.
const int zahl = 17; zahl = 18; // error:: assignment of read-only variable
Die Reihenfolge von const und int ist dabei egal. Das folgende ist äquivalent:
int const zahl = 17; zahl = 18; // error:: assignment of read-only variable
Die erste Schreibweise wird in der Regel bevorzugt. Jetzt betrachten wir const im Zusammenhang mit Pointern. Hier kann sich const auf den Pointer beziehen oder auf die Variable, auf die der Pointer zeigt, je nachdem wo das Schlüsselwort const steht. der Vollständigkeit halber beginnen wir mit einem Pointer ohne const.
Hier ist weder der Pointer konstant noch die Variable auf die er zeigt.
int zahl = 17; int* ipoi = &zahl; printf("%d", *ipoi); // 17 *ipoi = 18; // OK zahl = 18; // OK int zahl2 = 19; ipoi = &zahl2; *ipoi = 42; // OK
Hier steht const vor dem Stern *.
int zahl = 17; const int* ipoi = &zahl; printf("%d", *ipoi); // 17 //*ipoi = 18; // error: assignment of read-only location zahl = 18; // OK int zahl2 = 19; ipoi = &zahl2; //*ipoi = 42; // error: assignment of read-only location
Hier bezieht sich const auf das, worauf der Pointer zeigt. Ein so definierter Pointer kann die Variable auf die er zeigt nur auslesen, aber nicht verändern. Man kann den Wert aber über die Variable zahl ändern, denn diese ist ja nicht konstant. Ebenso darf der Pointer auf eine andere Adresse zeigen. Aber diese Adresse kann dann auch nur gelesen werden. ipoi ist also ein readonly-Pointer. Ob man const int *poi oder int const *poi schreibt ist egal. Ob der Stern * am Anfang von poi klebt oder am Ende von int bzw. const spielt ebenfalls keine Rolle (const int* poi bzw. int const* poi).
Nun wollen wird einen Pointer, der konstant auf eine Adresse zeigt, aber auf diese Adresse Lese- und Schreibzugriff hat. Hier steht const nach dem Stern *.
int zahl = 17; int zahl2 = 42; int* const ipoi = &zahl; // ipoi zeigt konstant auf zahl *ipoi = 18; //ipoi = &zahl2; // error: assignment of read-only variable
Steht const nach dem Pointerstern bezieht es sich auf den Pointer selbst und macht diesen zu einer konstanten. Der Pointer hält hier konstant die Adresse der Variablen zahl. Eine erneute Zuweisung führt zu einem Fehler.
Man kann const auch zweimal verwenden, also vor und nach dem Stern *. Dann ist sowohl der Pointer konstant als auch der Wert worauf er zeigt.
int zahl = 17; // Wert konstant und Pointer konstant const int* const ipoi = &zahl; //*ipoi = 69; // assignment of read-only location //ipoi = &zahl; // assignment of read-only variable
Mit dieser Regel erhält man die Möglichkeit alle drei Fälle mit einer Regel zu erfassen. Man beginnt bei der Pointervariablen und liest dann nach links weiter. Den Stern übersetzt man mit Pointer.
const int* ipoi = &zahl;
ipoi ist ein Pointer auf ein int das konstant ist.
int *const ipoi = &zahl;
ipoi ist ein konstanter Pointer auf ein int.
const int *const ipoi = &zahl;
ipoi ist ein konstanter Pointer auf ein int, das konstant ist.
Ein void-Pointer ist ein Pointer, der keinem Typ zugeordet ist. So liefert etwea die Funktion malloc() Speicher der noch keinem Typ zugeordnet ist und der dann einen bestimmten Typ erhält. Im folgenden Codeschnipsel wird der Speicher zuerst auf int typisiert, anschließend dann auf char.
#include <cstdlib> void voidpointer() { void* memory = malloc(2000); // 2000 bytes int *intpoi = (int*)memory; int *const intarr = (int*)memory; char *charpoi = (char*)memory; char *const chararr = (char*)memory; }
Statt dem klassischen Cast kann man hier auch static_cast einsetzen.
#include <cstdlib> void voidpointer2() { void* memory = malloc(2000); int *intpoi = static_cast<int*>(memory); int *const intarr = static_cast<int*const>(memory); char *charpoi = static_cast<char*>(memory); char *const chararr = static_cast<char*const>(memory); }
Ein Pointer auf ein Array (erkennbar an den eckigen Klammern) wird vom Compiler immer als konstanter Pointer angelegt, er hat damit etwa für int den Typ int *const
void arraypointer() { int arr1[17] = {0}; // Array der Größe 17, alle Elemente auf 0 gesetzt int arr2[17] = {1, 2, 3}; // 17 Elemente, // Array der Größe 17, die ersten Elemente wie in der Klammer angegeben, die // restlichen Elemente auf 0 gesetzt }
Im klassischen C werden Strings über char-Pointer realisiert, daher gibt es hier etwas andere Regeln. Ein char-Pointer kann auf eine einzige char-Variable zeigen oder auf einen C-String. Im letzten Fall zeigt er auf den Anfang einer Zeichenkette deren letztes Element den Inhalt 0 hat (nullterminierter String).
In diesem Fall gelten genau die oben erwähnten Regeln. Man kann das als Übung durchspielen.
Im folgenden zeigt der char-Pointer immer auf einen C-String.
Dies ist der eigentliche Sonderfall. Hier wird der char-Pointer mit einem C-String initialisiert. Die Zeichen liegen hintereinander im Speicher und nach dem letzten Zeichen wird vom Compiler eine binäre 0 als Stringendezeichen eingetragen. Der Pointer selbst zeigt auf den Anfang des Speicherbereichs, also hier im Beispiel auf T. Darüber hinaus, und das ist das besondere, wird der String als Konstante angelegt. Der Pointer selbst ist aber nicht konstant und kann auch für andere Strings verwendet werden.
char* st = "This is a simple string"; // Stringinhalt ist konstant, Pointer nicht puts(s); // st[17] = ' '; // Laufzeitfehler // st[19] = 'h'; // Laufzeitfehler st = "This is another string"; puts(s);
Genauer gesagt, der Compiler legt in diesem Fall folgendes an:
const char* cs = "This is a simple string"; // Stringinhalt ist konstant, Pointer nicht puts(cs); cs = "This is another string"; puts(cs);
In der ersten Variante wurde also implizit von const char* auf char* gecastet.
Das const nach dem Pointerstern müssen wir selbst schreiben.
char* const ccs = "This is a simple string"; // Stringinhalt ist konstant, Pointer auch puts(ccs); // ccs[0] = 'A'; // Laufzeitfehler //ccs = "This is another string"; // assignment of read-only variable
Der letzte Fall. Ein veränderbarer String wird über ein klassisches Array realisiert. Die Arrayklammern, die nach dem Variablennamen stehen müssen, können leer bleiben. Der Compiler ermittelt die Arraygröße automatisch. In diesem Fall wird der Pointer als Konstante angelegt.
char str[] = "This is a simple string"; // Stringinhalt ist veränderbar, Pointer ist konstant puts(str); str[17] = ' '; str[19] = 'h'; puts(str); // This is a simple thing // str = "foo"; // incompatible types