Class in Heap schreiben und wieder auslesen: System.NullReferenceException
-
Hallo C++'er.
Direkt zur Sache mit folgendem Problem. Ich möchte auf Basis einer Graustufenbitmap ein DGM (Digitales Geländemodell erstellen). Mit anderen Worten: Aus den Graustufen werden Höhen. Jeder Pixel der Bitmap ist zugleich auch eine Variable. Das tolle: Dieser Teil funktioniert schon
Da ich nicht vorschreiben kann und will, wie groß (Pixelanzahl) die Eingangsdatei ist, möchte / muss (?) ich auf den Heap zurückgreifen um aus der Rasterdatei eine "Punktwolke" mit Höhen zu machen. Für den Moment wird jeder Punkt wie folgt definiert:class feld //Definition der Speicherstruktur eines einzelnen Punktes { public: int id; int x; int y; double z; };
Soweit so gut. Neben dem ganzen Bitmap-Umgewandle, dass ich, falls nötig, noch nachreichen kann, geht es wie folgt an den Heap:
for(int ylauf2 = 0; ylauf2 < dgm->Height; ylauf2++) //Definierung der Y-Koordinate //Durchlaufen für jede Y-Spalte { y = y + 1; //Y-Koordinate um 1 erhöhen for(int xlauf2 = 0; xlauf2 <dgm->Width; xlauf2++) //Definierung der X-Koordinate //Durchlaufen für jede X-Spalte { x = x + 1; //X-Koordinate um 1 erhöhen //Berechnung des Rot-Wertes für jede Zelle, die restlichen Werte sind GLEICH! int dgm_x = x-1; //Da die Zählung der GetPixel()-Funktion bei 0 anfängt, die Koordinaten aber bei 1, muss die Umrechnung vorgenommen werden. int dgm_y = y-1; //Siehe oben Color pixelColor = dgm->GetPixel( dgm_x, dgm_y ); //Abfrage des Pixelwertes mit den aktualisierten Koordinaten int r; //Initialisierung der lokalen Variable für den R-Wert r = pixelColor.R; //Einlesen des R-Wertes if(r==0)//falls r 0 ist, wird er auf eins gesetzt, da sonst auch der Höhenwert genullt werden würde { r = 1; } double value = r; //Überführung des Wertes in den Datentyp Double double range = z_max-z_min; //Berechnung der Spanne zwischen höchstem und niedrigstem tatsächlichen Höhenwertes z = value/256*(range);//Umrechnung der RGB-Werte (max. 255) in die tatsächliche Höhe (beliebig)
Im folgenden werden sowohl x als auch y-Wert (sehr umständlich, ich weiß) in einen Strin umgewandelt und kombiniert, so dass x=1 und y=1 dann im weitern als INT = 11 gespeichert werden kann. Ein Int von 22 kann somit als x=2 und Y=2 aufgelöst werden. Weiter gehts:
stringstream sstrx; stringstream sstry; stringstream sstrid; sstrx << x; string idx; sstrx >> idx; sstry << y; string idy; sstry >> idy; string id_string = idy+idx; stringstream sstr_int; sstr_int << id_string; int id_int; sstr_int >> id_int; feld *feldZeiger; //Initialisierung des Zeigers feldZeiger = new feld[id_int]; //Schreiben der Informationen jeder Rasterzelle in die Struktur feldZeiger->id = id_int; //s.o. feldZeiger->x = x; //s.o. feldZeiger->y = y; //s.o. feldZeiger->z = z; //s.o. cout << feldZeiger->id << ": " << feldZeiger->x << " " << feldZeiger->y << " " << feldZeiger->z << endl; } x = 0; //Nachdem für Y=1 alle X-Werte berechnet wurden springt Y auf 2 und X beginnt wieder bei 1 an zu zählen. }
Die Datenzuordnung klappt soweit. Also der zuletzt genannte cout befehl gibt wunderbar für jeden Durchlauf der Schleife die richtigen Werte aus. Ob diese im Heap gespeichert sind, kann ich natürlich nicht sagen, da ich die Werte nämlich nicht abfragen kann. Zur Abfrage habe ich temporär folgende Schleife programmiert:
for(int abfragewert = 13; abfragewert < 14; abfragewert++) { feld *feldZeiger; cout << "Testausgabe"; cout << feldZeiger[abfragewert].x << feldZeiger[abfragewert].y << feldZeiger[abfragewert].z <<endl; }
An dieser Stell stürzt mein Programm mit einem Herzerweichenden *MÖP* ab und lässt mich mit der Nachricht
Eine nicht behandelte Ausnahme des Typs "System.NullReferenceException" ist in Tutorial_Fieber.exe aufgetreten.
Zusätzliche Informationen: Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt.
ab. Diese verweist auf das letzte cout der for-Schleife, also:
cout << feldZeiger[abfragewert].x << feldZeiger[abfragewert].y << feldZeiger[abfragewert].z <<endl;
Nach dieser Orgie an Text und Code also die alles entscheidende Frage... WARUUUUM?
Ernsthaft: Wo liegt der Fehler? Ich habe mich schon wahnsinnig probiert, vor allem da ich ein ähnliches Beispiel, also bezüglich der Abfrage einer Class, nirgendwo gefunden habe und mir dementsprechen unsicher bin, ob man hierfür nicht eine vollständig andere Methodik anwenden müsste.
Schonmal vielen Dank für eure Mühe und viel Spaß beim Knobeln.
Daniel
-
for(int abfragewert = 13; abfragewert < 14; abfragewert++) { feld *feldZeiger; cout << "Testausgabe"; cout << feldZeiger[abfragewert].x << feldZeiger[abfragewert].y << feldZeiger[abfragewert].z <<endl; }
Das funktioniert nicht, weil dein Zeiger auf nichts zeigt. Oder mit anderen Worten "Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt.".
Du definierst dein Feld hier:
for( /*...*/ ) //Hier ist feldZeiger nicht definiert { feld *feldZeiger; //Initialisierung des Zeigers feldZeiger = new feld[id_int]; //Memory Leak } //Hier verliert feldZeiger seine Gültigkeit
Das kann nicht funktionieren, du erstellst für jede id einen neuen Array (mit der länge id_int) auf dem Heap. Dann verlierst du den Zeiger auf deinen array wieder... Auf dem Heap sind dann jede Menge Felder auf die du nicht mehr zugreifen kannst.
Zwei Vorschläge um das zu verbessern:
// MaxID zuerst aus dgm->Height und dgm->Width berechnen int MaxID=calculateMaxId(dgm->Height,dgm->Width); //Die Funktion musst du natürlich noch schreiben ;) feld* myField=new feld[MaxID]; for(/*...*/) { /*...*/ myField[id_int].x=x; myField[id_int].y=y; /*...*/ } cout << "Testausgabe: " << myField[13].x << endl; //Irgendwo musst du den Speicher wieder freigeben! delete [] myField; //Code ist nicht getestet
Alternativ kannst du auch
vector
oderlist
verwenden, wenn du die Grösse am Anfang nicht berechnen willst. Da kannst du dynamisch Elemente hinzufügen.Noch ein paar Kommentare:
Diese Umwandlungsgeschichte int->string->int scheint kompliziert und überflüssig zu sein. Ausserdem bin ich mir nicht ganz sicher ob sie 100%ig funktioniert (habe es nicht genau angeschaut). Was passiert wenn x=11 und y=1 im gegensatz zu y=1 und x=11? Wird das beides mal zu id=111? Du könntest z.B. auch id=x*1000+y verwenden oder besser x*256+y. Da solltest du vielleicht nochmals darüber nachdenken...x = x + 1; //X-Koordinate um 1 erhöhen
Dieser Kommentar ist ziemlich sinnlos
(musste ich einfach noch loswerden...)
-
Hallo,
habe deinen Code ausprobiert und er funktioniert soweit. Danke schonmal für die Hilfe. Schön dass ich dich mit meinen Kommentaren für die Mühe entlohnen konnteAllerdings hat sich nun ein neues Problem aufgetan: Ich kriege zwar keine Fehler beim Ausführen mehr und auch Werte bei meinem cout angegeben, ganz gelöst ist das Problem jedoch nicht, leider. Bei meinem momentanen Code ist es leider so, dass die vom cout
cout << "Testausgabe: " << myField[13].x << endl;
ausgegebenen Werte hahnebüchen sind (Ich beziehe mich beim Code jetzt auf deine Syntax. Bei mir heißt myField natürlich feldZeiger). x und y haben den gleichen Wert (-842150451) und z wartet mit -6,27744e+066 auf. Erwartet hätte ich logischerweise eher sowas wie (4|1|13,5)
Woran könnte es liegen, dass falsche Werte ausgegeben werden? Irgendwo beim einlesen?
Danke schonmal,Daniel
-
Update zum Problem:
Es scheint bereits beim Speichern das Problem zu geben. Wenn ich in der Schleife, in der in den Speicher geschrieben wird, bereits die Ausgabe tätige, jedoch nicht wie bisher percout << feldZeiger->id;
sondern über
cout << feldZeiger[id_int].id;
Bekomme ich wiederum die genau gleichen Werte. Daraus schließe ich, dass diese Werte tatsächlich, wenn auch falsch, in den Speicherplätzen abgelegt werden. Woher diese kommen... keine Ahnung. Zum Fehlerausschluss habe ich die ganze String-Umwandlung jetzt erstmal weggelassen und einfach den Zeiger per id_int mit id_int = id_int + 1; definiert. Wie gesagt, funktionieren tut es trotzdem nicht.
GrüßeDaniel
-
Also zuerst kurz was anderes nur damit du es weisst, du programmierst nicht in C++, sondern in C++/CLI. Das sind zwei unterschiedliche Sprachen und C++/CLI hat ein eigenes Unterforum. Eine
System.NullReferenceException
stammt nämlich aus dem .Net Framework und dieses kann man in normalem C++ nicht verwenden.Dann zu deinem Problem:
Es würde dir und uns helfen, wenn du das Problem einmal auf das Wesentliche reduzieren würdest. Steht auch wunderschön hier:
http://www.c-plusplus.net/forum/viewtopic-var-t-is-200753.htmlDein Code ist nämlich nicht gerade sehr übersichtlich, um ehrlich zu sein, ich empfinde es sogar eher als ein heilloses Durcheinander. Deine Art zu kommentieren ist das typische Beispiel dafür, wie man es genau nicht tun sollte. Kommentare sind eine Ergänzung zum Code und sollten Dinge erklären, welche aus dem Code nicht sofort ersichtlich sind. Am besten nimmt man sie vor einem Block von Code und erklärt, was nachfolgend passieren wird. Wie lustig es bereits gesagt hat, ist zum Beispiel ein Kommentar, dass ein Wert um 1 erhöht wird, nicht besonders sinnvoll. Der Kommentar stört dann eher die Übersicht. Im übrigen gibt es noch die Inkrement-Operatoren.
Reduziere mal in einem kurzen kleinen Programm das Problem (nur ein paar Zeilen, keine komplizierten Berechnungen und Umwandlungen). Entweder wird schon bei dir ein Licht aufgehen oder du kannst dann mit einem kurzen funktionierenden (Edit: oder sagen wir kompilierendem ;)) Quellcode zu uns kommen und wir können dir sagen, wieso dieser Code nicht funktioniert.
Grüssli
-
Dieser Thread wurde von Moderator/in volkard aus dem Forum C++ in das Forum C++/CLI mit .NET verschoben.
Im Zweifelsfall bitte auch folgende Hinweise beachten:
C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?Dieses Posting wurde automatisch erzeugt.
-
Hallo,
danke für den Hinweis/die Tipps. Im Anhang der stark aufgeräumte Code mit neuem ProblemZum kommentieren: Diese Orgie an grünem Text entsteht, da ich ein Programm erstellen möchte, dass Hydrologen / Ökologen bewerten sollen. Diesen ist dann wiederum nicht klar, was int a = 13; bedeutet. Nichtsdestotrotz werde ich das hier im Forum in Zukunft nicht mehr tun, hätte ich natürlich auch selbst drauf kommen können
Zum entschlackten Code:
Die Klassendefinition besteht weiterhin, der vollständigkeit halber hier nochmal:class feld { public: int id; int x; int y; double z; };
Der "Hauptcode":
xy_gesamt = anz_x * anz_y; feld *feldZeiger; feldZeiger = new feld[xy_gesamt]; for(int ylauf2 = 0; ylauf2 < dgm->Height; ylauf2++) { y = y + 1; for(int xlauf2 = 0; xlauf2 <dgm->Width; xlauf2++) { x = x + 1; id_int = id_int+1; feldZeiger[id_int].id = id_int; feldZeiger[id_int].x = x; feldZeiger[id_int].y = y; feldZeiger[id_int].z = z; //Der z-Wert wird in hier nicht abgebildetem Code berechnet. } x = 0; } for(int id_check = 1; id_check < xy_gesamt+1; id_check++) { cout << "Testausgabe, ID: " << feldZeiger[id_check].id << " X: " << feldZeiger[id_check].x << " Y: " << feldZeiger[id_check].y << " Z: " << feldZeiger[5].z << endl; } return 0; }
Das gute: Der ganze Spass funktioniert. Ich kann in den Heap schreiben und gezielt abfragen, mein Problem: Das Löschen will nicht klappen. Mit delete[] feldZeiger , egal an welcher Stelle, stürzt das Programm ab inkl. Debugger, der dementsprechend auch nicht mehr sagen kann warum. Bestimmt wieder ein blöder Fehler, aber über Hilfe wäre ich sehr dankbar.
GrüßeDaniel
-
Dravere schrieb:
Also zuerst kurz was anderes nur damit du es weisst, du programmierst nicht in C++, sondern in C++/CLI. Das sind zwei unterschiedliche Sprachen und C++/CLI hat ein eigenes Unterforum. Eine
System.NullReferenceException
stammt nämlich aus dem .Net Framework und dieses kann man in normalem C++ nicht verwenden.ich würde eher behaupten das das C++ ist - nur das er es als CLR compiliert