Zeiger auf lokale Variable auflösen



  • Gut, danke für die Erklärung.


  • Mod

    Es geht sogar noch ein bisschen weiter; mit der Freigabe des Speichers wurde der Zeiger(-wert) selbst ungültig - jede Verwendung dieses Wertes (z.B. Speichern in einer Zeigervariable) resultiert für sich bereits in undefiniertem Verhalten.



  • Soweit ichs verstanden habe, kann der Speicher nach Funktionsende und damit nach "Zerstörung" der lokalen Variablen wieder andersweitig vom BS verwendet und beschrieben werden. Wenn ich jetzt mit dem Zeiger auf diesen Inhalt zugreife, benutze ich nicht mehr den Wert, den ich eigentlich benutzen will. Also ist das Verhalten undefiniert. Evtl. steht an der Stelle nicht mal mehr ein int. Dann stimmt auch der int-Zeiger vom Typ her nicht mehr überein. Meinst du das?



  • würdest du im debug-Modus kompilieren, müsste so eine Handlung zu einem Segmentation Fault führen, und das Programm beendet werden. Das sich der Wert noch nicht geänder hat, ist kein zufall, sondern nicht anders Möglich, denn es wurde seit der Freigabe des Speichers noch keine neue Lokale Variable angelegt. Würdest du bevor du die Variable ausgibst noch eine Weitere initialisieren müsste sich der Wert auch änders. Der Speicer kann nicht von anderen Programmen belegt werden, weil es sich um den Stack handelt.



  • Ach, du hast Recht. Es gibt ja nen gekapselten Adressraum für das Programm.


  • Mod

    moagnus schrieb:

    Soweit ichs verstanden habe, kann der Speicher nach Funktionsende und damit nach "Zerstörung" der lokalen Variablen wieder andersweitig vom BS verwendet und beschrieben werden. Wenn ich jetzt mit dem Zeiger auf diesen Inhalt zugreife, benutze ich nicht mehr den Wert, den ich eigentlich benutzen will. Also ist das Verhalten undefiniert. Evtl. steht an der Stelle nicht mal mehr ein int. Dann stimmt auch der int-Zeiger vom Typ her nicht mehr überein. Meinst du das?

    Das ist eine mögliche Variante von undefiniert, kommt aber nicht zum eigentlichen Punkt. Jede Verwendung des Zeigerwertes (und da der Rückgabewert der Funktion ein rvalue ist bedeutet das hier: jede beliebige Operation mit dem Funktionsaufruf, die diesen Wert nicht verwirft (=cast nach void, ,-Operator, Nicht-Verwendung des Funktionswertes) ist undefiniert. Wie eine konkrete Implementation hier reagiert, stellt dann nur eine Möglichkeit dar. Es gibt einen praktisch bedeutsamen analogen Fall:

    int* p = new int;
    delete p;
    

    Visual C++ (wie wohl auch andere Compiler) verändert im Debug-Modus beim delete das Zeigerobjekt p (das ist erlaubt, obwohl diese Veränderung durchaus beobachtbar sein kann - wir könnten ja p einfach als rohen Speicher anfassen und untersuchen, per memcmp und Ähnlichem). Der Wert, den p dann erhält, zeigt üblicherweise auf nicht existenten Speicher um dann eine Zugriffsverletzung auszulösen. Der Punkt dabei ist, dass der Wert p nach dem delete nichts mit dem davor zu tun hat. Ein vergleichbarer Vorgang (sogar problemloser, da wir hier nicht auf die Repräsentation zugreifen können) ist durchaus bei der Rückkehr aus der Funktion denkbar. Der bloße Versuch, diesen Wert z.B. zu dereferenzieren ist dann schon problematisch, zum Lesen irgendeines Wertes kommen wir gar nicht erst. Und das ist nur eine Möglichkeit, wie eine Implementation hier reagieren könnte (eine Andere wäre z.B. bei einem Interpreter, dass der Wert der Gleiche ist, aber nicht mehr mit einer realen Adresse verknüpft ist - und wenn ich länger drüber nachdenke, fallen mir sicher noch mehr Szenarien ein).



  • Ich versteh leider immer noch nicht ganz, was du meinst.
    Was ich verstanden zu haben glaube: Compiliert man dein Code-Beispiel im Debug-Modus, wird die Speicheradresse geändert, die in p abgelegt ist. Deswegen macht Dereferenzierung keinen Sinn.



  • Also camper, dass eine Implementierung den Wert von "p" ändern darf beim delete ist mir neu. MSVC macht das auch *nicht*. MSVC überschreibt nur den Speicher der freigegeben wurde mit 0xDD, allerdings nicht die Zeigervariable selbst.
    Und wie gesagt, ich glaube auch nicht dass das legal wäre.
    (Davon abgesehen dass es kaum noch sinnvolle Dinge gibt die man mit "p" machen kann nach dem delete)



  • hustbaer schrieb:

    Also camper, dass eine Implementierung den Wert von "p" ändern darf beim delete ist mir neu. MSVC macht das auch *nicht*. MSVC überschreibt nur den Speicher der freigegeben wurde mit 0xDD, allerdings nicht die Zeigervariable selbst.

    bin mir ziemlich sicher, das die debugging-rtl nach dem delete den pointer auf 0xfeeefeee zeigen lässt.



  • du irrst schrieb:

    hustbaer schrieb:

    Also camper, dass eine Implementierung den Wert von "p" ändern darf beim delete ist mir neu. MSVC macht das auch *nicht*. MSVC überschreibt nur den Speicher der freigegeben wurde mit 0xDD, allerdings nicht die Zeigervariable selbst.

    bin mir ziemlich sicher, das die debugging-rtl nach dem delete den pointer auf 0xfeeefeee zeigen lässt.

    Ja? Also bei mir wird der Speicher angefasst und auf 0xFEEE gesetzt, und nicht der Pointer selbst.



  • Tachyon schrieb:

    Ja? Also bei mir wird der Speicher angefasst und auf 0xFEEE gesetzt, und nicht der Pointer selbst.

    ja, hast recht. ich hab mich an "access violation reading address 0xfeeefeee" erinnert. das war aber ein pointer in einem gelöschten objekt, kein alleinstehender pointer.


  • Mod

    ich irrte schrieb:

    Tachyon schrieb:

    Ja? Also bei mir wird der Speicher angefasst und auf 0xFEEE gesetzt, und nicht der Pointer selbst.

    ja, hast recht. ich hab mich an "access violation reading address 0xfeeefeee" erinnert. das war aber ein pointer in einem gelöschten objekt, kein alleinstehender pointer.

    Ich kann es jetzt auch gerade nicht nachvollziehen, allerdings MSDN delete Operator (C++)

    If the operand to the delete operator is a modifiable l-value, its value is undefined after the object is deleted.

    "undefined" meint hier wahrscheinlich "indeterminate" in standardesisch.

    Was die Standardkonformität dieses Verhaltens angeht, muss ich noch mal recherchieren.



  • Nö, ok, wenn das so im Standard steht dann wird das schon gelten. Krass 🙂
    Naja, eigentlich eh ziemlich egal, weil man ja sowieso nixmehr mit dem Zeiger machen dürfte. Nichtmal lesen/vergleichen mit einem anderen Zeiger.



  • hustbaer schrieb:

    Nö, ok, wenn das so im Standard steht dann wird das schon gelten. Krass :)...

    Vielleicht wollten sie damit einer "delete setzt auf 0"-Implementierung die Hintertür auflassen.

    Gruß,

    Simon2.



  • Auch möglich.

    Ich glaube mich zu erinnern dass im Standard irgendwo steht dass man einen Zeiger der auf etwas zeigt was bereits gelöscht wurde, nichtmehr anfassen darf, mit der einzigen Ausnahme dass man ihn überschreiben darf. Und irgendwo las ich mal dass vermutlich die Überlegung dahinter bzw. der Grund dafür jene(r) ist, dass es Plattformen gibt bzw. geben könnte, wo bereits das Laden eines ungültigen Zeigers in ein Register zu einem Problem führen könnte. (Weil es z.B. das Segment bzw. die Seite auf die der Zeiger verweist garnichtmehr gibt, und eine Gültigkeitsprüfung schon beim Laden in ein Register erfolgt, nicht erst bei der Dereferenzierung.) Auf solchen Plattformen wäre es dann einfach doof für die Compiler Entwickler wenn es erlaubt wäre den Wert nach einem delete noch anzufassen.

    Da man den Wert also nichtmehr anfassen darf kann man im Standard auch gleich sagen er ist danach undefiniert, weil's ja eh egal ist -- "legal" kann man ja eh nixmehr damit machen.


Anmelden zum Antworten