"ungültige Gleitkommaoperation" in Funktion, die nichts berechnet
-
Erstmal vielen Dank! Die Feststellung mit den Zeigern schränkt das Problem ja schon mal ein.
Allerdings ist das ne echte Hiobsbotschaft für mich. Im Grunde hab ich sowas die ganze Zeit befürchtet (auch bei dem seltsamen Fehler, der dann plötzlich verschwunden war, nachdem alles neu übersetzt wurde - bzw. da haben wir gedacht, daß irgendwelcher Speicher (Arrays) durch andere Programmteile unabsichtlich überschrieben wird).
Problem ist, daß ich den Code einer Kollegin verwende, und ich hab keine Ahnung, ob der Fehler bei ihr oder bei mir liegt. Sowohl ihr als auch mein Code ist schon recht umfangreich und bei ihrem Programm kenn ich mich auch gar nicht aus.
Im Grunde kann ich zwar die vorige Programmversion wieder verwenden und da eine sehr einfache Lösung einbauen, nur im Grunde bewahrt mich das nicht davor, daß so ein Fehler wieder auftaucht, sowie ich irgendwas an dem Code ändere (wenn ich es richtig mitbekommen habe). Ich könnte grad nur fluchen, da meine Diss. darauf aufbaut, aber das Programm nur Mittel zum Zweck ist und eh schon alles sch*** läuft...Aber jetzt doch nochmal zum Verständnis:
das Problem tritt aber dann doch nur auf, wenn ich einen Zeiger nicht auf 0 setze und doch noch verwende?
Für meinen Teil (und eigentlich müßte das auf meine Kollegin auch zutreffen) bin ich aber noch in der Initialisierungsphase wenn der Fehler auftritt. Da ist es ja eigentlich noch nicht so, daß ein Zeiger nicht mehr benutzt wird, deswegen kann ich ja eigentlich auch keinen vergessen haben.Und entspricht es Eurer Erfahrung, daß so ein Zeigerfehler in einem Programm sein kann, aber sich lange nicht äußert? Also im Prinzip könnte es schon so sein, daß der Fehler im Code meiner Kollegin ist, die ihren Code alleine zwar viel getestet hat, daß er sich aber erst äußert, wenn von mir irgendwelcher Code dazukommt?
Sorry fürs lamentieren, aber ich bin grad verzweifelt und ratlos, in welcher Richtung ich weitermachen soll. Vielleicht doch mal unser beider Code durchforsten, vielleicht ist in der Initialierung ja noch gar nicht soviel fragwürdiges...
-
Aber jetzt doch nochmal zum Verständnis:
das Problem tritt aber dann doch nur auf, wenn ich einen Zeiger nicht auf 0 setze und doch noch verwende?Grundsätzlich ja. Aber könnte theoretisch auch sein,dass du ausserhalb von irgendwelchen Grenzen zugreifst und dann kann das auch passieren.
Im übrigen habe ich in deinem Code wirklich kein einziges delete gesehen. Was mich ansich schon einmal ein wenig beunruhigt.Und entspricht es Eurer Erfahrung, daß so ein Zeigerfehler in einem Programm sein kann, aber sich lange nicht äußert? Also im Prinzip könnte es schon so sein, daß der Fehler im Code meiner Kollegin ist, die ihren Code alleine zwar viel getestet hat, daß er sich aber erst äußert, wenn von mir irgendwelcher Code dazukommt?
Ohh ja. Das ist ja gerade das fiese daran. Das Verhalten ist undefiniert. Und somit kann alles passieren. Und das es durchaus so aussieht, als ob es funktioniert, ist gar nicht einmal so ungewöhnlich. Wenn es eine sicher einen Laufzeitfehler auslöst, dann hätte ich mir auch schon etliche Debug Stunden sparen können. (Das kenn wohl jeder).
Was helfen könnte, wäre sicher auch noch die Initialisierungsliste zu benutzen und alle Zeiger auf 0 zu setzen. Dann musst du ihnen vor der Benutzung einen Wert zuweisen und es kann nicht sein, dass es zufälligerweise doch funktioniert..
-
Hi drakon,
erstmal wieder vielen Dank. Bin ja froh, wenn ich das Problem umkreisen kann.
zu den fehlenden deletes in meinem Code: Stimmt, steht auch noch auf meiner ToDo-Liste. Also, normalerweise werden die Pointerobjekte der Arrayelemente (also dshr, inc usw.) teilweise im weiteren Programmverlauf abhängig von irgendwelchen Bedingungen gelöscht (da ist es auch schon drin, aber hab jetzt nicht alles gepostet), das Array ci mit den übrigen Arrayelementen und a bleiben bis zum Programmende bestehen, aber die muß ich da auch noch löschen.
Aber könnte theoretisch auch sein,dass du ausserhalb von irgendwelchen Grenzen zugreifst und dann kann das auch passieren.
Was meinst Du da? Inwiefern darauf zugreifen?
Was helfen könnte, wäre sicher auch noch die Initialisierungsliste zu benutzen
Was ist eine Initialisierungsliste?
Na ja, ich hab jetzt mal die Initialisierung meiner Kollegin als Ganzes auskommentiert, und jetzt klappt es (bzw. der Fehler tritt dann später auf, weil auf irgendwas zugegriffen werden soll, was halt noch nicht initialisert ist). Das spricht ja eigentlich schon dafür, daß es am Code meiner Kollegin liegt, oder? Puh, nur deren Initialisierung ist echt umfangreich...
-
Was helfen könnte, wäre sicher auch noch die Initialisierungsliste zu benutzen und alle Zeiger auf 0 zu setzen. Dann musst du ihnen vor der Benutzung einen Wert zuweisen und es kann nicht sein, dass es zufälligerweise doch funktioniert..
Ach so, Du meinst es so herum, daß ein Pointer deklariert wurde, aber NOCH einen undefinierten Wert besitzt. Und der dann verwendet wird, obwohl er noch nicht initialisiert wurde? Dann müßte ich den Code auf alle Pointer durchforsten (Suche nach "*") und diese mit 0 initialisieren.
Ich dachte, daß du meinst, daß ein Pointer verwendet wurde, aber nach der Benutzung nicht auf 0 zurückgesetzt wurde, so daß man evtl. auf Werte zurückgreift, die nicht mehr wirklich gültig sind (ist ja auch möglich, aber zumindest wird dabei nichts soooo unplausibles rauskommen oder ist unwahrscheinlicher).
-
Nun ja. Deletes sollten eigentlich immer in direktem Zusammenhang mit den jeweiligen new's sein. Da du ansonsten im Falle von z.B einer Exception ziemlich sicher Speicherlecks bekommst. Ich würde euch dringend anraten Smart Pointer zu benutzen.
http://www.boost.org/doc/libs/1_37_0/libs/smart_ptr/smart_ptr.htmNaja. Du hast ja Arrays und wenn du nun ausserhalb von gültigem Speicher zugreist, kann ein solcher Fehler auch vorkommen. (Auch wenn die Boundchecks stimmen, kann es sein, dass gar kein Speicher angefordert wurde und dann sonst wo zugegriffen wird).
Initialisierungsliste ist die einzige Möglichkeit eine (nicht-statische) Variable innerhalb einer Klasse zu initialisieren.
class foo { foo () : //Anfang einer Initialisierungsliste i ( 2 ), // jede Member mit einem Startwert füllen pi ( 0 ), // Zeiger am besten auf 0 ci ( 42 ) //einzige möglichkeit einen const Wert zu initialisieren { i = 5; // das hier ist niemals eine initialisierung, sonder immer eine Zuweisung ci = 993; // niemals möglich, da eine konstante ihren Wert bei der Initialisierung bekommt } int i; int* pi; const int ci; };Naja. Ich kann jetzt eigentlich nicht mehr viel machen, ausser zu sagen, dass du das schon noch mit ein wenig Geduld hinbringen kannst. Freunde dich mit deinem Debugger an und geniese die paar Stunden Debugging Sessions und dann das, was am wichtigsten ist. Lern im nachhinein aus deinen Fehlern!

-
Tja, das mit der Geduld ist so ne Sache. Wenn man nicht irgendwelchen Zwängen von außen unterliegen würde... Na ja, mal sehen, wie ich den besten Kompromiß finde... (auch das mit den SmartPointern: hört sich ganz gut an, aber bis Januar muß es erstmal einfach nur irgendwie funktionieren, und einiges mehr)
Ok, mit dem direkten delete einbauen mach ich es in Zukunft anders. Aber in dem Fall müßte es doch wirklich am Code meiner Kollegin liegen, oder kann man das so definitiv auch nicht sagen?
Naja. Du hast ja Arrays und wenn du nun ausserhalb von gültigem Speicher zugreist, kann ein solcher Fehler auch vorkommen. (Auch wenn die Boundchecks stimmen, kann es sein, dass gar kein Speicher angefordert wurde und dann sonst wo zugegriffen wird).
Wie kann das gehen? Wenn ich ein Array deklariere, wird doch automatisch Speicher dafür angefordert?! Und wenn ich es nicht deklariere, kann ich es auch nicht verwenden?!
Aber diese Möglichkeit als Fehlerquelle gibt es ja auch noch?! :
Ich dachte, daß du meinst, daß ein Pointer verwendet wurde, aber nach der Benutzung nicht auf 0 zurückgesetzt wurde, so daß man evtl. auf Werte zurückgreift, die nicht mehr wirklich gültig sind
Jedenfalls vielen Dank nochmal!
-
susie schrieb:
Wie kann das gehen? Wenn ich ein Array deklariere, wird doch automatisch Speicher dafür angefordert?! Und wenn ich es nicht deklariere, kann ich es auch nicht verwenden?!
char myArray[20]; myArray[200] = 34; // Oder char* p = new char[20]; p[30] = 3; delete[] p;Das ist gültiges C++ mit gesichertem undefiniertem Verhalten

Also ein standard konformer Kompiler wird das kompilieren, zur Laufzeit wird aber irgendetwas seltsames oder auch nichts passieren. Es ist halt nicht definiert, was passieren wird.Grüssli
-
staun, das wußt ich gar nicht, daß das geht.
Na ja, im Grunde glaub ich nicht, daß das mein Problem ist, da meine Indizes immer in for-Schleifen hochgezählt werden, immer bis zur selben Grenze, aber ich werds im Hinterkopf behalten.
-
Doch noch ne Frage zu den Initialisierungslisten, zu der ich in den Büchern keine Antwort gefunden hab: Wenn ich das mache, muß ich dann jedes Member in dieser Liste angeben? Oder kann ich mir nur grad die Pointer raussuchen und diese mit 0 initialisieren, die restlichen Member so lassen, wie sie sind (mag in fremden Code so wenig wie möglich ändern)?
-
Ja, kannst du.
Die ersten 2 Einträge sollten schon genügen..

http://www.google.ch/search?q=initialisierungsliste+c%2B%2B&ie=utf-8&oe=utf-8&aq=t&rls=org.mozilla
official&client=firefox-aGrundsätzlich sollten aber alle Member initialisiert werden, um Probleme zu vermeiden. Aber besser die Zeiger, als gar nichts.

-
Hm, weit bin ich damit nicht gekommen, die wenigsten Pointer sind Member einer Klasse.
Außerdem ist eine neue Frage aufgetaucht: Wenn ich ein Array von Pointern hab, wie kann ich das denn initialisieren? (außer in einer Schleife?)
-
susie schrieb:
Wenn ich ein Array von Pointern hab, wie kann ich das denn initialisieren? (außer in einer Schleife?)
Wie alle Arrays.
Normalerweise kannst du die Array-Initialisierungsliste verwenden. Arrays als Member können nur mit Schleifen initialisiert werden (du kannst natürlich auch Funktionen wie
std::fill()oderstd::generate()verwenden.
-
Also, ich hab den Fehler jetzt weiter eingegrenzt: Und zwar ist in der Initialisierung eine for-Schleife mit 4 Anweisungen drin, wenn ich die auskommentiere, läuft es über die vorige Fehlerstelle drüber.
Und zwar hat die Klasse, die diese for-Schleife enthält, neben anderen Arrays (alle derselben Größe) als Member ein Array
Soil* soilgrid[xsize][ysize];Besagte for-Schleife sieht so aus:
for (int i=0; i<xsize; i++) { for (int j=0; j<ysize; j++) { soilgrid[i][j] = new Soil( ... eine Liste mit knapp 20 Parametern...); soilgrid[i][j]->aspect = aspect[i][j]; soilgrid[i][j]->inclination = inclination[i][j]; soilgrid[i][j]->setAspectFactor(); } }Soil ist eine Klasse mit ca. 40 Datenmembern, vorwiegend double. In den beiden Konstruktoren, einmal ohne, einmal mit Parametern, habe ich nun für alle Member eine Initialisierungsliste zugefügt (entweder 0.0 genommen, ansonsten die Werte, die als Parameter angegeben waren).
Mehr als eine Zuweisung von Werten (teilweise von Berechnungsergebnissen) an die Datenmember passiert nicht im Konstruktor (es werden auch nicht allen Membern Werten zugewiesen).
Trotzdem, wenn ich die Zeilen 3-6 auskommentiere, tritt der Fehler nicht auf. Wenn ich nur Zeiln 4-6 auskommentiere, aber schon, weswegen ich denke, daß es am Konstruktor liegen muß. Aber was kann das denn dann sein?!
-
Der Fehler tritt auch auf, wenn ich alle Zuweisungen in dem Konstruktor mit den Parametern auskommentiere und auch, wenn ich die soeben erstellte Initialisierungsliste auskommentiere. Aber das macht doch dann gar keinen Sinn, dann müßte es ja an dem Array liegen, aber die Arraygrenzen und die for-Schleife sind doch von daher ok? Oder hab ich Tomaten auf den Augen?
Hab ich unterwegs irgendwelche falschen Schlußfolgerungen gezogen?
-
Hat Soil* soilgrid[xsize][ysize]; die konstanten Grenzen xsize und ysize oder wird es dynamisch erzeugt?
Falls ja: zeig mal die entsprechende Codestelle.Erbt Soil von anderen Klassen?
Falls ja: Was passiert in den Konstruktoren der Superklassen?
-
Die Grenzen vom Array sind konstant. Wie die anderen arrays auch (alle der Größe xsize*ysize und xsize und ysize sind Konstanten).
Soil erbt nicht von anderen Klassen.
-
Noch mal ne ganz grundsätzliche Frage:
Bislang dachte ich bei einem Fehler immer, wenn ich irgendwas auskommentiere, und dann läuft es über diese Fehlerstelle drüber, dann ist die URsache im Auskommentierten zu suchen. Aber ist das zwingend so?
-
Jetzt scheint es zu klappen.
Also ursprünglich war es so (=was ich vorher in main schrieb, aber da hatte ich die Initialisierung von dem Code meiner Kollegin (=das mit der for-Schleife und dem Soil =initialiseSoil) nicht mit aufgeführt).
int main(int argc, char* argv[]) { A* a; initialiseSoil(); a=new A(); a->calculate(); return 0; }Nun hab ich die Objekterzeugung von Klasse A vorgezogen, damit geht es.
int main(int argc, char* argv[]) { A* a; a=new A(); initialiseSoil(); a->calculate(); return 0; }Kann man daraus irgendwelche Schlüsse ziehen?
Ich bin jetzt erstmal total froh, daß es soweit läuft (und muß da erstmal wieder aufräumen). Aber eine wirkliche Fehlerlösung ist das ja nicht. Und wo ich den Fehler dann suchen soll, weiß ich immer noch nicht.
Seht ihr da noch irgendwas, wo man das eingrenzen kann? Seht ihr das noch wie vor als Problem mit Pointern?
(Das mit den SmartPointern ist ja schön, aber da verstehe ich noch zu wenig, wie man Code von ganz woanders her einbindet -zumindest mein Eindruck nach Überfliegen der Webseite)
-
Es könnte sein, dass du den Fehler nur wieder verschiebst. Aber wenn es geht, dann ist ja schon einmal gut.
Kommt halt drauf an, was du alles in initialiseSoil machst. Ich habe mittlerweile die Übersicht über deinen Code verloren.
- Darum wäre ein wirklich lauffähige Version, die den Fehler enthält praktisch gewesen.
-
drakon schrieb:
Darum wäre ein wirklich lauffähige Version, die den Fehler enthält praktisch gewesen.
Nicht nur läuffähig, sondern am besten auch noch lesbar.
susie schrieb:
Und ich glaube auch nicht, daß die Originalbezeichnungen viel weiterhelfen würden, wenn man die Anwendung nicht kennt.
So einer Aussage kann ich nur widersprechen. Es ist äusserst unangenehm, wenn man sich irgendwelche Buchstaben als Klassen erinnern muss. Es ist deutlich lesbarer, wenn man die Funktion der Klasse gleich an ihrem Namen ablesen kann. Dies gilt übrigens auch für die Variablen.
A a; B b = a.b(); C* c = b.do_a(); c->xyz();Da versteht ja niemand, was hier abgeht. Und wenn das über mehrere hundert Zeilen so abgeht, dann ist das für den Leser eher Buchstabensuppe als Programmcode

Grüssli