Heap Corruption: Finde Ursache nicht [gelöst]
-
Hallo,
folgendes Problem:bei meinem Programm (welches in einem ersten Schritt eine Grafikdatei einliest, und Konturen auf sehr primitiver Basis findet), kommt es unter bestimmten Umständen (d. h. unter bestimmten Grafikdateien), deren Ursache mir unbekannt ist, allerdings 100% reproduzierbar ist, zu einer Heap Corruption:
HEAP CORRUPTION DECTECTED: before normal block (#foo) at 0xEineAdresse.
CRT detected that the application wrote to memory before start of heap buffer.Bei dieser Datei trat der Fehler nur dann auf, wenn ich das Programm im Debugger starte. Wenn ich das im Debug-Modus compilierte Programm normal ausführe, funktioniert es einwandfrei. Bei einer anderen Grafikdatei ist es exakt umgekehrt (im Debugger einwandfrei, bei "Freilauf" Absturz). Die Release-Builds funktionieren einwandfrei (sicher aufgrund der Optimierungen, was jedoch eher gefährlich ist).
Ich werde ersteinmal den groben Aufbau des problematischen Teils beschreiben:
main.cpp:
// ... Texture bild; vector<vector< CVertex* >*>* gContours; // ... int main(int argc, char** argv) { // jede Menge init-Code (SDL, OpenGL) // Das Bild wird in die Variable bild geladen // die 2 interessanten Zeilen: gContours=createGraph(&bild); simplifyGraph(gContours); // ... }die Funktion "std::vector<std::vector< CVertex* >> createGraph(Texture* in_pTexture)" macht im großen und ganzen folgendes:
std::vector<std::vector< CVertex* >*>* gn=new std::vector<std::vector< CVertex* >*>(); char* lContourMap=new char[((Uint32) in_pTexture->width)*((Uint32) in_pTexture->height)]; memset(lContourMap, 0, ((Uint32) in_pTexture->width)*((Uint32) in_pTexture->height)); // eine Menge für das Problem unwichtiger Code gn->push_back(new std::vector< CVertex* >()); gn->back()->push_back(new CVertex((float) start_x, (float) start_y)); // und später dann in Schleifen vielfach durchgeführt: gn->back()->push_back(new CVertex((float) act_x, (float) act_y)); lContourMap[act_y*in_pTexture->width+act_x]=0x3; // am Ende dann noch delete[] lContourMap; return gn;Also im großen und ganzen lediglich Operationen, die ich als absolut ungefährlich ansehen würde.
Nun zur Funktion "void simplifyGraph(std::vector<std::vector< CVertex* >> gn)". Diese löscht im großen und ganzen zahlreiche der erzeugten CVertex aus dem Vektor.
for (lContourIt=gn->begin(); lContourIt!=gn->end(); lContourIt++) { for (size_t j=0; j<(*lContourIt)->size();) { size_t lActContourLength=(*lContourIt)->size(); if (/* eine lange, aber uninteressante Bedingung */) { delete (**lContourIt)[(j+1)%lActContourLength]; // (!!!) -> löscht ein CVertex std::vector< CVertex* >::iterator lVertexIt=(*lContourIt)->begin(); lVertexIt+=((j+1)%lActContourLength); (*lContourIt)->erase(lVertexIt); // entfernt es aus dem Vektor } else { j++; } } }Diese besteht aus mehrern Schleifen, die alle nahezu genauso aussehen, wie die genannte (lediglich unterschiedliche Bedingungen).
An der Stelle (!!!) tritt der Fehler auf. Wenn ich diese auskommentiere, so funktioniert das Programm (auch wenn dann wohl ein Speicherleck auftritt
).Ich habe mich selbstverständlich über Google schlau gemacht, wann der oben genannte Fehler auftritt:
i.) Bei Mischung von new, new[], delete und delete[] in nicht passender Kombination
ii.) Bei Verwendung von DLLs, wenn man auf dem falschen Heap etwas löscht (also man von einer DLL-Funktion
ein im Pogramm erzeugtes Objekt löscht und umgekehrt).i. habe ich sorgfältig überprüft, zu ii.: ich verwende keine DLLs. Somit bin ich mit meinem Latein am Ende und bitte um Hilfe.
Grüße
wolfgke
-
erster Fehler: viel zuviele zeiger.
std::vector<std::vector<CVertex> > gn; std::vector<char> lContourMap(in_pTexture->width)*(in_pTexture->height),0); // eine Menge für das Problem unwichtiger Code gn.push_back(std::vector<CVertex>());//kann man unter umständen durch was anderes ersetzen, kenne ja deinen code net gn.back().push_back(CVertex(start_x,start_y));//keine ahnung, was start_x/start_y für typen sind, aber wenn der c-cast geht, dann gehts sicher auch implizit // und später dann in Schleifen vielfach durchgeführt: gn.back().push_back(CVertex(act_x,act_y));// s.o. lContourMap[act_y*in_pTexture->width+act_x]=0x3; // am Ende dann noch return gn;bisher gehts ohne zeiger

for (lContourIt=gn.begin(); lContourIt!=gn.end(); lContourIt++) { for (size_t j=0; j<lContourIt->size();) { size_t lActContourLength=lContourIt->size(); if (/* eine lange, aber uninteressante Bedingung */) { std::vector<CVertex>::iterator lVertexIt=lContourIt->begin(); lVertexIt+=((j+1)%lActContourLength); lContourIt->erase(lVertexIt); // entfernt es aus dem Vektor } else { j++; } } }mit mehr code ging sicher noch mehr zu verbessern

(j+1)%lActContourLength;wenn, dann liegt der fehler hier. bist du dir sicher, dass j+1 richtig ist? bedenke, das erste vectorlement liegt an position 0. Ohne den fehlenden code kann man aber leider nicht sagen, ob dieser wert 0 werden kann(unter umständen greifst du deshalb auf ein element hinter dem vector zu - programmabsturz). Dann kann alles passieren, auch solche fehler.
-
Für die ganzen Dinge, die du anmerkst (außer, dass das Element an Position ((j+1)%lActContourLength) gelöscht wird: das ist sehr durchdacht), gab es historische Gründe (und Faulheit, dies durch besseren Code zu ersetzen, so lange es der alte tut).
Ich habe sie jetzt alle durch modernen Code ersetzt (war weniger Arbeit als erwartet; glaub mir aber, dass ich in die Korrektheit des alten eine Menge Hirnschmalz investiert habe, die Zeiger zeigen dahin, wo sie sollen
).Problem bleibt.
Übrigens j=10 und lActContourWidth=945, wenn der Fehler auftritt. Also nicht so einfach, wie schon von mir erwartet (sonst hätte ich den Fehler schon selbst gefunden).
P. S. Hier etwas vollständigerer Code (in neuer moderner Schreibweise):
std::vector<std::vector< CVertex* >>* createGraph(Texture* in_pTexture) { TransparencyNode tn; Sint32 start_x; Sint32 start_y; // gn always points to the first vertex of the graph and will be returned std::vector<std::vector< CVertex* > >* gn=new std::vector<std::vector< CVertex* > >(); char* lContourMap=new char[((Uint32) in_pTexture->width)*((Uint32) in_pTexture->height)]; memset(lContourMap, 0, ((Uint32) in_pTexture->width)*((Uint32) in_pTexture->height)); while (true) { bool lStartVertexFound=false; for (Uint32 i=0; i<=in_pTexture->height; i++) for (Uint32 j=0; j<=in_pTexture->width; j++) { tn=getTransparencyNode(in_pTexture, j, i); if (tn.neighbour_opaque!=0x0 && tn.neighbour_opaque!=0xF && lContourMap[i*in_pTexture->width+j]!=3 /*&& tn.neighbour_opaque!=0x6 && tn.neighbour_opaque!=0x9*/ ) { printf("Start vertex i=%u, j=%u\n", i, j); start_x=j; start_y=i; lStartVertexFound=true; goto after_vertex_search; } } after_vertex_search: if (!lStartVertexFound) break; Sint32 act_x=start_x; Sint32 act_y=start_y; gn->push_back(std::vector< CVertex* >()); gn->back().push_back(new CVertex((float) start_x, (float) start_y)); if (tn.neighbour_opaque!=0x6 && tn.neighbour_opaque!=0x9) lContourMap[start_y*in_pTexture->width+start_x]=0x3; else { // TODO: Write correct code here } while (true) { tn=getTransparencyNode(in_pTexture, act_x, act_y); if (opaque_delta_x[tn.neighbour_opaque]==0 && opaque_delta_y[tn.neighbour_opaque]==0) { if (tn.neighbour_opaque==0x6) { // TODO: Write correct code here } else if (tn.neighbour_opaque==0x9) { // TODO: Write correct code here } assert(tn.neighbour_opaque!=0x0 && tn.neighbour_opaque!=0xF); } else { act_x+=opaque_delta_x[tn.neighbour_opaque]; act_y+=opaque_delta_y[tn.neighbour_opaque]; if (act_x==start_x && act_y==start_y) { break; } else { gn->back().push_back(new CVertex((float) act_x, (float) act_y)); if (tn.neighbour_opaque!=0x6 && tn.neighbour_opaque!=0x9) lContourMap[act_y*in_pTexture->width+act_x]=0x3; else { // TODO: Write correct code here } } } } } delete[] lContourMap; return gn; } void simplifyGraph(std::vector<std::vector< CVertex* > >* gn) { std::vector<std::vector< CVertex* >>::iterator lContourIt; for (lContourIt=gn->begin(); lContourIt!=gn->end(); lContourIt++) { for (size_t j=0; j<(*lContourIt).size();) { size_t lActContourLength=(*lContourIt).size(); if (abs(determinant( (*lContourIt)[(j+1)%lActContourLength]->getCoord(0) -(*lContourIt)[j]->getCoord(0), (*lContourIt)[(j+2)%lActContourLength]->getCoord(0) -(*lContourIt)[(j+1)%lActContourLength]->getCoord(0), (*lContourIt)[(j+1)%lActContourLength]->getCoord(1) -(*lContourIt)[j]->getCoord(1), (*lContourIt)[(j+2)%lActContourLength]->getCoord(1) -(*lContourIt)[(j+1)%lActContourLength]->getCoord(1) ) )<=gcDoubleEps) { delete (*lContourIt)[(j+1)%lActContourLength]; (*lContourIt).erase((*lContourIt).begin()+((j+1)%lActContourLength)); } else { j++; } } } // 2 weitere analoge Schleifen mit anderen Bedingungen im if }P. P. S.: Die Fehlermeldung, die ich oben angegeben habe, sagt aus, dass ich auf ein Element *vor* dem Beginn des Heap Buffers schreibend zugreifen wollte, was meines Erachtens gar nicht sein kann, weshalb ich auf eine mir unbekannte Tücke der STL als Problemursache tippe.
-
Ich habe mittlerweile einen Verdacht, dass das Problem auf eine Inkompatibilität zwischen SDL und der Debug-Version der Microsoft-CRT zurückzuführen ist (als ich es gegen die Nicht-Debug-Build linkte, kam es zu keinem Fehler mehr).
Ich werde von daher eine eigene Build von SDL, welche gegen die Debug-Version der CRT gelinkt ist, erzeugen.
-
wolfgke schrieb:
als ich es gegen die Nicht-Debug-Build linkte, kam es zu keinem Fehler mehr
Das liegt daran, dass die Releaseversion die Heapprüfung nicht durchführt.
Benutz doch mal vector::at statt vector::operator[]. Dann fliegen dir wenigstens Zugriffe mit ungültigem Index gleich um die Ohren.
-
Diese Idee klingt ausgesprochen sinnvoll. Werde es sofort ausprobieren. Das Erstellenn einer Debug-Build von SDL brachte nämlich (wie du es zurecht erwartest) nicht den gewünschten Zweck (wenn auch einen unerwarteten anderen sinnvollen, weshalb es keine ganz vertane Arbeit war).
-
Update: Habe die Änderung durchgeführt. Scheint tatsächlich zu helfen, da das Resultat darauf hindeutet, dass der Fehler an einer anderen Stelle liegt. Es ist noch zu früh, Details darüber zu schreiben (ich muss mir erst 100% klar werden, was da tatsächlich vor sich geht, bevor ich weiter poste), aber auf jeden Fall wirft es schonmal ein ganz neues Licht auf alles.
-
Hallo,
ich wollte euch sagen: Problem gelöst, "Kunde" wolfgke glücklich.Durch den Tipp konnte ich die wahre Ursache des Problems finden: und zwar hatte ich einen zu kleinen Speicherbereich reserviert:
new char[((Uint32) in_pTexture->width)*((Uint32) in_pTexture->height)];
während tatsächlich:
new char[((Uint32) in_pTexture->width+1)*((Uint32) in_pTexture->height+1)];
Elemente notwendig sind. Dies führte unter gewissen seltenen Umständen (ich hatte sehr viel Glück überhaupt auf eine Situation zu stoßen, in der sich dieser Fehler zeigt), dazu, dass Teile des vector überschrieben werden. Und das Unheil nimmt seinen Lauf...
Herzlichen Dank
wolfgke