Pointer/ Zeiger ?!



  • zeiger braucht man überall! aber heutzutage sind sie (in C++) von Referenzen abzulösen



  • Original erstellt von <doch>:
    zeiger braucht man überall! aber heutzutage sind sie (in C++) von Referenzen abzulösen

    stimmt wer braucht schon dynamischen speicher 🙂



  • also ein zeiger (ich red deutsch):
    in einer "normalen" variable (int, double, char, ...) speicherst du einen Wert.
    in einem Zeiger kannst du genauso einen Wert speichern, nur sollte dieser Wert eine Adresse im Speicher sein. Über den Zeiger kannst du dann den Inhalt des Speichers, auf den diese Speicheradresse verweist, ändern

    Beispiele (kompilierbar)
    [cpp]
    #include <iostream>
    using namespace std;

    int main () {
    {
    int x = 10; //Erzeuge eine Variable im Stapelspeicher (Stack) des Programms
    //vom Typ int, und weise ihr den Wert 10 zu
    int *zeiger;//Erzeuge einen Zeiger, der nur Adressen von ints beinhalten kann
    zeiger = &x;//&x - nimm die Speicheradresse von x und weise sie dem Zeiger zu
    //Man sagt: "zeiger" zeigt auf x

    cout << x << endl;
    cout << *zeiger << endl; //Mit dem Dereferenzierungsoperator * greifst du
    //auf den Inhalt der Speicheradresse, die im Zeiger
    //gespeichert ist zu

    (*zeiger)++; //Inkrementiere den Wert, auf den der Zeiger zeigt um 1
    cout << x << endl; //x ist inkrementiert worden
    }
    //2. Beispiel:
    {
    int x = 10, y = 20; //Zwei neue ints auf dem Stack erzeugen
    int * zeiger; //Erklärung: siehe oben
    int * * doppel_zeiger;//Erzeuge einen Zeiger, der nur auf Zeiger zeigen kann,
    //die auf ints zeigen

    zeiger = &x; //zeiger zeigt auf x
    doppel_zeiger = &zeiger; //doppel_zeiger zeigt auf zeiger

    (*doppel_zeiger) = &y; //lässt den zeiger, auf den doppelzeiger zeigt, auf y zeigen

    cout << *zeiger << endl; //beweis: zeiger zeigt auf y

    cout << **doppel_zeiger << endl; //Doppelte Zeiger müssen auch doppelt
    //dereferenziert werden, um den richtigen
    //wert zu erhalten

    }
    //3. Beispiel - Zusatz - 3/4/5 fach Zeiger (hoffentlich vertippe ich mich nicht)
    {
    char x = 'x', y = 'y', z = 'z'; //Natürlich gibt es nicht nur int-Variablen
    char *zeiger1 = &x, *zeiger2 = &y; //Merke die Syntax:
    /*
    int *i, y; - i ist vom Typ Zeiger auf int, y vom typ int!
    int i, *y; - i ist vom Typ int, y vom Typ Zeiger auf int!
    int *i, *y; - i und y sind vom Typ Zeiger auf int
    */
    char * * zeiger_auf_zeiger = &zeiger1;
    **zeiger_auf_zeiger = 'a';
    cout << x << endl; //Sollte 'a' ausgeben

    zeiger_auf_zeiger = &zeiger2; //Merken für unten
    **zeiger_auf_zeiger = 'b';
    cout << y << endl; //Sollte 'b' ausgeben

    *zeiger_auf_zeiger = &z;
    cout << *zeiger2 << endl; //Sollte 'z' ausgeben
    **zeiger_auf_zeiger = 'c';
    cout << z << endl; //Sollte 'c' ausgeben

    char *** dreifach_zeiger = &zeiger_auf_zeiger;
    ***dreifach_zeiger = 'z';
    cout << z << endl; //Sollte 'z' ausgeben
    **dreifach_zeiger = zeiger1;
    ***dreifach_zeiger = 'n';
    cout << x << endl; //Sollte 'n' ausgeben

    So, 4 und 5fach Zeiger darf wer anders machen!
    }
    //4. Beispiel - Die häufigere Verwendung von Zeigern - dynamischer Speicher
    {
    int *x = new int; //operator new nimmt sich Speicher vom Heap (Freestore, Freispeicher, etc)
    *x = 10;
    //und nicht vergessen:
    delete x; //Um den Speicher wieder freizugeben
    }
    //5. Beispiel - Array auf dem Heap
    {
    int *x = new int[10]; //Hole Speicher für 10 ints
    //x (und im allgemeinen Zeiger überhaupt) kann wie ein Array verwendet werden
    x[0] = 1;
    x[1] = 2;
    ...
    x[9] = 10; //10. Element ist unter x[9]

    //Freigeben mit delete []
    delete [] x; //wenn du mit new [] speicher besorgst, mit delete [] freigeben!
    }
    //6. Beispiel - Zeigerarithmetik - Einführung
    {
    /*Man kann auch noch anders auf array-elemente zugreifen: du inkrementierst den zeiger:*/
    int *x = new int[10];
    *x = 1; //hat denselben effekt wie x[0] = 1;
    ++x; //x zeigt jetzt einfach auf den nächsten int im Speicher, also auf &x[1]
    *x = 2; //etc.
    --x; //wieder zurück, den zum freigeben muss x auf das erste element zeigen
    delete x;
    }
    //7. Beispiel - Arrays und Zeigerarithmetik - Vertiefung
    {
    int x[10]; //Ein Array von 10 ints herstellen
    int *y = &x[0]; //y zeigt auf das 1. Element von x
    *y = 10;
    cout << x[0] << endl; //Ausgabe: '10'
    ++y; //y zeigt auf das 2. Element von x
    *y = 20;
    cout << x[1] << endl; //Ausgabe: '20'

    int *z = x; //Wie oben schon besprochen: eine Array-Variable (x) verhält sich
    //wie ein zeiger (z)
    z += 5; //Um 5 ints weiterspringen -> Zum 5.Element
    *z = 50;
    cout << x[4] << endl; //5.Element ausgeben: sollte sein '50'
    z -= 4; //Auf das 2. Element zurückspringen
    (*z)++;
    cout << x[1] << endl; //Ausgabe: 21

    /*
    Wie man sieht, kann man zu Zeigern also auch Werte addieren, und so quer
    durch den Speicher navigieren. Wenn ein Zeiger aber auf eine "falsche"
    Adresse zeigt (z.b. hinter die Array-Grenze) oder auf 0 Zeigt (int *x = 0)
    und man in dereferenziert, erzeugt dies "undefiniertes Verhalten".
    Der Computer könnte abstürzen, oder auch ein Vogonenraumschiff anrufen,
    und den Kommandanten an seine Mission, die Erde zu vernichten, erinnern,
    was er vergessen haben könnte, weil er gerade ein Gedicht vorträgt.
    Also: Zeiger zu dereferenzieren, die auf ungültige Adressen verweisen, ist
    *schlecht
    /

    //8. Beispiel - Zeigerarithmetik (3, oder shon 4?) - ptrdiff_t
    {
    //Man kann auch Zeiger (gleichen typs versteht sich)
    //voneinander abziehen. Das Ergebnis hat den Typ ptrdiff_t:

    int x[10];
    ptrdiff_t groesse = &x[9] - &x[0]; //groesse enthält nun den Wert, wieviele
    //ints zwischen x[0] und x[9] liegen.
    }
    //9. Beispiel - Dereferenzierung und der operator ->
    {
    //C++ ist eine schöne objektorientierte Sprache, in der man natürlich
    //auch Zeiger auf Objekte anlegen kann
    struct bar {
    int x, y;
    bar () : x(0), y(0) {}
    };
    bar b;
    b.x = 10; b.y = 20; //Alles klar, bis jetzt.

    //Und nun erzeugen wir ein bar im Heap:
    bar *b = new bar;
    //Wie greift man jetzt auf die elemente zu? richtig, zuerst dereferenzieren:
    (*b).x = 10; (*b).y = 20;
    //und wieder löschen
    delete bar;

    //Und das ganz nochmal als Beispiel mit nem Array
    bar *barr = new bar[20];
    barr[0].x = 10; barr[0].y = 20;
    /* etc. */
    delete [] barr;

    //Aber - weil sich jemand gedacht hat, er müsse zuviel tippen - ist die schreibweise
    //(*zeiger).member zwar erlaubt, aber durch die kürzere Form
    //zeiger->member ersetzbar:
    bar *foo = new bar;
    foo->x = 10; foo->y = 20;
    delete foo;
    }

    } //ende von main[/cpp]

    So. Ich nenne das eine kleine Einführung in die Welt der Zeiger. Das klingt jetzt ganz schön verwirrend, für einen Neuling, aber so ist's. Ich hoffe ich habe keine Fehler eingebaut (und wenn doch, bitte korrigiert mich).
    Was ich noch sagen will: Das ist mir jetzt mal so auf die schnelle eingefallen. Ich wette, es gibt noch mindestens 1000 Probleme, die noch auf dich zukommen, Neuling 😉 Aber dazu gibt es ja die Community 🙂
    Ich will auch nicht demotivierend klingen, aber das ist noch nicht mal ein kleiner Teil der Komplexität von C++. Viel Spaß noch beim lernen 😃
    Ich hoffe, ich habe ein kleines bisschen weitergeholfen.
    Könnte ja jemand in die FAQs stellen 🙂



  • Original erstellt von japro:
    [quote]Original erstellt von <doch>:
    [qb]zeiger braucht man überall! aber heutzutage sind sie (in C++) von Referenzen abzulösen

    stimmt wer braucht schon dynamischen speicher :)[/QB][/QUOTE]
    int &x = *new int; :p
    aber ich meinte damit Zeiger die in den Stack zeigen.



  • Danke! Habt mir sehr weitergeholfen 🙂



  • Dimah tu das bitte zu den FAQs dazu 🙂



  • Original erstellt von <Zusatz>:
    **
    > stimmt wer braucht schon dynamischen speicher 🙂
    int &x = *new int; :p
    **

    Eigentlich interessant.

    struct B {}; struct D1 : B {}; struct D2 : B {}; struct D3 : B {};
    
    void foo (int a, int b)
    {
        B& r = *(a == 12 ? new D1 : b == 42 ? new D2 : a == b ? new D3 : new B);
        /* ... */
        delete &r;
    }
    

    Ich bin mir nicht sicher, ob so etwas definiert ist, weil nach dem delete »zeigt« r auf kein gültiges Objekt mehr ...



  • ich würd sagen, nach dem delete ist r keine gültige Referenz mehr, und genauso dumm wie int &i = (int&) 01234;



  • das hatten wir aber doch schonmal, oder?



  • kannst du wenigstens mal antworten Dimah bitte ? 🙄



  • Dimah oder Hume: ihr könntet doch wenigstens den langen beispielcode zu Arrays und Pointer dazutun



  • sorry <oder > & <erklärer> war mit php bescheftig und habe mir die threads nicht duchgelesen

    ich warte bist keiner mehr was zu sagen hat (wenn der thread auf seite 2 kommt) dann verschibe ich ihn



  • Na und, hat irgendjemand festgelegt, dass Referenzen während ihrer Existenz immer gültig sein müssen? Das benutzen von r würde UB auslösen, aber die bloße Existenz doch nicht.



  • es ist nur festgelegt, dass eine Referenz bei ihrer Initialisierung gültig sein muss! Was anderes hab ich jedenfalls noch nie gelesen!
    Also könnte doch

    int &m = *new int;
    /*
    ...
    */
    delete &m;
    
    m = 10;
    

    implizit undefiniert sein 😃
    aber erlaubt sollte es schon sein 😉

    wenn noch wer was zu sagen hat, sollte er das jetzt tun 🙂



  • Zeiger verwendet man außer um Dynamischen Speicher zu verwalten auch heutzutage noch häufig als iterator.



  • @<erklärer>
    habe dein posting in den Arrays und Pointer thread reingeedit


Anmelden zum Antworten