C++
VeröffentlichtRevision 1

Zeiger, nullptr und Dereferenzieren

Ein Zeiger, englisch pointer, ist ein Wert, der auf ein passendes Objekt beziehungsweise eine Funktion zeigen kann. Ein Objektzeiger kann auch ein Nullzeiger sein: Er bezeichnet dann kein Objekt. Verwende dafür das Schlüsselwort nullptr.

Zeiger, nullptr und Dereferenzieren#

Referenzen, Zeiger und Speicher · Lektion 34 von 60

Voraussetzungen: Speicher, Referenzen. Lernziel: Du greifst kontrolliert über eine Adresse auf ein Objekt zu.

Ein Zeiger, englisch pointer, ist ein Wert, der auf ein passendes Objekt beziehungsweise eine Funktion zeigen kann. Ein Objektzeiger kann auch ein Nullzeiger sein: Er bezeichnet dann kein Objekt. Verwende dafür das Schlüsselwort nullptr.

C++
1#include <iostream>2 3int main() {4    int zahl{42};5    int andereZahl{7};6    int* zeiger = &zahl;7    if (zeiger != nullptr) {8        *zeiger = 50;9        std::cout << zahl << '\n';10    }11    zeiger = &andereZahl;12    std::cout << *zeiger << '\n';13    zeiger = nullptr;14}

Die Ausgabe ist 50 und 7. int* bedeutet „Zeiger auf int“. &zahl liefert die Adresse von zahl. *zeiger dereferenziert den Zeiger: Du greifst auf das Zielobjekt zu. Die Zuweisung zeiger = &andereZahl ändert dagegen den gespeicherten Zeigerwert, nicht die alte Zahl.

Schritt für Schritt im Speicher#

Stell dir zahl und andereZahl als zwei beschriftete Speicherplätze vor. Der Zeiger speichert, welchen Platz du gerade meinst. Die Namen in dieser Skizze stehen für Adressen; echte Adresswerte sehen je nach Ausführung anders aus.

Nur Text
1Nach int* zeiger = &zahl:2 3zeiger ───────► zahl [42]       andereZahl [7]4 5Nach *zeiger = 50:6 7zeiger ───────► zahl [50]       andereZahl [7]8 9Nach zeiger = &andereZahl:10 11               zahl [50]       andereZahl [7] ◄────── zeiger

Anweisung

Zeiger zeigt danach auf

Wert von zahl

Wert von andereZahl

int* zeiger = &zahl;

zahl

42

7

*zeiger = 50;

zahl

50

7

zeiger = &andereZahl;

andereZahl

50

7

zeiger = nullptr;

kein Objekt

50

7

zeiger = nullptr zerstört keine der beiden Zahlen. Es verändert nur den Zeiger. Ebenso reserviert int* zeiger = &zahl keinen neuen Speicher für eine weitere Zahl: zahl existiert bereits.

Ein Zeichen, mehrere Bedeutungen#

Das Sternchen in int* p gehört zur Deklaration eines Zeigers. Das Sternchen in *p ist ein Zugriff auf dessen Ziel. In 3 * 4 ist es Multiplikation. Ebenso bedeutet & in int& r Referenz und in &zahl Adressbildung. Entscheidend ist der Kontext.

Schreibe vorzugsweise eine Variable pro Definition. Bei int* a, b; ist nur a ein Zeiger; b ist ein int. Eine klarere Schreibweise vermeidet diese Überraschung.

const und Zeiger#

Schreibweise

Was bleibt unveränderbar?

const int* p

Das Ziel darf nicht über p verändert werden

int* const p

Der Zeigerwert darf nach Initialisierung nicht geändert werden

const int* const p

Beides gilt gleichzeitig

Ein Zeiger auf konstante Daten bedeutet nicht zwingend, dass das Ziel auch über alle anderen Wege konstant ist. Er begrenzt zunächst diesen Zugriff.

Wann brauchst du überhaupt einen Zeiger?#

Situation

Passender Ausgangspunkt

Eine eigene kleine Zahl speichern

Ein Wert, etwa int

Ein vorhandenes Objekt unter einem zweiten Namen verwenden

Eine Referenz, etwa int&

Ein vorhandenes Ziel ist optional oder kann wechseln

Ein Zeiger, etwa int*, zunächst nullptr

Ein dynamisch erzeugtes Objekt besitzen

Meist std::unique_ptr, siehe Smart Pointer

Mehrere Werte speichern

Meist std::vector, siehe vector

Ein roher Zeiger beschreibt von sich aus weder eine Länge noch Besitz. Bei einer Funktion mit int* musst du deshalb zusätzlich wissen, ob null erlaubt ist, wie lange das Ziel lebt und ob die Funktion es verändern darf.

Übung#

Was ist der Unterschied zwischen p = nullptr; und *p = 0;?

Lösung

Die erste Anweisung entfernt die Zielzuordnung des Zeigerwerts. Die zweite schreibt null in ein gültiges Zielobjekt und darf nur mit einem passenden lebenden Ziel ausgeführt werden.

Ausgabe vorhersagen#

Welcher Wert wird jeweils ausgegeben? Rechne erst auf Papier, bevor du das vollständige Programm ausführst.

C++
1#include <iostream>2 3int main() {4    int a{4};5    int b{9};6    int* p{&a};7    *p += 2;8    p = &b;9    *p = a;10    std::cout << a << ' ' << b << '\n';11}
Lösung: Werte und Ziel getrennt verfolgen

Die Ausgabe ist 6 6. Zunächst wird a über *p von 4 auf 6 erhöht. p = &b ändert danach nur das Ziel. *p = a kopiert den Wert 6 in b. Es kopiert weder eine Adresse nach b noch bindet es p wieder an a.

Selbstprüfung#

  • Ich kann p, &p und *p unterscheiden: Zieladresse, Adresse der Zeigervariablen und Zugriff auf das Ziel.

  • Ich prüfe nicht nur auf nullptr, sondern kenne auch die Lebensdauer des Zielobjekts.

  • Ich kann erklären, weshalb ein gewöhnlicher Zeiger nicht automatisch delete ausführen darf.

Weiterlernen#

Zurück: Referenzen: ein zweiter Name für ein Objekt · Kursübersicht · Weiter: Zeigerfallen: Grenzen, C-Strings und dangling

Mitwirkende 1

Menschen, deren Änderungen in dieser Seite veröffentlicht wurden.

Kommentare 0

Kommentare sind für diese Seite deaktiviert.