====== Zeiger (Pointer) ====== Zeiger (engl. pointers) sind Variablen, die als Wert die Speicheradresse einer anderen Variable enthalten. Jede Variable wird in CPP an einer bestimmten Position im Hauptspeicher abgelegt. Diese Position nennt man Speicheradresse (engl. memory address). CPP bietet die Möglichkeit, die Adresse jeder Variable zu ermitteln. Solange eine Variable gültig ist, bleibt sie an ein und derselben Stelle im Speicher. Am einfachsten vergegenwärtigt man sich dieses Konzept anhand der globalen Variablen. Diese werden außerhalb aller Funktionen und Klassen deklariert und sind überall gültig. Auf sie kann man von jeder Klasse und jeder Funktion aus zugreifen. Über globale Variablen ist bereits zur Kompilierzeit bekannt, wo sie sich innerhalb des Speichers befinden (also kennt das Programm ihre Adresse). Zeiger sind nichts anderes als normale Variablen. Sie werden deklariert (und definiert), besitzen einen Gültigkeitsbereich, eine Adresse und einen Wert. Dieser Wert, der Inhalt der Zeigervariable, ist aber nicht wie in unseren bisherigen Beispielen eine Zahl, sondern die Adresse einer anderen Variable oder eines Speicherbereichs. Bei der Deklaration einer Zeigervariable wird der Typ der Variable festgelegt, auf den sie verweisen soll. #include int main() { int Wert; // eine int-Variable int *pWert; // eine Zeigervariable, zeigt auf einen int int *pZahl; // ein weiterer "Zeiger auf int" Wert = 10; // Zuweisung eines Wertes an eine int-Variable pWert = &Wert; // Adressoperator '&' liefert die Adresse einer Variable pZahl = pWert; // pZahl und pWert zeigen jetzt auf dieselbe Variable ===Beispielhafte Speicherbelegung des Programms im Hauptspeicher:=== ^Datentyp^Variable^Adresse^Wert| |int|Wert|0x0001|10| |int *|pWert|0x0005|0x0001| |int *|pZahl|0x0009|0x0001| |…|…|.|.| |…|…|.|.| Der Adressoperator & kann auf jede Variable angewandt werden und liefert deren Adresse, die man einer (dem Variablentyp entsprechenden) Zeigervariablen zuweisen kann. Wie im Beispiel gezeigt, können Zeiger gleichen Typs einander zugewiesen werden. Zeiger verschiedenen Typs bedürfen einer Typumwandlung. Die Zeigervariablen pWert und pZahl sind an verschiedenen Stellen im Speicher abgelegt, nur die Inhalte sind gleich. Wollen Sie auf den Wert zugreifen, der sich hinter der im Zeiger gespeicherten Adresse verbirgt, so verwenden Sie den Dereferenzierungsoperator *. *pWert += 5; *pZahl += 8; std::cout << "Wert = " << Wert << std::endl; ===Beispielhafte Speicherbelegung des Programms im Hauptspeicher:=== ^Datentyp^Variable^Adresse^Wert| |int|Wert|0x0001|10 --> 15 --> 23| |int *|pWert|0x0005|0x0001| |int *|pZahl|0x0009|0x0001| |…|…|.|.| |…|…|.|.| ===Ausgabe:=== Wert = 23 Man nennt das den Zeiger dereferenzieren. Im Beispiel erhalten Sie die Ausgabe Wert = 23, denn pWert und pZahl verweisen ja beide auf die Variable Wert. Um es noch einmal hervorzuheben: Zeiger auf Integer (int) sind selbst keine Integer. Den Versuch, einer Zeigervariablen eine Zahl zuzuweisen, beantwortet der Compiler mit einer Fehlermeldung oder mindestens einer Warnung. Hier gibt es nur eine Ausnahme: die Zahl 0 darf jedem beliebigen Zeiger zugewiesen werden. Ein solcher Nullzeiger zeigt nirgendwohin. Der Versuch, ihn zu dereferenzieren, führt zu einem Laufzeitfehler. ===== Bespiel Zeigerübung ===== int main() { int zahl=10; int *z=NULL; int **zz=NULL; cout<< zahl< ====== Verschiedene Konventionen bei der Definition von Zeigern ====== Bei der Definition einer Zeigervariablen muss das Zeichen * nicht unmittelbar auf den Datentyp folgen. Die folgenden vier Definitionen sind gleichwertig: int* i; // Whitespace (z.B. ein Leerzeichen) nach * int *i; // Whitespace vor * int * i; // Whitespace vor * und nach * int*i; // Kein whitespace vor * und nach * Versuchen wir nun, diese vier Definitionen nach demselben Schema wie eine Definition von „gewöhnlichen“ Variablen zu interpretieren. Bei einer solchen Definition bedeutet ''T v;'' dass eine Variable v definiert wird, die den Datentyp T hat. Für die vier gleichwertigen Definitionen ergeben sich verschiedene Interpretationen, die als Kommentar angegeben sind: int* i; // Definition der Variablen i des Datentyps int* int *i; // Irreführend: Es wird kein "*i" definiert, // obwohl die dereferenzierte Variable *i heißt. int * i; // Datentyp int oder int* oder was? int*i; // Datentyp int oder int* oder was? Offensichtlich passen nur die ersten beiden in dieses Schema. Die erste führt dabei zu einer richtigen und die zweite zu einer falschen Interpretation. Allerdings passt die erste Schreibweise nur bei der Definitionen einer einzelnen Zeigervariablen in dieses Schema, da sich der * bei einer Definition nur auf die Variable unmittelbar rechts vom * bezieht: int* i,j,k; // definiert int* i, int j, int k // und nicht: int* j, int* k Zur Vermeidung solcher Missverständnisse sind zwei Schreibweisen verbreitet: * C-Programmierer verwenden oft die zweite Schreibweise von oben, obwohl sie nicht in das Schema der Definition von „gewöhnlichen“ Variablen passt. Diese Schreibweise wird auch in Turbo CPP verwendet. int *i,*j,*k; // definiert int* i, int* j, int* k * Bjarne Stroustrup (einer der C bzw. CPP-Entwickler) empfiehlt, auf Mehrfachdefinitionen zu verzichten. Er schreibt den * wie in „int* pi;“ immer unmittelbar nach dem Datentyp. ==== Ein weiteres Beispiel.... ==== int zahl = 7; int *zeiger; zeiger = &zahl; printf("Zeiger-Wert: %d\n", *zeiger); Ein **Zeiger repräsentiert eine Adresse** und nicht wie eine Variable einen Wert. Will man auf den Wert der Adresse zugreifen, auf die ein Zeiger zeigt, muss der Stern * vor den Namen gesetzt werden. {{:inf:inf8bi_201819:3:pasted:20190121-155046.png}} ===== Zeiger auf Zeiger ===== Zeiger zeigen auf Adressen. Sie können nicht nur auf die Adressen von Variablen, sondern auch auf die Adressen von Zeigern verweisen. Dies erreicht man mit dem doppelten Stern-Operator * * . int zahl=7; int *zeiger = &zahl; int **zeigerAufZeiger = &zeiger; cout << "Wert von zeigerAufZeiger -> zeiger -> zahl:" << **zeigerAufZeiger; === Ausgabe === Wert von zeigerAufZeiger -> zeiger -> zahl: 7