Warum sollte man keine versteckten Anweisungen in logischen Ausdrücken ausführen lassen?



  • #include <iostream>
    #include <limits>
    
    #define printMinMaxT( TYPE ) \
      std::cout << #TYPE << std::endl; \
      std::cout << "min: " << std::numeric_limits<TYPE>::lowest() << std::endl; \
      std::cout << "max: " << std::numeric_limits<TYPE>::max() << std::endl;
    
    int main() {
      printMinMaxT(short)
      return 0;
    }
    
    short
    min: -32768
    max: 32767
    

    🙂

    Nochmal von vorne (edit3 - jetzt aber).
    Es ist nur kein Überlauf da implizit auf int gecastet wird.
    Mit folgenden Zeilen kommt es zum Überlauf:

    short test = 32767;
      std::cout << short(test + 1) << std::endl; // -32768  !!!
      std::cout << short(test++) << std::endl;   //32767
    


  • Beachtet aber nebenbei dass signed überläufe undefiniert sind 😉



  • roflo schrieb:

    Beachtet aber nebenbei dass signed überläufe undefiniert sind 😉

    Ja, ein wunderbar schwachsinniges Relikt...



  • Sind die nicht mittlerweile implementation defined?
    Ich merk mir das nie...



  • Shade Of Mine schrieb:

    Ich sehe da keinen Überlauf.

    Beachte ++n vs n++

    if (n > 100 && n++ > 100)
    

    vs.

    if(n > 100 && n+1 > 100){ 
        n++;
    

    Und die haben nur dann das selbe Verhalten, wenn es bei n+1 keinen Überlauf gibt.


  • Mod

    hustbaer schrieb:

    Sind die nicht mittlerweile implementation defined?
    Ich merk mir das nie...

    Nein, es ist nach wie vor undefined. It's not a bug, it's a feature. So kann man signed Typen aggressiver optimieren als unsigned Typen. In diesem konkreten Beispiel ( if (n>100 && n+1>100) ) kann beispielsweise der Test mit n+1 wegoptimiert werden, wenn n signed ist. Denn man kann davon ausgehen, dass n+1 > n ist. Und wenn es doch zu einem Überlauf kommt, ist es eben undefiniertes Verhalten. Bei einem unsigned int müssen hingegen die Rechnung und der Test tatsächlich ausgeführt werden.

    Das sieht in diesem konkreten Fall natürlich ziemlich schwachsinnig aus, das liegt aber da dran, dass das Beispiel nicht viel her gibt. Es gibt sicherlich bessere Beispiele.



  • Nein, es wurde aus dem Bug ein zweifelhaftes "Feature" gemacht.


  • Mod

    Der echte Tim schrieb:

    Nein, es wurde aus dem Bug ein zweifelhaftes "Feature" gemacht.

    Quelle? Begründung?



  • Du glaubst nicht ernsthaft, dass signed overflow im Standard undefined ist damit Compilerbauer hier "optimieren" können?


  • Mod

    Der echte Tim schrieb:

    Du glaubst nicht ernsthaft, dass signed overflow im Standard undefined ist damit Compilerbauer hier "optimieren" können?

    Doch. Wenn es um 1-Komplement vs. 2-Komplement ginge, hätten sie implementation defined geschrieben. Durch undefined sind ziemlich gute Optimierungen möglich, vor allem was die Lauflänge von Schleifen angeht.



  • Wäre das tatsächlich der Grund, dann wäre es aber nur konsequent wenn unsigned overflow auch undefined wäre.

    Zumal man sich damals über derartige Optimierungen ziemlich sicher keine Gedanken gemacht hat. Denn dann hätte man Dinge wie restrict auch direkt eingeführt. Nein, damals hat man andere Sorgen gemacht, z.B. das (zum Glück mittlerweile entfernte) 31 Zeichen-Limit von Bezeichnern.



  • Was damals der Grund war es ursprünglich so zu machen ist die eine Frage.

    Was der Grund war weswegen es mit C++11 bzw. C++14 nicht geändert wurde ist eine andere Frage.

    Kann der selbe Grund sein, muss aber nicht.



  • NineT4 schrieb:

    Sorry Es sollte so aussehen:
    ich habe "n" an Stelle von "m" geschrieben

    if (n > 100 && m++ > 100){
       //tue etwas
    }
    
    if(n > 100 && m+1 > 100){ 
        m++; 
        //tue etwas 
    }
    

    Das ist nicht das gleiche.

    if (n > 100 )
       m++
       if ( m > 100)
       {
          //tue etwas
       }
    }
    

    Das ist wie das erste Beispiel, aber ganz einfach lesbar.



  • - signed overflow war schon immer UB (im Gegensatz zur signed (integralen) promotion, die schon immer IB war)
    - unsigned overflow war schon immer defined (wrap around), was ich persönlich für den praktischen Gebrauch für Unsinn halte, da hier Überläufe (stillschweigend und standardkonform!) verschleiert werden; ich möchte nicht wissen, wieviele Software in kritischen Bereichen im Umlauf ist, die dieses "Stillschweigen" aus Sorg- oder/und Ahnungslosigkeit "verwenden"
    - der Standard macht (naturgemäß) keine Vorgaben für Implementierungsvarianten geschweige denn Optimierungen
    - der Standard sieht aber sehr wohl prinzipielle Möglichkeiten vor, um solche fragilen Konstrukte sichtbar zu machen, er spricht z.B. von "the result is implementation-defined or an implementation-defined signal is raised" und bei UB spricht er sogar explizit davon, dass eine Implementierung (also ein Compiler) eine UB in eine constraint-violation umwandeln darf, die dann wiederum standardgemäß auch ein "signal" in irgendeiner Form von sich geben muss



  • lesenkönnen schrieb:

    if (n > 100 )
       m++
       if ( m > 100)
       {
          //tue etwas
       }
    }
    

    Das ist wie das erste Beispiel, aber ganz einfach lesbar.

    Mal abgesehen davon dass ein ";" fehlt...

    Nein, ist nicht "wie das erste Beispiel".
    Das Thema war aber eigentlich schon durch.



  • hustbaer schrieb:

    lesenkönnen schrieb:

    if (n > 100 )
       m++
       if ( m > 100)
       {
          //tue etwas
       }
    }
    

    Das ist wie das erste Beispiel, aber ganz einfach lesbar.

    Mal abgesehen davon dass ein ";" fehlt...

    Nein, ist nicht "wie das erste Beispiel".
    Das Thema war aber eigentlich schon durch.

    Und eine { fehlt auch.
    Aber

    #include <iostream>
        using namespace std;
    
        int main() {
        	int n = 180;
        	int m = 80;
        	if (n > 100 && m++ > 100){
           		//tue etwas
        	}
        	std::cout << "n: " << n << " m: " << m;
        	// your code goes here
        	return 0;
        }
    

    n: 180 m: 81

    #include <iostream>
        using namespace std;
    
        int main() {
        	int n = 180;
        	int m = 80;
        	if(n > 100 && m+1 > 100){
        	   m++;
            	//tue etwas
        	}
        	std::cout << "n: " << n << " m: " << m;
        	// your code goes here
        	return 0;
        }
    

    n: 180 m: 80

    #include <iostream>
        using namespace std;
    
        int main() {
        	int n = 180;
        	int m = 80;
        	if (n > 100 ) {
           		m++;
           		if ( m > 100)
           		{
              		//tue etwas
           		}
        	}
        	std::cout << "n: " << n << " m: " << m;
        	// your code goes here
        	return 0;
        }
    

    n: 180 m: 81

    Der Compiler macht doch aus jedem && wieder ein geschachteltes if.
    Zeigt ein Beispiel wo bei 1 und 3 nicht das gleiche raus kommt.



  • n = 101
    m = 100
    Wird "tue etwas" ausgeführt oder nicht?
    Im Original nicht.
    In deiner Variante schon.

    Hatten wir aber wie gesagt alles schon durch.
    https://www.c-plusplus.net/forum/p2478891#2478891



  • Igitt, wer hat denn so einen Bug in die Sprache eingebaut? Irgendwas links und rechts vom Vergleichsoperator nach dem Vergleich auszuführen ist doch total kontraintuitiv.



  • Das hat doch mit dem Vergleich nix zu tun.

    Ist halt die Semantik von n++ . Post increment halt.
    Auch ganz ohne if:

    int x = 100;
    printf("%d\n", x++); // Schreibt 100 raus
    printf("%d\n", x);   // Schreibt 101 raus
    


  • Gibt es ein Beispiel wo man Post increment sinnvoll braucht?


Anmelden zum Antworten