Pointer & Referenzen

Eine kurze Einführung in Pointer und Referenzen. Pointer sind, vereinfacht gesagt, Variablen die statt Werten Speicheradressen enthalten. Mittels einer Dereferenzierung lässt sich auf den Inhalt dieser Speicheradresse zugreifen. Pointer sind auch als Zeiger bekannt. Eine Referenz ist ein direkter Verweis auf eine andere Variable, jede Änderung an der Referenz erfolgt auch an der Variablen, auf die sie referenziert. Während ein Pointer nicht initialisiert werden muss, muss eine Referenz immer auf eine Variable zeigen. Was jedoch nicht heißen muss, das das Objekt auf das eine Referenz zeigt, noch gültig ist. Bei Referenzen ist immer darauf zu achten, das die Referenzierte Variable länger lebt, als die Referenz selber!
Ein Pointer der nicht mit der Adresse einer Variablen initialisiert wird, sollte auf 0 gesetzt werden. Ein Pointer der den Wert 0 hat, bzw. auf die 'Adresse' 0 zeigt, gilt als ungültig bzw. nicht initialisiert. So lässt sich die Gültigkeit eines Pointer überprüfen, da man ihn auf 0 testen kann.
Der Vorteil von Pointern und Referenzen ist, das man sie einfacher kopieren kann, statt den Inhalt einer ganzen Variablen oder Objektes zu kopieren, wird nur die Referenz bzw. der Pointer kopiert.
Etwas Code zu Pointern & Referenzen:
int a =1;//(1)
int* pi = &a;//(2)
int& ri = a;//(3)
std::cout << "Die Adresse von a ist " << pi << std::endl;//(4)
std::cout << "Der Wert von a ist " << *pi << std::endl;//(5)
*pi = 12;//(6)
std::cout << "a:" << a << std::endl;
ri = *pi *3;//(7)
std::cout <<  "a:" << a << std::endl;


  1. Die Variable int a; ist in unserem Beispiel das Versuchsobjekt, nur sie hält den Wert als Integer.
  2. Ein Pointer pi vom Typ int wird angelegt. Er wird mit der Adresse von a initialisiert. Hierfür wird der Adressoperator & verwendet.
  3. Eine Referenz ri vom Typ int wird angelegt. Auch sie wird mit a initialisiert, ist nun also ein Alias für a.
  4. Die Speicheradresse von a ist der Wert den der Pointer enthält.
  5. Mit dem Dereferenzierungsoperator * lässt sich auf den Inhalt der im Pointer gespeicherten Adresse zugreifen.
  6. Über die Dereferenzierung von pi wird auf den Inhalt von a zugegriffen, hier wird er auf 12 gesetzt.
  7. Der Wert von a wird mit 3 multipliziert, und an die Referenz pi zu gewiesen, welche den Wert wiederum in a abspeichert.
 
Man könnte in dem Beispiel auch ri und *pi mit a ersetzen, und hätte das selbe Ergebnis. Für den Anfänger wirken daher Pointer oder Referenzen etwas obskur, da man mit ihnen etwas machen kann, was man mit der Variablen ja auch  hätte tun können.
Es sei noch kurz erwähnt, das man auch auf einen Pointer einen Pointer anlegen kann:

int** ppi = &pi;

Die Deklaration eines Pointer auf einen anderen Pointer folgt der simplen Regel, das es ein * mehr sein muss, als der Pointer auf den er zeigt. Bei einer Referenz ist dies nicht möglich, es gibt keine Referenz auf eine Referenz.

Pointer, Referenzen und const

Ein weiteres wichtiges Thema bei Pointern und Referenzen, ist wie man sie const macht. In C++ gibt es dafür 3 verschiedene Möglichkeiten:
int value = 42;
const int * cpi = &
value;//(1)
int const * pci = &
value;//(2)
const int *const cpci = &
value;//(3)
// Das gleiche als Referenz
const int &
cri = value;//(1)
int const &
rci = value;//(2)
const int &
const crci = value;// Fehler, das geht nicht

1 und 2 deklarieren jeweils einen konstanten Pointer/Referenz auf die Variable value. Damit kann man value nicht über die Pointer/Referenzen ändern. In C++ bezieht sich const immer auf das was links, also nachfolgend danach steht.
Bei 3 wird ein deshalb ein Zeiger auf ein konstante Variable deklariert. Dies geht bei einer Referenz nicht, da sie schon intern als Konstant gilt, eine Referenz zeigt immer auf das selbe Objekt.

Referenzen auf Pointer

Man kann auch eine Referenz auf einen Pointer anlegen, dies hat die Syntax Datentyp*&, und verhält sich nun im Code wie der eigentliche Pointer. Wie jede andere Referenz muss auch die Referenz auf einen Pointer bei ihrer Deklaration initialisiert werden:
// int* pi;
int*&
rpi = pi;//(1)
rpi = arr;
for(int i =0; i <<
10; ++i,++pi)//(2)
{
    if(i%2 ==0)//(3)
        std::cout <<
pi << " : " << *pi << std::endl;
    else
        std::cout <<
rpi << " : " << *rpi << std::endl;//(4)
}


  1. Eine Referenz auf einen int Pointer wird deklariert, und mit dem Pointer pi initialisiert.
  2. Die selbe Zählschleife wie aus dem letzten Beispiel
  3. Durch den % Operator (Modulo) wird erreicht, das wenn i gerade ist, der Wert über pi, sonst über die Referenz auf pi ausgegeben wird.
  4. Die Ausgabe erfolgt wie über pi, nur über die Referenz als Alias.