Zugriff auf Speicheradresse nach 'delete' noch immer möglich



  • Hallo

    Wenn ich über 'delete ...' den belegten Speicher wieder freigebe, kann ich danach trotzdem noch auf diesen zugreifen.

    // Klassendefinitionen
    class Point
    {
    public:
    	float x, y, z;
    	Point();
    	~Point();
    };
    
    class Gallier
    {
    public:
    	Point* position;
    	Gallier();
    	~Gallier();
    };
    
    // danach deren Konstruktoren und Destruktoren
    Gallier::Gallier()
    {
    	std::cout << "\nConstructor of Gallier executed\n";
    	position = new Point();
    }
    
    Gallier::~Gallier()
    {
    	std::cout << "\nDestructor of Gallier executed\n";
    	delete position;
    	position = nullptr;
    }
    
    Point::Point()
    {
    	std::cout << "\nConstructor of Point executed\n";
    	x = 7.0F;
    	y = 9.0F;
    	z = 11.0F;
    }
    
    Point::~Point()
    {
    	std::cout << "\nDestructor of Point executed\n";
    }
    
    // und jetzt etwas damit spielen
    Gallier* asterix = new Gallier();
    	std::cout << "asterix->position->x = " << asterix->position->x << "\n";
    	std::cout << "asterix->position->y = " << asterix->position->y << "\n";
    	std::cout << "asterix->position->z = " << asterix->position->z << "\n";
    
    	Point* tempPtr = asterix->position;
    
    	asterix->position->x = 70.0F;
    	asterix->position->y = 90.0F;
    	asterix->position->z = 110.0F;
    
    	delete asterix;
    
    	// ab hier dürfte der Zugriff eigentlich nicht mehr möglich sein
    	std::cout << "tempPtr->pos x = " << tempPtr->x << "\n";
    	std::cout << "tempPtr->pos y = " << tempPtr->y << "\n";
    	std::cout << "tempPtr->pos z = " << tempPtr->z << "\n";
    

    Was mich wundert: Ich kann mit tempPtr noch immer auf die korrekten Werte zugreifen, obwohl mit 'delete position' im Destruktor der Klasse 'Gallier' der Speicher wieder frei sein sollte.
    Ich habe den Eindruck, dass der Speicher nicht freigegeben wird, weil das System bemerkt, dass es einen weiteren Zeiger (tempPtr) gibt, der ebenfalls auf genau diese Adresse zeigt.

    Die Ausgabe in der Konsole sieht so aus:

    Constructor of Gallier executed

    Constructor of Point executed
    asterix->position->x = 7
    asterix->position->y = 9
    asterix->position->z = 11

    Destructor of Gallier executed

    Destructor of Point executed
    tempPtr->pos x = 70
    tempPtr->pos y = 90
    tempPtr->pos z = 110



  • Speicher freigeben != Speicher initialisieren
    da stehen einfach noch die alten Daten das ist alles

    Warum: Es kostet einfach zu viel Zeit - und wenn du es für Fehlersuche brauchst kannst du ja selber mit nullen oder sonstigem überschreiben



  • Doofkopp schrieb:

    Wenn ich über 'delete ...' den belegten Speicher wieder freigebe, kann ich danach trotzdem noch auf diesen zugreifen.

    Das ist reiner Zufall, wenn auch zu erwarten.

    Streng genommen ist das Verhalten undefiniert. Praktisch ist es meistens das einfachste, den Inhalt des Speichers an der Stelle so zu lassen, wie er ist und den Bereich als "verfügbar" zu markieren.



  • Doofkopp schrieb:

    Wenn ich über 'delete ...' den belegten Speicher wieder freigebe, kann ich danach trotzdem noch auf diesen zugreifen.

    Du darfst es aber nicht (weil dir der Speicher dann nicht mehr gehört).



  • Mit anderen Worten: Genau dieser Speicher kann zufällig von einem anderen Zeiger oder auch direkt anderem Programm wieder angefordert werden. Wenn das passiert und ich mit tempPtr nochmals darauf zugreife, kann es krachen und das Programm schmiert ab.
    Dass noch die alten Werte drin stehen, war mir so nicht bewusst. Das erklärt allerdings tatsächlich das Verhalten.

    OK, von daher alles bestens. Ich danke allen.



  • Doofkopp schrieb:

    Wenn das passiert und ich mit tempPtr nochmals darauf zugreife, kann es krachen und das Programm schmiert ab.

    Ja, es kann. Oder noch viel schlimmer: Es kracht nix und das Programm läuft weiter, rechnet aber Unsinn aus.

    Doofkopp schrieb:

    Dass noch die alten Werte drin stehen, war mir so nicht bewusst. Das erklärt allerdings tatsächlich das Verhalten.

    Bei MSVC wird in DEBUG Builds der Speicherinhalt beim Freigeben überschrieben - eben damit man solche Fehler schneller findet.
    Würde mich fast wundern wenn andere Compiler (bzw. Library Implementierungen) das nicht auch könnten.


Anmelden zum Antworten