Die Sache mit den Zeigern



  • Hallo zusammen,
    ich habe nun schon viel in C++ gelernt, doch bin ich natürlich noch weit davon entfernt richtig professionel zu arbeiten. Dennoch arbeite ich daran. Ein Thema wurde leider immer in den Büchern mit denen ich bis jetzt gearbeitet habe vernachlässigt. Dabei geht es um die Zeiger beziehungsweise Pointer. Nun bin ich gerade dabei mir "diese beizubringen". Dazu habe ich gleich schon einige Fragen.

    int *Pointer
    

    Mit dieser Deklaration vereinbare ich eine Variable namens Pointer, wobei diese keinen eindeutigen Speicherplatz hat, sondern nur eine Adresse? Habe ich das richtig verstanden? Ist jetzt "Pointer" der Zeiger oder was wird hier als Zeiger bezeichnet?

    int *Poiner
    *Pointer=3;
    

    Ich kann einem Zeiger nicht einfach so einen Wert zuweisen!? Hier erscheint nämlich eine Zugriffsverletzung! Kommt diese Fehlermeldung, weil dem Zeiger Pointer noch gar keine Adresse zugewisen worden ist? Nachdem man nämlich die folgenden Zeilen geschrieben hat, kommt jedoch keine Fehlermeldung mehr:

    int *Pointer;
    int pointer=3;
    Pointer=&pointer;
    *Pointer=3; //Hier macht eine Zuweisung keine Probleme
    

    Spricht man denn überhaupt hier auch von einer Wertzuweisung? Ich dachte, dass man auf Pointer immer mit dem Sternchen zugreifen muss, also so: *Pointer. Warum ist dies in der dritten Zeile nicht der Fall? Dort schreibe ich einfach:
    Pointer=&pointer;

    Die letzte Frage bezieht sich nochmals auf die Wertzuweisung:

    int Alter;
     int *pAlter;
     pAlter=&Alter;
     *pAlter=10;
     cout << Alter;
    

    Hier wird 10 ausgegeben, obwohl ich der Variablen "Alter" ja nicht direkt einen Wert zugewiesen hat. Ist so, weil ich in den Speicherplatz von Alter 10 reinschreibe (mit der Zeile *pAlter=10)?? pAlter habe ich ja die Adresse von Alter zugewiesen.

    So, ich hoffe, dass ihr mir helft. Ich bin halt erst gerade in dieses Thema eingestiegen und es ist dabei ja schon irgendwie
    eine neue Denkweise.

    Vielen Dank
    lg, freakC++



  • C/C++ Code:
    int *Poiner
    *Pointer=3;

    Ich kann einem Zeiger nicht einfach so einen Wert zuweisen!? Hier erscheint nämlich eine Zugriffsverletzung! Kommt diese Fehlermeldung, weil dem Zeiger Pointer noch gar keine Adresse zugewisen worden ist? Nachdem man nämlich die folgenden Zeilen geschrieben hat, kommt jedoch keine Fehlermeldung mehr:

    Genau

    C/C++ Code:
    int *Pointer;
    int pointer=3;
    Pointer=&pointer;
    *Pointer=3; //Hier macht eine Zuweisung keine Probleme

    Spricht man denn überhaupt hier auch von einer Wertzuweisung? Ich dachte, dass man auf Pointer immer mit dem Sternchen zugreifen muss, also so: *Pointer. Warum ist dies in der dritten Zeile nicht der Fall? Dort schreibe ich einfach:
    Pointer=&pointer;

    Weil du an dieser Stelle, keinen Wert zuweist, sondern die dir die Adresse von "pointer" holst und sie in deinem Zeiger speicherst.

    Hier wird 10 ausgegeben, obwohl ich der Variablen "Alter" ja nicht direkt einen Wert zugewiesen hat. Ist so, weil ich in den Speicherplatz von Alter 10 reinschreibe (mit der Zeile *pAlter=10)?? pAlter habe ich ja die Adresse von Alter zugewiesen.

    Auch richtig.



  • Mit dieser Deklaration vereinbare ich eine Variable namens Pointer, wobei diese keinen eindeutigen Speicherplatz hat, sondern nur eine Adresse?

    Nicht ganz richtig. Du meinst es wahrscheinlich richtig, aber ein Zeiger ist auch nichts anderes, als ein Speicherbereich, indem halt eine Adresse, anstatt ein Wert steht.

    int* p = 0;
    

    Das hier ist dann z.B die representation im Speicher:

    0x45125689[ 0 ]
                ^
                Wert von p, also die Adresse, auf den er zeigt, hier nicht zugewiesen, also 0
    ^
    Addresse von p selbst
    
    int zahl = 5;
    p = &zahl;
    

    zahl:

    0x34481289 [ 5 ]
                  ^
                  Wert von zahl
     ^
     Addresse von zahl
    

    Nach der Zuweisung sieht p dann so aus:

    0x45125689[ 0x34481289  ]
                ^
                Wert von p, also die Addresse von zahl
    ^
    Addresse von p selbst
    

    Wie du siehst hat p selbst auch einen Speicherort und somit eine Addresse, die man auslesen kann oder auch selbst ein einem Zeiger speichern:

    int*    *                  pp      = &p;
    [ Typ ] [ ist ein Zeiger ] [ Name ]
     (1)           (2)           (3)
    

    Hier wird in pp einfach die Addresse von p gespeichert. (bei uns also der Wert 0x45125689). Hier siehst du, dass pp (3) ein Zeiger (2) auf einen gewissen Typen ist (1). Das ist immer dasselbe, man sollte sich einfach nicht von mehreren Sternen verwirren lassen.

    Wie du siehst sind Zeiger eigentlich nichts anderes, als eine ganz normale Variable, bei der du einfach per *-Operator auf den Speicherbereich, den er beinhaltet zugreifen kannst.
    Man könnte einen Zeiger auch mit einem integer und casting emulieren und das würde wahrscheinlich sogar den gleichen generierten Code ergeben, allerdings ist es einfacher und verständlicher mit Zeigern zu arbeiten. Allerdings sollte man für das Verständnis dieses Faktum im Hinterkopf behalten.

    Wenn du Zeiger jetzt mit Referenzen vergleichst, wirst du merken, dass Referenzen keinen eigenen Speicher haben (müssen), respektive der &-Operator (um die Addresse zu bekommen) gibt dir die gleiche Addresse, wie die referenzierte Variable.

    Ich hoffe, ich habe das einigermassen verständlich rübergebracht. Bei Fragen einfach nachfragen. 😉



  • Hallo zusammen,
    vielen Dank für eure tollen Erklärungen. Sie haben es sehr gut dargestellt und ich kann mir alles nun schon viel besser vorstellen.
    Doch bleiben noch eine Menge Fragen bestehehen, die euch auch gerne stellen würde. Hier sind ein paar:

    Bei dem folgendem Quelltext gebe ich doch die Adresse der Variablen aus, auf die der Zeiger zeigt:

    #include "iostream.h"
    
    int main ()
    {
     float fQuartalumsaetze[4]={2,3,4,5};
     float *pZeiger;
     pZeiger=&fQuartalumsaetze[0];
     int i;
    
     for (i=0;i<=3;i++)
     {
      cout << "Wert:           " << *(pZeiger+i) << endl;
      cout << "an Adresse      " << (pZeiger+i) << endl;
     }
    return 0;
    }
    

    Wie kann man denn nun die Adresse des Zeigers ausgeben?

    Ich habe mir soeben nochmals den new-Operator angeschaut.Folgendes kleines Programm diente mir zur Verdeutlichung:

    #include <iostream.h>
    int main ()
    {
     int *pZahl = new int;
     cin >> *pZahl;
     cout << "Zahl     :" << *pZahl << endl;
     cout << "Adresse   : " << &pZahl;  //Wessen Adresse wird hier ausgegeben???
    }
    

    Nun verstehe ich leider noch nicht ganz die Bedeutung dieses Operators. Ich deklariere wiedermal einen Zeiger,
    der dann die Adresse einer anderen Variablen beinhalten kann. Nun soll dieser Variablen ein Wert zugewiesen werden???? Wie ich
    gelernt habe, kann man einem Zeiger nicht direkt einen Wert zuweisen, sondern entweder mittels einer anderen Variablen und dem Ampersand
    oder jetzt mit dieser Methode.
    Was macht das new genau hier und weshalb kann ich auch so *pZahl einen Wert zuweisen (wird überhaupt *pZahl ein Wert
    zugewiesen oder vielleicht einer "unsichtbaren" Variablen)?? Wenn wirklich diese "unsichtbare" Variable dann nur über den
    Zeiger anzusprechen wäre, würde dies doch bedeuten, dass nachdem der Zeiger auf eine andere Variable zeigt die "unsichtbare"
    Variable verloren wäre???

    Ich hoffe, dass ihr mir folgen könnt
    Vielen Dank
    lg, freakC++



  • freakC++ schrieb:

    Wie kann man denn nun die Adresse des Zeigers ausgeben?

    Du meinst die Adresse, an der der Zeiger gespeichert ist? Ebenfalls mit dem Adressoperator.

    int* Ptr = &Var;
    std::cout << &Ptr << std::endl;
    

    freakC++ schrieb:

    Nun verstehe ich leider noch nicht ganz die Bedeutung dieses Operators.

    Der new -Operator macht nichts anderes, als Speicherbereich dynamisch anzufordern und ein Objekt an dieser Stelle zu konstruieren (bei Non-PODs). Er gibt dir dabei einen Zeiger auf den Anfang dieses Speicherbereiches zurück. Das heisst, wenn du Folgendes hast, zeigt Ptr auf einen int , der aber nicht in einer Variable steht:

    int* Ptr = new int(5);
    

    Du kannst diesen int also nur über den Zeiger Ptr ansprechen. Sobald du Ptr auf etwas anderes zeigen lässt, ist der int unwiederbringlich verloren. Du hast dann ein Memory Leak, das heisst Speicher, der reserviert bleibt, aber nicht mehr genutzt werden kann.

    Deshalb immer schön delete einsetzen. Für jedes new ein delete . Und wenn du dann noch den Array-Speicheroperator new[] einsetzt, brauchst du ein entsprechendes delete[] .

    delete Ptr;
    


  • Hallo,
    vielen Dank für die Antwort. Wenn ich den Operator new in dem oben gezeigten Beipiel weglasse und mein Semikolon hinter der Zeigerdeklaration setzte, dann ernte ich einen Fehler. Warum ist dies so?? Ich musste doch auch bei den anderen Beispielen mit Zeigern diesen nichtOperator verwenden?

    #include <iostream.h>
    int main ()
    {
     int *pZahl; //Warum muss ich hier den new Operator nutzen?
     cin >> *pZahl;
     cout << "Zahl     :" << *pZahl << endl;
     cout << "Adresse   : " << &pZahl;  //Wessen Adresse wird hier ausgegeben???
    }
    

    Ach ja, was ich leider nicht verstehe ,doch sehe ich dies sehr oft (wie in der letzten Antwort von Nexus), ist folgender Ausdruck:

    int* Variable;
    

    Dies ist doch auch ein Pointer, doch wird hier auch eine Variable vereinbart, die die Adresse einer anderen beinhaltet? Warum schreibe ich dann nicht

    int *Variable;
    

    Was ist der Unterschied. Ich habe leider nichts in Google gefunden (und auch nichts in meinem Buch).
    Vielen Dank
    lg, freakC++



  • new gibt dir einen Zeiger auf einen Speicherbereich zurück.

    Du hast also grundlegend folgenden Möglichkeiten:

    int* sp;
    int zahl = 2;
    sp = &zahl;
    //oder
    int* dp;
    dp = new int;
    delete dp;
    

    Das sind zwei grundlegend andere Verwendungszwecke. Beim ersten Beispiel benutzt du den Zeiger nur als eine andere Weise, um auf zahl zuzugreifen. Beim zweiten Beispiel (mit new) brauchst du den Zeiger, damit du immer einen Verweis auf den angefordereten Speicherbereich brauchst. Den brauchst du immer, da du den Speicher, den du mit new angefordert hast auch wieder mit delete freigeben musst.

    Es macht keinen Unterschied, wo das Sternchen steht. Das ist reine Geschmackssache. Ich persönlich mag es anschliessend an den Typen, da der für mich zu dem Typen gehört. Es gibt aber auch Leute, die den Stern beim Namen haben wollen. Macht im Code aber überhaupt keinen Unterschied.



  • freakC++ schrieb:

    vielen Dank für die Antwort. Wenn ich den Operator new in dem oben gezeigten Beipiel weglasse und mein Semikolon hinter der Zeigerdeklaration setzte, dann ernte ich einen Fehler. Warum ist dies so??

    Du hast dem Zeiger keinen Speicher zugewiesen. Der Zeiger ist nicht initialisiert, du darfst ihn also keinesfalls dereferenzieren, da er irgendwo hinzeigen könnte. Da du es doch tust, kommt es zu einer Zugriffsverletzung. Das muss aber nicht sein, es kann gerade so gut nichts passieren, ein kleines schwarzes Loch entstehen oder Daten überschrieben werden. Das Verhalten ist undefiniert.


Anmelden zum Antworten