Zugriffverletzung mit STL vector
-
Hallo,
ich bein dabei ein Spiel (mit MVC++ 2010 EE) zu programmieren, habe jetzt jedoch ein ungelöstes Problem. Erstellen funktioniert ohne Fehler, jedoch stürtzt das Programm beim Debuggen mit folgender Fehlermeldung ab:
Eine Ausnahme (erste Chance) bei 0x008234f9 in Labyrinth.exe: 0xC0000005: Zugriffsverletzung beim Lesen an Position 0x00000018. Unbehandelte Ausnahme bei 0x008234f9 in Labyrinth.exe: 0xC0000005: Zugriffsverletzung beim Lesen an Position 0x00000018.Mir ist unverständlich, wo mein Programmier- bzw. Denkfehler liegt; hier der wohl relevante Quellcode:
struct SDamageBlock { SDamageBlock() : Damage(0), Time(0) {} char Damage; char Time; }; class CDynamiteMgr { public: CDynamiteMgr() { } void Init() { DamageLayer.clear(); for( int i = 0; i < mapwidth*mapheight; i++) { SDamageBlock* blk = new SDamageBlock; DamageLayer.push_back( blk); } } void SetDamage( int x, int y, char damage, char time) { DamageLayer[ y*mapwidth +x]->Damage = damage; DamageLayer[ y*mapwidth +x]->Time = time; } // ... private: vector<SDamageBlock*> DamageLayer; };Der Fehler liegt irgendwo beim initialisieren, aber was ist falsch?
-
Der gezeigt Code reicht noch nicht, um den Fehler zweifelsfrei zu identifizieren. Ich stelle aber fest, dass du ungewöhnliche und fehleranfällige Programmiertechniken benutzt (kommst du von Java?), daher verwundert es nicht, dass du diese Art von Fehler bekommst. Die Hauptfrage an dich ist daher diese: Warum speicherst du in deinem vector Zeiger anstatt konkreter Objekte? Ich würde Geld darauf wetten, dass dein Fehler hierauf zurückzuführen ist.
Außerdem schreit eine Funktion mit Namen init geradezu danach, dass dies eigentlich der Konstruktor sein sollte.
-
Ich stelle aber fest, dass du ungewöhnliche und fehleranfällige Programmiertechniken benutzt (kommst du von Java?)
Meinst du damit den indizierten Zugriff auf Elemente des vectors?
Nein, in Java habe ich noch nicht programmiert.Warum speicherst du in deinem vector Zeiger anstatt konkreter Objekte?
Ist das denn so verkehrt? Ich hab das ganze aber mal abgeändert - jedoch ohne Erfolg.
Außerdem schreit eine Funktion mit Namen init geradezu danach, dass dies eigentlich der Konstruktor sein sollte.
Da hast du eig recht, jedoch wollte ich die Funktion mehrfach aufrufen können (für das erneute Laden), trotzdem ist es wohl sinnvoll das Init in den Konstruktor zu nehmen.
Diesmal also ein bisschen mehr Code:
// includes... struct SDamageBlock { char Damage; char Time; }; typedef std::list<CDynamite>::iterator iter; class CDynamiteMgr { public: CDynamiteMgr() { Init(); } void Init() { DynamiteList.clear(); DamageLayer.clear(); for( int i = 0; i < mapwidth*mapheight; i++) { SDamageBlock blk; blk.Damage = 0; blk.Time = 0; DamageLayer.push_back( blk); } } void AddDynamite( int type, int x, int y) { CDynamite dyn; dyn.Activate( type, x, y); DynamiteList.push_back( dyn); SetDamage( x, y, 50, 2); if( type == 1 || type == 3 || type == 4) { SetDamage( x+1, y, 20, 2); SetDamage( x-1, y, 20, 2); SetDamage( x, y+1, 20, 2); SetDamage( x, y-1, 20, 2); } if( type == 2 || type == 3 ) { SetDamage( x+1, y+1, 20, 2); SetDamage( x+1, y-1, 20, 2); SetDamage( x-1, y+1, 20, 2); SetDamage( x-1, y-1, 20, 2); } if( type == 4) { SetDamage( x+2, y, 20, 2); SetDamage( x-2, y, 20, 2); SetDamage( x, y+2, 20, 2); SetDamage( x, y-2, 20, 2); } } void SetDamage( int x, int y, char damage, char time) { DamageLayer[ (y-1)*mapwidth +x -1].Damage = damage; DamageLayer[ (y-1)*mapwidth +x -1].Time = time; } void ResetDamage( int i) { DamageLayer[ i].Damage = 0; } void ResetDamage( int x, int y) { ResetDamage( (y-1)*mapwidth +x -1); } // alles Dynamit anzeigen void BlitAll( BITMAP* dest, int mxo, int myo) { for( iter i = DynamiteList.begin(); i != DynamiteList.end(); ++i) { (*i).Blit( dest, mxo, myo); if( (*i).Explode()) { DynamiteList.erase( i, i); } } } private: std::list<CDynamite> DynamiteList; std::vector<SDamageBlock> DamageLayer; };CDynamite ist übrigens eine Klasse.
Wenn ich das Programm debugge, wird mir die folgende return-Zeile (aus xutility, Z.97) markiert:
_Iterator_base12 **_Getpfirst() const { // get address of iterator chain return (_Myproxy == 0 ? 0 : &_Myproxy->_Myfirstiter); }
-
Und mit welchen Werten initialisierst du mapwidth und mapheight ?

-
mapwidth und mapheight sind globale short int die mit dem Laden der Map initialisiert werden, mit meiner aktuellen Map sind beide Werte je 100.
Aber guter Hinweis, dass ich darauf achten muss, diese vorher zu initialisieren habe ich fast missachtet (ist aber trotzdem geschehen...).
-
Kannst du mit dem Debugger eventuell noch etwas den Stack hochwandern in deinen eigenen Quelltext?
-
Also der Fehler entsteht schon beim Aufruf von
DynamiteList.clear();Das ganze ist mir unerklärlich!?
-
for( iter i = DynamiteList.begin(); i != DynamiteList.end(); ++i) { (*i).Blit( dest, mxo, myo); if( (*i).Explode()) { DynamiteList.erase( i, i); } }In diesem Fall würdest du der Iteration unter dem Hintern die Elemente wegziehen, ist das so legal? Allerdings bringt das erase so nix, denn die beiden Iteratoren sollten eine Sequenz bilden und die ist in diesem Fall leer.
Edit: Aber erase gibt den ersten gültigen iterator nach der gelöschten Sequenz zurück, den könntest du für den nächsten Durchlauf nutzen oder den alten Iterator inkrementieren, falls nix explodiert ist.
-
Also wenn das clear zum Absturz führt, ist dein DynamiteMgr nicht initialisiert... Dann liegt das momentane Absturzproblem außerhalb von CDynamiteMgr. Immerhin ist diese Anweisung die allererste die überhaupt auf die Daten des Objekts zugreift.
-
olli11 schrieb:
Also der Fehler entsteht schon beim Aufruf von
DynamiteList.clear();Das ganze ist mir unerklärlich!?
Dann wäre (insbesondere der Destruktor von) CDynamite (incl.
Copy-Construktoren) mal interessant...
-
Vielen Dank erstmal allen, die mir helfen wollen!
@Decimad
Du hast wohl recht, diese for-Schleife muss ich wohl noch mal überarbeiten. Aber wie lösche ich denn ein einzelnes Objekt aus der Liste?EIn Auszug aus CDynamite:
CDynamite() : Active( 0), Type( 0), Time( 0), X( 0), Y( 0) { //BmpDynamite = load_bitmap( "dynamite.tga", NULL); } ~CDynamite() { //destroy_bitmap( BmpDynamite); } void Reset() { Active = 0; Type = 0; Time = 0; X = 0; Y = 0; } void Blit( BITMAP* dest, int mxo, int myo) { if( Active) { char fpstext[8]; sprintf (fpstext, "%d", Time) ; text_mode (0); textout (dest, font, fpstext, X -mxo +10, Y -myo +10, 255); //masked_blit( BmpDynamite, dest, 0, 0, X -mxo, Y -myo, BmpDynamite->w, BmpDynamite->h); Time--; } } bool Explode() { if( !Time) { if( Type == 1 || Type == 3 || Type == 4) { // ... } if( Type == 2 || Type == 3 ) { // ... } if( Type == 4) { // ... } Active = false; return 1; } return 0; } void Activate( int type, int x, int y) { X = x; Y = y; Type = type; Time = 3 * 60; Active = true; } //... bool Active; // aktiv, d.h. gezündet char Type; char Time; // verbleibende Zeit bis zur Explosion int X, Y; // Position in Map //BITMAP *BmpDynamite; // die Bitmap-GrafikEs ist jedoch egal, ob ich BmpDynamite herausnehme oder nicht!
Dann liegt das momentane Absturzproblem außerhalb von CDynamiteMgr
Wie kann es dann sein, dass der Fehler nur mit (vollständigem) CDynamiteMgr auftritt?
Oder besser gefragt: Wo meinst du kann der Fehler sonst liegen?
-
Also ich steige im Moment nicht durch diesen Quelltext bzw. verstehe den Bezug nicht zu dem vorhergehenden, kann nirgendwo etwas von wegen DynamiteMgr lesen. Was ich meinte ist, dass Init und somit std::list::clear ja überhaupt nur aus dem Konstruktor von DynamiteMgr aufgerufen wird. Wenn ein clear von einer Default-Konstruierten std::list mit Ausnahme aussteigt, dann muss da vorher schon etwas arg im Argen gelegen haben. Zumindest meiner außenstehenden Ansich nach, im Moment.
Also, debugge das Programm, beim Crash gehe per Debugger im Stackfenster so weit hoch dass du irgendwann in deinem Quelltext landest und dann noch weiter hoch, bis du etwas unerwartetes entdecken kannst. Das wird man wahrscheinlich schon in Init sehen, wenn der this-Zeiger krumm ist, dann kann man noch weiter hochgehen und schauen, warum dem wohl so ist.Da ich mich heute an anderer Stelle mit dem Thema Remote-Unterstützung bzw. RDP beschäftigt habe, kommen mir jetzt immer die Gedanken, dass man als Außenstehender ja eigentlich fix per RDP dein Visual-Studio fernsteuern könnte, um den Fehler zu finden etc bzw. fix den Source-Code zu brausen

-
Ach ich Dummchen
- habe Init von DynamiteMgr aufgerufen bevor ich die Instanz selbst erstellt habe. *Rot-werd*-Smiley
Der Fehler ist also damit behoben.
Vielen Dank euch!
-
olli11 schrieb:
Ach ich Dummchen
- habe Init von DynamiteMgr aufgerufen bevor ich die Instanz selbst erstellt habe.Würdest du nicht so eine C-Style-Objektsemantik mit öffentlichen Init-Funktionen betreiben, könntest du Probleme dieser Art auch besser in den Griff kriegen. Verwende Konstruktoren zur Objektinitialisierung. Die Container am Anfang zu leeren macht übrigens auch nicht viel Sinn. Sollte die
Init()-Funktion mehr als einmal aufgerufen werden, nenn sie anders.Und halte dich bei der Gelegenheit auch an die Regel der Grossen Drei: Sobald eine der Methoden Kopierkonstruktor, Zuweisungsoperator und Destruktor implementiert wird, müssen die anderen beiden ebenfalls vom Benutzer definiert werden. Und nimm statt
int typeeinenummit allen Typen, damit sparst du dir Magic Numbers und ihre Probleme, zudem wird der Code übersichtlicher.
-
und nur so btw:
for( iter i = DynamiteList.begin(); i != DynamiteList.end(); ++i) { (*i).Blit( dest, mxo, myo); if( (*i).Explode()) { DynamiteList.erase( i, i); } }=>
iter i = DynamiteList.begin(); while( i != DynamiteList.end()) { i->Blit( dest, mxo, myo); if( i->Explode()) { i = DynamiteList.erase( i); } else { ++i; } }