finde den Fehler
-
SeppJ schrieb:
camper schrieb:
SeppJ schrieb:
Einen Fehler sehe ich aber immer noch nicht.
Hast du doch schon angesprochen:
Falls__is >> __chfehlschlägt, hat __ch weiter einen unbestimmten Wert, die folgende Abfrage wäre also UB.
Laut Standard vielleicht, der Implementierer des GCC weiß aber ganz genau (und wir ebenfalls), dass der Computer nicht wirklich die Festplatte löschen wird, sondern bloß gegen irgendeinen Müllwert vergleicht. Da er beide Pfade abgedeckt hat (entweder ist das Zeichen zufällig '(' oder eben nicht), kann also nichts passieren.
Du bist also gcc-Entwickler? Für mich sieht das erher so aus als ob der Compiler auf die Idee kommen könnte, den Extraktor zu inlinen und dann zu entscheiden, dass der Kontrollpfad, der Fehlschlag anzeigt, nie betreten werden wird, folglich eliminiert werden kann, weil ja andernfalls UB die Folge wäre.
Wenn das bis jetzt noch nicht passiert ist, dann vielleicht nur, weil noch niemand die richtige Kombination aus -O99 -funroll-everything und -finline-limit=infinite gefunden hat.I.d.R. wird der Code sicher trotzdem funktionieren, allerdings wird valgrind damit nicht glücklich.
valgrind defineirt aber nicht, was ein Fehler ist oder nicht, sondern im Zweifelsfalle die korrekte Funktion unter allen Umständen. Diese ist gegeben, sofern man den Code nur mit dem GCC (oder einem anderen nicht-geisteskranken Compiler) übersetzt.[/quote]Das kommentiere ich jetzt mal nicht.
SeppJ schrieb:
Die Zeichextraktoren verhalten sich bei Fehler anders als die arthmetischen Extraktoren, denn im Fehlerfall gibt num_get im Zweifel 0 zurück.
Hmm, eigentlich lese ich den Standard so, dass bei char-Typen keine Änderung erfolgt:
genau.
-
Das selbe Problem gibt's dann nochmal mit
__re_xim äusserenelseZweig.
-
hustbaer schrieb:
Das selbe Problem gibt's dann nochmal mit
__re_xim äusserenelseZweig.Das ist dann aber ein arithmetischer Extraktor, der num_get aufruft, und dabei kommt dann 0 heraus.
Ich habe jetzt erst mal mit+ if (__is.fail()) + { + __x = _Tp(); + } + else if (__ch == '(') - if (__ch == '(')gepatcht, und das beseitigt das Problem fürs Erste.
__ch zu intialisieren dürfte auch helfen.
-
camper schrieb:
hustbaer schrieb:
Das selbe Problem gibt's dann nochmal mit
__re_xim äusserenelseZweig.Das ist dann aber ein arithmetischer Extraktor, der num_get aufruft, und dabei kommt dann 0 heraus.
ich = Brett vorm Kopf
Das mit num_get hattest du ja schon geschrieben. Ich hab's auch gelesen und mir gedacht "aha, sehr interessant, jaja *mit-dem-kopf-nicke*".
10 Sekunden später war's anscheinend wieder weg *g*.Naja, danke für die wiederholte Erklärung

