Pointer Verständniss

  • C++/CLI

Es gibt 8 Antworten in diesem Thema. Der letzte Beitrag () ist von Farliam.

    Pointer Verständniss

    Hallo zusammen,

    mein erster Post ^^

    Ich sitze gerade mal bei Seite 164 bei meinem Buch, und verstehe jetzt schon nimmer wirklich warum und wieso das so ist, wie es ist^^.

    Ich poste mal schnell einen kurzen Ausschnitt aus dem Buch :

    C-Quellcode

    1. int Punkte = 500;
    2. int *pPunkte = NULL;
    3. pPunkte = &Punkte;
    4. *pPunkte = 1500;


    So weit so klar, aber wenn mann sicher ist, dass die Variabel auf die man zeigt bereits vorhanden ist und einen Wert hat, warum benötigt man dann eine Null initialisierung?

    Nun gut, ich habe mir also das Beispiel durchgelesen und dann selber etwas zusammengeschrieben :

    C-Quellcode

    1. #include <iostream>
    2. struct member {
    3. int mCount=0;
    4. std::string mName;
    5. std::string mTelefon;
    6. void druckeInfo() {
    7. std::cout << "\nCounter:" << mCount << "\nName:" << mName << "\nTelefon:" << mTelefon << "\n";
    8. }
    9. };
    10. void changeMember(member* pMember) {
    11. //Pointer wird übergeben. Überprüfen auf 0
    12. if (pMember != NULL) {
    13. pMember->mCount = 1;
    14. pMember->mName = "Geaendert über Pointer";
    15. pMember->mTelefon = "08004141";
    16. }
    17. }
    18. int main()
    19. {
    20. std::cout << "Erstelle Member";
    21. member philipp;
    22. philipp.mCount = 0;
    23. std::cout << "\nBitte gebe einen Namen ein:";
    24. std::cin >> philipp.mName;
    25. std::cout << "\nBitte gebe eine Telefonnummer an:";
    26. std::cin >> philipp.mTelefon;
    27. philipp.druckeInfo();
    28. std::cout << "\nDie Adresse von Member lautet:" << &philipp << '\n';
    29. member* pPhilipp = &philipp;
    30. changeMember(pPhilipp);
    31. philipp.druckeInfo();
    32. }


    In dem Beispiel aus dem Buch wurde auf den Zeiger über ein * zugegriffen. Wenn ich dies aber in meinem Beispiel mache bekomme ich eine : Der Operand von "*" muss ein Zeiger sein. Meldung.
    Mache ich es aber ohne direkte *davor
    pPhilipp->mcount=1;
    funktioniert es ohne Probleme.

    Liegt es daran, dass in dem Beispiel einfach nur ein int Wert über den Pointer verändert wird, und in meinem Beispiel eine Structur? Ich verstehe dass nicht so ganz.
    Also mein Beispiel macht (glaube) ich was es soll. Wenn ich aber nach dem Buch gehe müsste ich ja eigentlich
    *​pMember->mCount = 1;
    schreiben?

    Farliam schrieb:

    warum benötigt man dann eine Null initialisierung?
    C und C++ weisen dem Pointer einen Speicherplatz zu, unabhängig davon, was da drinne steht.
    Su kannst z.B.

    C-Quellcode

    1. LPCTSTR str;
    2. printf(str);
    schreiben und bekommst alles bis zum zufälligen Auftreten 2er 0-Bytes auf einer geraden Adresse angezeigt.
    .NET löscht den Inhalt der angelegten Variablen, sodass da nix unvorhergesehenes passieren kann.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Das habe ich auch so weit verstanden. In der Adresse kann alles mögliche stehen, doch ich zeige ja dem Pointer auf welchen Bereich er gehen soll.
    Weswegen muss ich wenn ich von Anfang an weiß, das mein Speicherbereich existiert, und zugewiesen ist eine Null Zuweißung machen, nur um in der nächsten Zeile
    die bereits bekannte Adresse zuzuweisen? Wenn dies aber zur allgemeinen Sicherheit beträgt, werde ich mir dass merken.

    Nun bleibt nur noch folgendes stehen :

    C-Quellcode

    1. void test() {
    2. int x = 50;
    3. int* px = &x;
    4. *px = 50;
    5. }


    funktioniert wie im Beispiel.

    C-Quellcode

    1. void test() {
    2. member Ilse;
    3. member* pIlse = &Ilse;
    4. *pIlse->mCount = 1; //Falsch
    5. pIlse->mCount = 1; // Geht
    6. }


    Wie das Beispiel nur statt int eine Structur, funktioniert aber nur ohne das '*'. Warum ist das so.
    pIlse->mCount ist Equivalent zu (*pIlse).mCount. Das löst der Compiler für dich so auf, damit du nicht immer das Sternchen schreiben musst. Besonders Vorteilhaft bei tiefen Verschachtelungen.
    *pIlse->mCount ist also Praktisch doppelt gemoppelt und würde ja zu *((*pIlse).mCount) (glaub ich/nicht sicher) auflösen. Du versuchst also damit mCount als Adresse zu verwenden und auf den gezeigten Inhalt zu gehen.

    Vermutlich hast du schon gemerkt, dass das * bedeutet, nimm dies als Pointer und gehe auf den Inhalt auf den er Zeigt.

    C-Quellcode

    1. *pIlse->mCount
    nimmt er erst gar nicht an ^^ Aber mit den Klammern.

    Danke dass habe ich jetzt ungefähr verstanden. Die andere Schreibweise liegt dann wahrscheinlich daran, dass ich statt auf int, auf eine ganze Struktur zeige, die selber nochmal mehre Werte hat.
    Die IDE hat mir praktischer Weiße auch das -> eingefügt. Sonst hätte das alles erst gar nicht funktioniert.

    Bin glaube etwas weit vorgesprungen. Im Buch wurden bisher nur Pointer mit int verwendet. Hab mir gedacht, dass das selbe ja auch mit Klassen und Structuren funktionieren müsste, und dann einfach mal getestet. ^^
    Übrigens:

    Farliam schrieb:

    So weit so klar, aber wenn mann sicher ist, dass die Variabel auf die man zeigt bereits vorhanden ist und einen Wert hat, warum benötigt man dann eine Null initialisierung

    Benötigt man nicht. Du kannst den Pointer auch einfach uninitialisiert lassen. Dann zeigt der Pointer irgendwo auf irgendeinen x-beliebigen Speicherbereich. Wenn du dann drauf zugreifen würdest, könnte es passieren, dass du zufällig irgendeinen existiernden Wert aus dem Speicher liest oder was überschreibst, oder eine Access Violation ausgelöst wird, weil du versuchst, in Speicher zu schreiben, der nicht beschreibbar ist, oder du auf eine nicht allokierte Page zugreifst. Siehe dazu unten.

    Farliam schrieb:

    Weswegen muss ich wenn ich von Anfang an weiß, das mein Speicherbereich existiert, und zugewiesen ist eine Null Zuweißung machen, nur um in der nächsten Zeile
    die bereits bekannte Adresse zuzuweisen?

    Musst du nicht machen. Du kannst dir die Initialisierung mit NULL sparen, wenn du direkt danach das NULL mit einem anderen Wert überschreibst. Du kannst direkt mit dem Wert initialisieren, den du haben willst. In den Beispielen in Post #3 machst du das ja auch.

    Farliam schrieb:

    Wenn dies aber zur allgemeinen Sicherheit beträgt, werde ich mir dass merken.

    In diesem konkreten Fall trägt es nicht zur Sicherheit bei. Aber allgemein gesagt: Das System allokiert nie die Page, in der ein Pointer mit NULL als Wert (auch Null-Pointer genannt) landen würde. Dadurch ist garantiert, dass das Dereferenzieren eines Null-Pointers immer zu einer Access Violation führt. In deinem konkreten Fall kann dir das nicht passieren, aber wenn du dem Pointer nicht sofort was zuweist, sondern das erst später passiert, dann empfiehlt es sich manchmal, mit NULL zu initialisieren, damit im Falle eines Bugs nicht einfach irgendwo irgendwas gemacht wird, sondern definitiv eine Access Violation auftritt.
    Bei lokalen Variablen macht man das normalerweise nicht, wenn man auf jeden Fall irgendwas zuweisen will, was nicht NULL ist. Die Flussanalyse sollte den Fall nämlich abfangen.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    Absolut kein muss zur initialisation!
    Der Grund dafür ist das initialisation auch in Instruktionen übersetzt wird was auch CPU-Zeit in anspruch nimmt,
    daher werden lokale Variablen nicht initialisiert ganz nach dem Sprichwort "You don't pay for what you don't use."

    Ps:
    Da es sich hier um C++ handelt und nicht C:
    Bitte verwende niemals niemals niemals "NULL" sondern "nullptr" anstelle dessen!

    NULL ist ein archeologisches Macro das noch aus dem Hause C stammt und bis hin zu C++11 Standard war! (ich gehe mal davon aus das dein Buch etwas älter als 2010 ist)
    Das Problem damit ist das, je nach Implementation ambiguität entstehen kann.

    Stell dir mal vor was passiert wenn du das hier hast:

    C-Quellcode

    1. Ctor(const char*);
    2. Ctor(int);

    Was denkst wird hier passieren wenn du NULL übergibst?
    Welcher Konstruktor denkst du wird aufgerufen werden?

    Also als Tipp von mir, gewöhne dich daran die altbackenen C/C++ Zeiten hinter dir zu lassen und schön langsam mal mit modernem C++ anzufangen. (Wie gesagt, warscheinlich liegt es daran dass du ein bereits älteres Buch verwendest :) )

    Lg Elanda
    ----------------------------------------------------------------------------------------------------------------------

    Hier könnte meine Signatur stehen, aber die ist mir abfußen gekommen.

    ----------------------------------------------------------------------------------------------------------------------

    Dieser Beitrag wurde bereits 7 mal editiert, zuletzt von „Elanda“ ()

    Farliam schrieb:

    nimmt er erst gar nicht an
    Weil ihm die Reihenfolge der Operatoren nicht klar ist, das wäre so was wie Strict Off.
    Das geht nur mit Klammern richtig aufzulösen.
    Ob das allerdings sinnvoll ist, ist eine andere Frage.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hallo,

    Erstmal danke für die ganzen Antworten. Mittlerweile ist mir dass alles etwas bewusster geworden. Knappe 100 Seiten später wurde dann auch mal ein kleiner Hinweiß ausgegeben, ich zitiere :

    Hinweis: Wenn, wie in diesem Beispiel, von Anfang an bekannt ist, welche Adresse der Zeiger erhalten soll, so kann auf diese Null-Initialisierung verzichtet werden, wenn die Adresse sofort zugewiesen wird.


    Elanda schrieb:

    Ps:
    Da es sich hier um C++ handelt und nicht C:
    Bitte verwende niemals niemals niemals "NULL" sondern "nullptr" anstelle dessen!


    Oh , dass ist eine 5te aktualisierte Ausgabe von 2016. Weiß gerade nicht was ich dazu sagen soll, das der Autor C und C++ fröhlich zusammenmischt ^^

    Hätte aber auch noch "C++ Das umfassende Handbuch" aktuell zu C++17 hier.
    Die Ausgabe 2016 vom C++ für Spiele Programmierer schien mir für den Anfang aber passender, dort werden die Beispiele direkt verwendet, wodurch mir die Verwendung und Nutzen mehr einleuchten.
    Das Umfassende Handbuch ist recht theoretisch für´n Anfang.