Historie:
Zeiger sind eines der grundlegenden Werkzeuge in C++ seit der Einführung der Sprache, fundamentales Mittel zur Speicherverwaltung und dynamischen Strukturen sowie zur Interaktion mit niedrigstufigen Systembibliotheken. Referenzen wurden später (mit der Einführung von C++) hinzugefügt, um die Syntax zu vereinfachen und die Sicherheit des Codes zu erhöhen.
Problem:
Fehler im Umgang mit Speicher sind eines der häufigsten Probleme in C++, insbesondere bei falscher Verwendung von Zeigern (dangling pointers, memory leaks). Dabei versteht ein Anfänger manchmal nicht, wann eine Referenz und wann ein Zeiger verwendet werden sollte, oder glaubt irrtümlich, dass sie vollständig austauschbar sind.
Lösung:
Ein Zeiger (T*) ist eine Variable, die die Adresse eines Objekts speichert, er kann nullptr sein, unterstützt Arithmetik, erlaubt dynamische Zuweisung und Freigabe sowie kann ändern, auf was er verweist.
Eine Referenz (T&) ist ein Alias für ein anderes bestehendes Objekt. Referenzen müssen bei der Erstellung initialisiert werden, können nicht „leer“ sein (referencing null), unterstützen keine Arithmetik, sind aber bequemer und sicherer (zum Beispiel beim Übergeben an Funktionen).
Beispielcode:
void increment_pointer(int* p) { if(p) ++(*p); } void increment_reference(int& r) { ++r; } int main() { int a = 5; increment_pointer(&a); increment_reference(a); }
Wesentliche Merkmale:
Kann man eine Referenz nach ihrer Initialisierung ändern, sodass sie auf ein anderes Objekt zeigt?
Nein. Eine Referenz in C++ ist ein konstanter Alias. Nach der Initialisierung wird die Referenz immer auf dasselbe Objekt zeigen, im Gegensatz zu einem Zeiger, der umgeleitet werden kann.
Beispielcode:
int a = 1; int b = 2; int& ref = a; // ref = b - weist b zu, aber ref zeigt weiterhin auf a
Kann man eine „null“-Referenz oder eine Referenz auf nullptr erstellen?
Nein, der Standard erlaubt keine Referenzen auf nicht existente Objekte. Dies führt zu undefiniertem Verhalten.
Beispiel:
int* p = nullptr; // int& r = *p; - UB
Was unterscheidet int const ptr von const int ptr?**
int* const ptr ist ein konstanter Zeiger auf int (der Zeiger kann nicht umgeleitet werden, aber der Wert kann geändert werden). const int* ptr ist ein Zeiger auf ein konstantes int (der Inhalt, auf den er zeigt, kann nicht verändert werden, aber der Zeiger kann umgeleitet werden).
Beispielcode:
int a = 1, b = 2; const int* ptr1 = &a; // *ptr1 kann nicht geändert werden, ptr1 = &b - erlaubt int* const ptr2 = &a; // *ptr2 kann geändert werden, ptr2 = &b - nicht erlaubt
Ein junger Entwickler verwendete Zeiger zur Übergabe von Parametern, überprüfte sie nicht auf nullptr und führte bei Fehlern zu einem Absturz.
Vorteile:
Nachteile:
Ein erfahrener Entwickler verwendete int&, wenn die Übergabe garantiert war, und int*, wenn nullptr möglich war, wobei er den Zeiger immer auf null überprüfte.
Vorteile:
Nachteile: