frage zu if(bed1&&bed2&&bed3&&bed4&&..)



  • Ringding schrieb:

    EinGast schrieb:

    Also ich kann Dir da nicht zustimmen, wenn Du sagst "[diese Aussage] stimmt zwar, aber...", denn sie stimmt definitiv nicht. (Siehe mein anderes Posting.)

    Doch, sie stimmt.

    bool inline einpraedikat(double x) { return sin(x) > .1; }
    [...]
    if (<bedingung> && einpraedikat(x)) { dosomething(); }
    

    Nehmen wir an, dass der Compiler "sin" als eingebaute Funktion erkennt (was er darf). Weil sin besonders lange rechnet, möchte es der Compiler vielleicht möglichst weit nach vorne schieben. Daher schiebt er es einfach vor die Auswertung von <bedingung>. Du wirst nie etwas davon merken, ob einpraedikat(x) nun ausgerechnet wurde oder nicht, weil einpraedikat keine Nebeneffekte hat.

    nun sieh dir folgenden Code an, auch 2 "Funktionen" ganz ohne Nebeneffekte:

    if( pointer && *pointer == x )
    ...
    

    Das wäre ja furchtbar, wenn der Compiler hier den Zugriff auf *pointer nach vorne nähme.



  • Ringding schrieb:

    EinGast schrieb:

    Also ich kann Dir da nicht zustimmen, wenn Du sagst "[diese Aussage] stimmt zwar, aber...", denn sie stimmt definitiv nicht. (Siehe mein anderes Posting.)

    Doch, sie stimmt.

    *Seufz* Nein sie stimmt nicht. Ich denke, der C-Standard ist in diesem Fall wohl die verbindlichere Informationsquelle, als Deine (in diesem Fall eher unmaßgebliche) persönliche Meinung, meinst Du nicht auch? 🙂

    (eod) ⚠



  • DrGreenthumb schrieb:

    Das wäre ja furchtbar, wenn der Compiler hier den Zugriff auf *pointer nach vorne nähme.

    Ja. Vielleicht noch klarer:

    int function(int x) { int y = 1/x; return 1; }
    [...]
    if ((x != 0) && function(x)) { ... }
    

    Der Compiler kann doch unmöglich wissen, w a s die Funktion denn nun für jeden x-Wert tut oder nicht tut. Für x = 0 hat die Funktion aber einen etwas unschönen "Seiteneffekt". 🙂 (Natürlich könnte in der Funktion auch ein Pointer involviert sein, oder was auch immer.)



  • @DrGreenthumb: gutes Argument, aber der Compiler "weiß" ja, dass ein Pointerzugriff gefährlich sein kann, und dass er ihn deshalb nicht vorziehen darf. Der Compiler weiß auch, dass eine Division gefährlich ist.

    Konsultieren wir den C99-Draft (was anderes hab ich gerade nicht hier, es wird aber nicht wesentlich anders sein):

    An implementation might define a one-to-one correspondence between abstract and actual semantics: at every sequence point, the values of the actual objects would agree with those specified by the abstract semantics.
    Alternatively, an implementation might perform various optimizations within each translation unit, such that the actual semantics would agree with the abstract semantics only when making function calls across translation unit boundaries. In such an implementation, at the time of each function entry and function return where the calling function and the called function are in different translation units, the values of all externally linked objects and of all objects accessible via pointers therein would agree with the abstract semantics. Furthermore, at the time of each such function entry the values of the parameters of the called function and of all objects accessible via pointers therein would agree with the abstract semantics. In this type of implementation, objects referred to by interrupt service routines activated by the signal function would require explicit specification of volatile storage, as well as other implementation-defined restrictions.

    Also im Prinzip darf der Compiler machen, was er will, solange sich das Programm so verhält, wie es durch den Standard festgelegt wird.



  • Hierzu auch nocheinmal etwas aus deutschsprachigen Übersetzung von Steve Summits C-FAQ:

    4.4: Kann ich die Reihenfolge, in der Teilausdrücke ausgewertet werden,
    nicht durch Klammern festlegen? Selbst ohne Klammern sollte die
    Reihenfolge doch durch die Rangfolge der Operatoren vorgeschrieben
    sein.

    A: Die Hierarchie der Operatoren und Klammern legen nur zum Teil fest,
    in welcher Reihenfolge die Teilausdrücke ausgewertet werden. Im
    Beispiel

    f() + g() * h()

    ist nur sicher, dass die Multiplikation vor der Addition ausgeführt
    wird. Welche der drei Funktionen zuerst aufgerufen wird, ist
    allerdings undefiniert.

    Wenn die Reihenfolge, in der Teilausdrücke ausgewertet werden,
    wichtig ist, führt an temporären Variablen kein Weg vorbei.

    4.5: Und wie steht es mit den &&-, ||- und Komma-Operatoren? Ich sehe
    häufiger Ausdrücke der Form

    if((c = getchar()) == EOF || c == '\n') ...

    A: Für diese Operatoren (sowie für den ?:-Operator) gilt tatsächlich
    eine Ausnahme; Jeder einzelne dieser Operatoren stellt einen
    "sequence point" dar (d.h. der Ausdruck wird von links nach rechts
    ausgewertet). Dies sollte in jedem C-Buch klargestellt werden.

    Siehe hierzu: K&R I Abschn. 2.6 S. 38, Abschn. A7.11-12 S. 190-191;
    K&R II Abschn. 2.6 S. 41, Abschn. A7.14-15 S. 207-208;
    ANSI Abschn. 3.3.13 S. 52, 3.3.14 S. 52, 3.3.15 S. 53, 3.3.17 S.55;
    CT&P Abschnitt 3.7 Seiten 46-47



  • Du beachtest nur nicht, dass hier beschrieben wird, wie sich die abstrakte Maschine zu verhalten hat. Wie in meinem Quote nachzulesen ist, muss das aber nicht 1:1 auf die reale Maschine abgebildet werden.



  • Ringding schrieb:

    @DrGreenthumb: gutes Argument, aber der Compiler "weiß" ja, dass ein Pointerzugriff gefährlich sein kann, und dass er ihn deshalb nicht vorziehen darf. Der Compiler weiß auch, dass eine Division gefährlich ist.

    Ok, ich hatte vorhin gesagt EOD, aber jetzt reizt es mich doch nochmal, ein Argument in Anschlag zu bringen...

    Du sagst oben "der Compiler weiß" (dies und das)... Gut, gut. Bekanntlich (seit Turing) aber kann ein Compiler nicht alles wissen; vor allem kann er (prinzipiell) nicht wissen, ob eine Funktion (mit einem bestimmten Input) in eine Endlosschleife gerät, oder nocht! (Schön wär's ja, wenn Compiler d a s könnten!)

    Mit anderen Worten, es wäre, wenn der Compiler die Berechnung der Funktion vorzieht, völlig unbestimmt, ob er diese Berechnung je abschließt oder nicht. 🙂 [Es wäre also sehr "gefährlich", "komplexere" Funktionen vorzuziehen.]

    Nun magst Du einwenden, dass das sehr theoretisch ist. Ok.

    Nehmen wir nun aber mal das wesentlich realistischere Beispiel, dass die Funktion einfach eine relativ lange LAUFZEIT hat! Dann würde das einen MERKBAREN Unterschied machen, ob die Funktion vorgezogen wird oder nicht; auch dann wenn keine Seiteneffekte (im eigentlichen Sinne) auftreten: die Laufzeit ist allerdings für den Benutzer ein durchaus realer Effekt! Ich behaupte nun, dass dieser Effekt nicht auftreten DÜRFTE, wenn der Compiler dem Standard entsprechend implementiert ist.

    bool inline wait(double x) { Zählschleife // ca. 1 Sek. return 1; }
    [...]
    if (<bedingung> && wait(x)) { dosomething(); }
    

    Jetzt sag bitte nicht, der Compiler erkennt auch eine Zählschleife als solche, usw. usf - wir reden hier ja nicht über AI, sondern über einen C-Compieler; ich denke der hat anderes zu tun, als Funktionen auf ihre (umfassende) Funktion hin zu analysieren. 😉

    (Es muss noch nicht mal eine Zählschleife sein. Deine sin()-Funktion tut's im Grunde auch: Du selbst hast gesagt, dass ihre Berechnung relativ lange dauert. Und "ich" behaupte nun, dass dise Funktion eben erst gar nicht "evaluiert" wird, wenn die erste Bedingung nicht erfüllt ist, und so stehts auch im Standard.)



  • Bei deinem letzten Post gebe ich dir schon recht - das kann natürlich nur für ganz simple Sachen funktionieren und ist überhaupt sehr hypothetisch. Der Compiler muss auch den Inhalt der Funktion vollständig kennen, die ausgewertet wird.

    Trotzdem wäre das mit dem sin denkbar, denn moderne Prozessoren sind ja problemlos in der Lage, weiterzuarbeiten, während im Hintergrund die langwierige Sinusberechnung läuft. Vermutlich macht es auch nur dann Sinn, ihn vorzuziehen, wenn er soundso "fast immer" ausgeführt wird (diese Daten stehen ja bei Profile Guided Optimization zur Verfügung).



  • Ringding schrieb:

    Konsultieren wir den C99-Draft (was anderes hab ich gerade nicht hier, es wird aber nicht wesentlich anders sein):

    An implementation might define a one-to-one correspondence between abstract and actual semantics: at every sequence point, the values of the actual objects would agree with those specified by the abstract semantics.
    Alternatively, an implementation might perform various optimizations within each translation unit, such that the actual semantics would agree with the abstract semantics only when making function calls across translation unit boundaries. [...]

    Hmmm... ja...

    Dann aber heißt es da wiederum:

    Unlike the bitwise binary & operator, the && operator
    guarantees left-to-right evaluation; there is a sequence
    point after the evaluation of the first operand. If the
    first operand compares equal to 0, the second operand is not
    evaluated.

    Du meinst also, dass sich dies auf die "abstract semantics" bezieht. Aber ich kann mir nicht helfen...: Wenn der Compiler dann DOCH, wie Du sagst, die Berechnung der Funktion vorziehen kann, dann wirklich nur in dem Fall, dass auch garantiert GAR KEINE Seiteneffekte auftreten. Also so, dass es für den Programmablauf so ist, ALS OB die Funktion nicht aufgerufen worden wäre. (Das Laufzeitverhalten würde das allerdings u.U. ans Tageslicht bringen!)

    Ein Compiler müsste das also auch immer überprüfen, dass keine Seiteneffekte auftreten (und er müsste das perfekt können) ...Hmmm... (Man bedenke mal, wieviel der Compiler über Mathematik wissen müsste, um entscheiden zu können, welche mathematischen Funktionen nun "gefährlich" sind, und welche nicht, usw. usf.)

    Ein bissl "verrückt" ist das natürlich schon, wenn es einerseits im Standard heißt: "If the first operand compares equal to 0, the second operand is not
    evaluated." Andererseits

    if(0 && wait_some_time()) {...}

    u.U. dazu führt, dass das Programm tatsächlich eine Pause einlegt... 😞

    Kopfschüttel

    Was haben sich die Leute, die den Standard verbrochen haben, nur dabei gedacht... 😮



  • Ringding schrieb:

    [...] Vermutlich macht es auch nur dann Sinn, ihn vorzuziehen, wenn er soundso "fast immer" ausgeführt wird (diese Daten stehen ja bei Profile Guided Optimization zur Verfügung).

    Ja, verstehe, was Du meinst (habe ich mir gerade auch schon überlegt gehabt...).

    Was mich jetzt noch interessieren würde: Wie sieht es wohl damit in der PRAXIS aus, welche Compiler weichen hier u.U. wirklich von der "abstract semantics" ab (und unter welchen Umständen).

    Wenn es wirklich so ist, wie Du sagst, dann habe ich in jedem Fall wieder was dazugelernt! 🙂


Anmelden zum Antworten