[C++] Verwirrung bei Zeigern, Referenzen, etc

Irgendjemand_1

Erfahrenes Mitglied
n'Abend ;)
Mein C++-Buch verwirrt mich irgendwie im Zusammenhang mit Zeigern und Referenzen.
Könnte mir vielleicht nochmal kurz einer schreiben, wie das genau war?
Referenzen (&) verweisen auf die Adresse des Objektes, also sind praktisch das Objekt selbst, oder? Und als Funktionsparameter eingesetzt sind sich keine lokalen Variablen, sondern verändern auch die Echten, also nicht nur die Kopie?
Und wie war das mit Zeigern? Wie war das nochmal mit dem Deferenzieren und warum muss man zB const char* var = "text"; anstatt const char var = "text"; schreiben? Also * (Zeiger, oder?)

Also das ist halt etwas, was mich total verwirrt hat, da will ich einfach nochmal Klarheit schaffen, ich denke, wenn mir schon das nicht klar ist, kann ich auch gleich mit C++ aufhören.
Danke schonmal für eure Hilfe :)
 
Es stimmt, wie Du es gesagt hast, Zeiger und Referenzen sind völlig unterschiedliche Dinge und liegen doch von der Semantik so nah beieinander. Um es kurz zu fassen ist eine Referenz lediglich ein anderer Name für ein Objekt, z.B.:
Code:
int& r = x;
jetzt kann man sowohl mit r, als auch mit x arbeiten. Daher ist eine Referenz vom Typ her auch kein(!) Zeiger, sondern hat den gleichen Typ wie das Objekt, für das sie steht (vgl. Josuttis, »Objektorientiertes Programmieren in C++«, Seite 190f.)
Verwendet man Referenzen in Funktionsaufrufen als Parameter, so arbeitet man tatsächlich mit einem call-by-reference Mechanismus. Eine swpa Funktion würde dann so aussehen:
Code:
void swap(int& a, int& b) {
  int tmp;
  tmp = a;
  a = b;
  b = tmp;
}
Aufgerufen wird die Funktion dann einfach mit den Variablen einer anderen Funktion:
Code:
[...]
int x = 13;
int y = 42;
[...]
swap(x, y);
Josuttis nennt die Verwirklichung dieser Sprachtechnik entweder Traum oder Alptraum, denn Referenzen können das Leben einfacher machen: Man müsste die swap Funktion mit Zeigern implementieren und den Aufruf mit swap(&x, &y); durchführen. Der Nachteil ist, dass dem »Leser« nicht mehr sofort klar sein muss, ob die Werte, die an eine Funktion übergeben werden, nun tatsächlich geändert werden, oder ob nur Kopien von diesen Werten verändert werden -- man muss also die Funktionsdeklaration beachten.

Zeiger sind eigentlich total einfach zu verstehen, man muss sich nur daran gewöhnen. Es gibt drei verschiedene Operatoren, die da greifen, einmal der Sternoperator, der einmal bei der Deklaration eingesetzt wird (int *a -> a ist ein Zeiger auf einen Integer) und dann auch als Dereferenzierung eines Zeigers (die Verwendung von *a gibt den int-Wert zurück). Außerdem gibt es noch den &-Operator, auch Adressoperator genannt. Mit diesem kann man die Adresse eines Objekts an einen Zeiger übergeben. Folgendes Beispiel:
Code:
#include <stdio.h>

int main()
{
        int x = 4711;
        int y = 13;
        int *xp = &x;
        int *yp = &y;
        *xp = 42;

        printf("x: %d, y: %d\n", *xp, *yp);

        return 0;
}
Wichtig ist bei Zeigern immer, dass Speicher bereitstehen muss, sonst kommt es zu undefiniertem Verhalten, i.d.R. Speicherzugriffsfehlern. Folgendes würde also nicht(!) funktionieren:
Code:
#include <stdio.h>

int main()
{
        int *x;
        int *y;
        *x = 42;
        *y = 13;

        printf("x: %d, y: %d\n", *x, *y);

        return 0;
}
weil einfach kein Speicher für *x und *y bereitgestellt wurde.

Zusätzlich gibt es noch den Pfeiloperator (->) und den Punkt-Operator (.), die bei Strukturen zum Einsatz kommen. Folgendes Beispiel:
Code:
#include <stdio.h>

struct test {
        int a;
};

int main()
{
        struct test atest;
        struct test *btest = &atest;

        atest.a = 42;
        printf("btest->a: %d\n", btest->a);

        return 0;
}
Wir legen eine Struktur atest an (allozieren dafür automatisch Speicher) und legen einen Zeiger *btest darauf an. Wenn wir mit einer Struktur direkt arbeiten, kann man mittels des Punktoperators direkt auf die Werte zugreifen (atest.a). Haben wir nur einen Zeiger darauf bekommen, könnte man schreiben: (*btest).a also zuerst den Strukturzeiger dereferenzieren und dann mit dem Punktoperator darauf zugreifen. Diese umständliche Schreibweise ist einem mit dem Pfeiloperator abgenommen worden.

Jetzt gibt es noch schließlich zu beachten, dass sich ein Array verhält wie ein Zeiger auf sein erstes Element. Und der Grund, warum man const char* var = "text"; so schreiben muss liegt darin begründet, dass man unter C keinen fundamentalen String-Typ hat (unter C++ bietet sich std::string an), sondern mit einem Array aus Zeichen arbeiten muss. Und da sich Arrays und Zeiger so verwandt sind, kann man einen konstanten String eben so definieren.
 
Danke, für deinen recht ausführlichen Post! :)
Ich denke, jetzt ist mir das ganze klar geworden. Vielen dank!

An Referenzen habe ich mich ja schon so einigermaßen gewöhnt, nur bei Zeigern wirds wohl noch etwas dauern ;)
 

Neue Beiträge

Zurück