-
camper schrieb:
Für mich sieht das erher so aus als ob der Compiler auf die Idee kommen könnte, den Extraktor zu inlinen und dann zu entscheiden, dass der Kontrollpfad, der Fehlschlag anzeigt, nie betreten werden wird, folglich eliminiert werden kann, weil ja andernfalls UB die Folge wäre.
Wage ich zu bezweifeln.
Der Compiler "denkt" ja nicht in UB oder nicht UB. Weil UB ist ja nur ein Standardausdruck. UB gibt es ja nicht. Es ist immer definiert was passiert.Auch wenn man uU alle Variablen kennen muss um es vorauszusagen.
-
Shade Of Mine schrieb:
camper schrieb:
Für mich sieht das erher so aus als ob der Compiler auf die Idee kommen könnte, den Extraktor zu inlinen und dann zu entscheiden, dass der Kontrollpfad, der Fehlschlag anzeigt, nie betreten werden wird, folglich eliminiert werden kann, weil ja andernfalls UB die Folge wäre.
Wage ich zu bezweifeln.
Der Compiler "denkt" ja nicht in UB oder nicht UB. Weil UB ist ja nur ein Standardausdruck. UB gibt es ja nicht. Es ist immer definiert was passiert.Auch wenn man uU alle Variablen kennen muss um es vorauszusagen.
Der Compiler "denkt" in impliziten Nebenbedingungen.
Inint x; cin >> x; if ( x * x >= x ) cout << x; else cout << "Überlauf";"weiß" der Compiler, dass die Bedingung überflüssig ist, und wird sie eliminieren.
Und - um auf den ursprünglichen Code zurückzukommen - durch statische Analyse zu entdecken, dass in einem Ausführungspfad auf eine nicht initialisierte Variable zugegriffen wird, ist in solchen relative einfachen Fällen durchaus möglich.
-
Ein schlechts Beispiel. Was ist wenn x hinreichend groß ist und ein Overflow entsteht?
Und die Ausgabe Overflow ist auch blödsinn, weil bei x*x so zu sagen mehrere Overflows entstehen können
-
Ramanujan schrieb:
Ein schlechts Beispiel. Was ist wenn x hinreichend groß ist und ein Overflow entsteht?
Dann hat man undefiniertes Verhalten.
Das heißt im Klartext:
entweder es gibt keinen Überlauf, dann ist x*x >=x.
oder es gibt undefiniertes Verhalten, dann kann alles passieren, z.B. x ausgegeben werden oder das übergelaufenene Etwas als >= x gelten. Zusammengenommen ist es völlig legitim, wenn in jedem Fall x ausgegeben wird, also kann der Optimizer sich die Abfrage und den else-Zweig sparen und völlig standardkonform bleiben.
-
Ramanujan schrieb:
Ein schlechts Beispiel. Was ist wenn x hinreichend groß ist und ein Overflow entsteht?
Das ist es, worauf camper hinauswill. Ein arithmetischer Überlauf ist an der Stelle undefiniertes Verhalten, deshalb darf der Compiler diesen Fall ignorieren und so tun, als gäbe es hier nie Überlauf. Er darf also annehmen, dass die Bedingung immer true ist.
-
Hm, ok. Ist dann auch sowas undefiniert:
unsigned u = -1;
Oder kann man davon ausgehen, dass in u die Zahl 2^32 - 1 steht, falls int 32 bit groß ist?
-
camper schrieb:
Der Compiler "denkt" in impliziten Nebenbedingungen.
Ok, ich kann mir vorstellen wie du das theoretisch meinst - ich tue mir lediglich schwer an sowas in der Praxis zu glauben. Denn es setzt ein ziemlich starkes reorganisieren des codes voraus. (ohne dabei performance gewinn zu haben)
Dennoch sollte man es fixen.
-
Ramanujan schrieb:
Hm, ok. Ist dann auch sowas undefiniert:
unsigned u = -1;
Oder kann man davon ausgehen, dass in u die Zahl 2^32 - 1 steht, falls int 32 bit groß ist?
Unsigned Datentypen folgen garantiert der Artihmetik modulo der größten darstellbaren Zahl + 1. Es ist also definiert, dass hier UINT_MAX heraus kommt.
Shade Of Mine schrieb:
camper schrieb:
Der Compiler "denkt" in impliziten Nebenbedingungen.
Ok, ich kann mir vorstellen wie du das theoretisch meinst - ich tue mir lediglich schwer an sowas in der Praxis zu glauben. Denn es setzt ein ziemlich starkes reorganisieren des codes voraus. (ohne dabei performance gewinn zu haben)
Vor allem sind da gleich mehrere, in der Regel externe, Funktionsaufrufe drin, an denen der Compiler nicht vorbei kommen kann. Ja, man kann sich einen eigenen Stream schreiben, bei dem alles inline ist, dann greift wieder Shade Of Mines Einwand.
Dennoch sollte man es fixen.
Das würde ich als einen Fehler ansehen. Der Code wird unter allen Umständen korrekt funktionieren. Man würde also einen kleinen Performanceverlust einbauen, bloß um eine Warnung in einem externen Programm los zu werden. Das richtige Vorgehen wäre wohl eher, eine weitere Ausnahme zu valgrind hinzu zu fügen. Es gibt schließlich schon genügend unterdrückte Warnungen für die Standardbibliothek.
-
SeppJ schrieb:
Shade Of Mine schrieb:
camper schrieb:
Der Compiler "denkt" in impliziten Nebenbedingungen.
Ok, ich kann mir vorstellen wie du das theoretisch meinst - ich tue mir lediglich schwer an sowas in der Praxis zu glauben. Denn es setzt ein ziemlich starkes reorganisieren des codes voraus. (ohne dabei performance gewinn zu haben)
Vor allem sind da gleich mehrere, in der Regel externe, Funktionsaufrufe drin, an denen der Compiler nicht vorbei kommen kann. Ja, man kann sich einen eigenen Stream schreiben, bei dem alles inline ist, dann greift wieder Shade Of Mines Einwand.
So viel muss man da gar nicht inlinen: (gcc 4.6.3)
template<typename _CharT, typename _Traits> basic_istream<_CharT, _Traits>& operator>>(basic_istream<_CharT, _Traits>& __in, _CharT& __c) { typedef basic_istream<_CharT, _Traits> __istream_type; typedef typename __istream_type::int_type __int_type; typename __istream_type::sentry __cerb(__in, false); if (__cerb) { ios_base::iostate __err = ios_base::goodbit; __try { const __int_type __cb = __in.rdbuf()->sbumpc(); if (!_Traits::eq_int_type(__cb, _Traits::eof())) __c = _Traits::to_char_type(__cb); else __err |= (ios_base::eofbit | ios_base::failbit); } __catch(__cxxabiv1::__forced_unwind&) { __in._M_setstate(ios_base::badbit); __throw_exception_again; } __catch(...) { __in._M_setstate(ios_base::badbit); } if (__err) __in.setstate(__err); } return __in; }Es genügt diesen Operator zu inlinen. __c ist garantiert alias-frei (weil es eine lokale automatische Variable des Aufrufers ist), damit spielt es für das Problem keine Rolle, was evtl. aufgerufene externe Funktionen tun.
Dennoch sollte man es fixen.
Das würde ich als einen Fehler ansehen. Der Code wird unter allen Umständen korrekt funktionieren. Man würde also einen kleinen Performanceverlust einbauen, bloß um eine Warnung in einem externen Programm los zu werden. Das richtige Vorgehen wäre wohl eher, eine weitere Ausnahme zu valgrind hinzu zu fügen. Es gibt schließlich schon genügend unterdrückte Warnungen für die Standardbibliothek.
Die Fehlermeldung ist lediglich Anlass, nicht Grund für die Behebung. Es ist schlicht nicht beweisbar, dass dieser Code immer funktionieren wird, und es kann ein Modus angegeben werden, mit dem er fehlschlagen könnte. Die notwendige Korrektur ist einfach und überschaubar. Das sollte als Änderungsgrund reichen. Und Performance muss dabei nicht zwingend verloren gehen.
/edit pumuckl: fehlernder quote-Tag