Hi, Mir ist grad beim Programmieren aufgefallen das sich ein Programm anders verhält wenn man den Optimizer einschaltet. Ist etwas schwierig zu erklären wo und wie das genau passiert. Aber vielleicht gibts ja irgendwelche allgemeinen Regeln anhand derer man das nachvollziehen kann. Vielleicht könnt ihr mir da weiterhelfen. Gruß Markus
Allgemeine Regeln für Probleme mit dem Optimizer? Gibt's bestimmt... zu Tausenden.
Ok, fang mal an aufzuzählen ;) Spaß beiseite, hab ich vermutet. Aber wie kann ich an das Problem rangehen ohne graue Haare zu kriegen?
Optimizer sind auch nur Programme und haben als solche Fehler. Meistens ist es aber so, dass du irgendwelche C Regeln verletzt hast, die sich bei einem normalen Debug-Build nicht auswirken. Erst wenn der Optimizer loslegen darf, treten die Fehler dann zu Tage, da der Optimizer laut C-Sprachstandard irgendwelche Annahmen treffen darf, die du aber ignoriert, bzw. verletzt hast. Klassische Dinge am PC sind zb: * Bei einem Debug Build werden zwischen den Variablen oft und gerne ein paar Füll-Bytes eingeschoben. Vor allem bei Arrays ist das häufig der Fall. Wenn du einen Array nur schreibend wenig überläufst, dann merkst du das nicht, da die illegalen Zugriffe in diesen Füll-Bytes stattfinden. Erst wenn bei einem optimierten Build diese Füll-Bytes nicht mehr da sind, verändern sich dann plötzlich Variablen wie von Geisterhand * Oft übersieht man auch, dass laut Sprachstandard lokale Variablen nicht initialisiert werden. Bei Debug Builds ist es jedoch oft so, dass die Compiler die Variablen mit irgendeinem konstanten Wert initialisieren. Wie schon gesagt: Klar haben auch Optimizer Fehler. Genauso wie alle Compiler insgesammt den einen oder anderen Fehler haben. Aber in 99% aller Fälle ist es das vom Programmierer geschriebene Programm, und nicht der Compiler, das den eigentlichen Fehler enthält. Finden kannst du sowas meist nur mit einer Stufenmethode: Kommentiere aus deinem Programm alles aus und fange bei 0 an. Danach werden sukzessive Teilbereiche wieder aktiviert bis sich das erste mal wieder ein Fehler zeigt. Der Fehler muss nicht zwangsläufig im zuletzt aktivierten Bereich sein, oft ist er es aber. Zumindest liefert aber der zuletzt aktivierte Bereich Hinweise, wo den das eigentliche Problem sitzen könnte.
Das es nicht am Optimizer selber liegt war mir schon vorher klar, aber ich dachte mir das lesen mehr ;) Ich habe einen Pointer deklariert, aber nicht die Größe definiert hatte das im Programm stehen: char* s; itoa(*transmitData, s, 16); trotzdem finde ich es strange das der fehler ohne optimizer weder beim compilieren noch bei der programmausführung auftritt. ok, letzteres kann zufall sein, aber der compiler sollte doch in beiden fällen meckern. Ohne Optimizer ist der Compiler ohne Error oder Warning durchmarschiert und mit Optimizer kann dann die Meldung: warning: 's' is used uninitialized in this function
Auf welcher Platform willst du Kompilierten? Bei einem MCU lohnt sich meiner Erfahrung nach immer ein Blick auf den ASM-Code. Ich hatte auch schon einige Fehler bei denen ich den Comiler verflucht hab und nach dem ich den ASM-Code angeschaut hab ist mir klar geworden, dass der Kompiler das was ich da schreibe anders Interpretiert bzw. Sachen weckoptimiert die ich eigendlich brauch. Da kann man dann z.b. mit valatile eingreifen.
ne, ich hab die Deklarierung jetzt in char s[2] geändert. Jetzt gehts klaglos.
> trotzdem finde ich es strange Dann ist C nicht deine Sprache. Genau das sind Dinge, die ganz alleine deiner Verantwortung obliegen. Weder könnte der Compiler so einen Fehler in allen Fällen zu Compile-Zeit diesen Fehler detektieren noch wird (aus Performance-Gründen) zur Laufzeit ein Check eingebaut. Zur Laufzeit wäre es nämlich ebenso nicht möglich dieses Problem in allen Fällen zuverlässig zu identifizieren. In C ist ganz einfach deine Sorgfalt als Programmierer gefragt und C entlässt dich nicht aus dieser Verantwortung. > Ohne Optimizer ist der Compiler ohne Error oder Warning > durchmarschiert und mit Optimizer kann dann die Meldung Das liegt daran, dass auf deinem Compiler erst der Optimizer eine Datenfluss-Analyse macht und es dabei erst auffält, dass da was nicht stimmen kann. Was du daraus mitnehmen solltest: Warnungen ernst nehmen. Nicht umsonst gilt bei Industrieprogrammierern: Der Code muss in allen Optimierungsleveln, fehler- UND warnungsfrei comililiert werden können. Oft gibt es auch externe Tools die den Code auch noch auf häufige Programmierfehler durchchecken. 'lint' ist ein prominentes Beispiel dafür.
C ist auch nicht wirklich meine bevorzugte Sprache. Aber man wird ja geradezu dazu gezwungen C zu können weil es die einzige Sprache ist (von Assembler mal abgesehen) die es für fast jedes System gibt. Mittlerweile hab ich schon gehört das beim Compilieren ohne Optimizer auch einige Überprüfungen weggelassen werden um die Compilierung zu beschleunigen. Daher können also solche Unterschiede kommen. Ich gebe zu das ich weder ein C Freak bin noch unbedingt einer werden will, aber ich will es gut genug können um damit über die Runden zu kommen. Desshalb pack ich es hin und wieder mal aus
"trotzdem finde ich es strange das der fehler ohne optimizer weder beim compilieren noch bei der programmausführung auftritt." Die Pointer-Variable ist bei dir nicht initialisiert. Sowas merkt der Compiler, wie du ja gesehen hast. Aber das merkt er als Nebeneffekt einer Datenflussanalyse. Die kostet Zeit und wird eigentlich mit dem Ziel der Optimierung durchgeführt. Folglich kommt die Meldung nur bei eingeschalteter Optimierung. Und dass sich solcherart fehlerhafte Programme mit und ohne Optimierung völlig anders verhalten ist ganz normal. Ohne Optimierung landen Variablen immer im Speicher, mit Optimierung oft im Register, was bei fehlender Initialisierung gänzlich andere Werte zur Folge hat.
Ja, wie gesagt. Ich hab mittlerweile erfahren das der Compiler ohne Optimizer auch noch diverse andere Prüfungen weglässt. Man sollte also hin und wieder mal einen Compilerlauf mit Optimizer machen um auch solche Fehler frühzeitig zu finden
Irgendwann machst du solch läppische Fehler von Haus aus nicht mehr. Denn dann hast du dir das Ritual eingelernt, dich bei einem Pointer zuallererst mal zu fragen, ob und wo dieser Pointer hinzeigt und wenn du das nicht mehr im Kopf hast zu überprüfen ob mit dem Pointer alles mit rechten Dingen zugeht.
> Man sollte also > hin und wieder mal einen Compilerlauf mit Optimizer machen um auch > solche Fehler frühzeitig zu finden Meine ganz persönliche Meinung: man sollte immer mit Optimizer compilieren, andernfalls debugst du einen komplett anderen Maschinencode und debugst daher effektiv zweimal. Gewöhn dich stattdessen an die Artefakte der Optimierung (Variablen leben nicht mehr über die gesamte Lebensdauer einer Funktion bzw. wenn sie in Registern leben, werden diese anders wiederbelegt; Der Programmablauf kann ,,springen'', da der Compiler Code anders sortieren darf; nicht [mehr] benötigte Ergebnisse werden auch nirgends mehr gespeichert) und debugge gleich das Endergebnis.
"man sollte immer mit Optimizer compilieren" Bei GCC/ARM nicht uneingeschränkt zu empfehlen. Der Optimizer reorganisiert den Code derart, dass man bei Source-Code-Debugging bisweilen wild im Quellcode herumspringt.
Ist etwas Offtopic,aber naja... Was ist mit dem aktuellen DevC++ für Windows?Eigentlich eine prima Sache,nur leider ist es scheinbar nicht möglich einen Final-Build ohne Debug-Infos zu machen.Die Einstellungen der IDE bezüglich Debuginfo einbinden ignoriert das System scheinbar und wenn man den Compiler/Linker von der Commandozeile aufruft das selbe.Ein kleines Programm ist immer >450kb gross.Und selbst nach einem Strip-Durchlauf ist die Exe noch 2xxKB gross. Unter VC 6.0 konnte man immerhin ein Fenster mit einer 4kb Exe-Datei erzeugen (mit etwas Linker-Tuning)
> Der Optimizer > reorganisiert den Code derart, dass man bei Source-Code-Debugging > bisweilen wild im Quellcode herumspringt. Logisch, RISC halt. Deshalb ja auch mein "Get used to it.". Du debuggst eben sonst wirklich etwas völlig anderes, und damit ist die Sache ziemlich wertlos. Been there, done that. Motorola m88k. Auch eine echte RISC- Maschine. Da hüpfte der Zeiger im Emacs auch nur so auf und ab im Quellcode. Man gewöhnt sich dran, wenn man einmal weiß, wie das zusammenhängt. (In diesem Fall dürfte es das Ineinander- schachteln von Speicher- und Registeroperationen sein, da ein RISC-Prozessor typisch locker während eines Speicherzyklus noch eine komplette Registerrechnung mit abarbeiten kann.)
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.