Hallo,
ich habe eine Frage zu folgender if-Abfrage
1
if(a()||b()==0)
2
{
3
...
4
}
wenn die Funktion a() 1 Zurück gibt, wird die funktion b() noch
ausgeführt?
Wenn ja/nein, ist das immer so? Oder liegt das am Compiler (oder
Einstellungen)?
ich habe b() immer in einer neuen if-Abrfage gepackt
1
if(!a())
2
{
3
if(0==b())
4
{
5
...
6
}
7
}
aber den oberen Weg auch schon öfters im Code gefunden
Marcel schrieb:> Wenn ja/nein, ist das immer so? Oder liegt das am Compiler (oder> Einstellungen)?
Das ist im C/C++-Standard so festgelegt. Solange du also einen
C/C++-Compiler benutzt, ist das immer so.
Oliver
Dirk B. schrieb:> In C und C++ ist das im Standard definiert.Oliver S. schrieb:> Das ist im C/C++-Standard so festgelegt. Solange du also einen> C/C++-Compiler benutzt, ist das immer so.
danke
Schlaumaier schrieb:> Mach einen vernünftige AND bzw. OR Abfrage wie sich das gehört.
was wäre denn vernünftig?
Schlaumaier schrieb:> Mach einen vernünftige AND bzw. OR Abfrage wie sich das gehört.
Er macht das vernünftig.
Du hättest in diesem Thread mal wieder die Chance gehabt, deinem Namen
nicht alle Ehre zu machen.
Vielleicht beim nächsten Mal?
Und weil if kein Funktionsaufruf ist, gehört zwischen das f und die
öffnende Klammer ein Leerzeichen. (Ja, syntaktisch spielt es keine
Rolle, schon klar. Aber konsequent ist es.)
</klugschiss>
Eben wegen der bedingten Ausführung ist die Ausführung von Funktionen -
abgesehen vom 1. Argument - mit Vorsicht zu genießen: Wenn die Funktion
nicht nebenwirkungsfrei ist, kann man sich da schwer zu findende
Programmfehler einbauen. Deshalb verbietet z.B. MISRA solche Konstrukte.
Der Workaround wäre dann entweder, den Rückgabewert von b() vorher einer
lokalen Variable zuzuweisen und diese für die Auswertung zu verwenden.
Marcel schrieb:> if(a() || b()==0)> {> ...> }
....
> if(!a())> {> if(0==b())> {> ...> }> }
Auch hier wird b() nicht ausgeführt, wenn a() wahr ist....
Evtl. so:
if (a() + b()) {....
Nachdenklicher schrieb:> Und weil if kein Funktionsaufruf ist, gehört zwischen das f und> die öffnende Klammer ein Leerzeichen.
Aber bei der if-Schleife... duck ^^
So rein der Vollständigkeit halber:
Wikipedia weiß (https://de.wikipedia.org/wiki/Kurzschlussauswertung):
> In der Programmiersprache C werden binäre boolesche Ausdrücke mit den Operatoren
&& und || ausschließlich nach dem Kurzschluss-Prinzip ausgewertet.
> In der Programmiersprache Java stehen die Operator && und || ebenfalls für eine
Kurzschlussauswertung.
> Zusätzlich existieren jedoch die logischen Operatoren & und |, bei denen die
Auswertung beider Seiten erzwungen wird.
Und in Delphi gibt es eine Compilerdirektive, mit der zwischen
Kurzschlussauswertung und "vollständiger" Auswertung umgeschaltet werden
kann.
https://docwiki.embarcadero.com/RADStudio/Sydney/en/Boolean_short-circuit_evaluation_(Delphi_compiler_directive)
Nachdenklicher schrieb:> nd weil if kein Funktionsaufruf ist, gehört zwischen das f und die> öffnende Klammer ein Leerzeichen. (Ja, syntaktisch spielt es keine> Rolle, schon klar. Aber konsequent ist es.)> </klugschiss>
Wer empfiehlt denn sowas? (ich hab nichts dagegen, aber also
"begründete" Empfehlung hab ich noch nie davon gehört)
A. S. schrieb:> Wer empfiehlt denn sowas? (ich hab nichts dagegen, aber also> "begründete" Empfehlung hab ich noch nie davon gehört)
Mindestens einer von den drölfzig verschiedenen Style Guides, die es für
C gibt. Hab die Quelle gerade nicht zur Hand. Die Begründung lautete
auch nicht "if ist keine Funktion", sondern daß vor einer öffnenden
Klammer eines Funktionsaufrufs kein Leerzeichen steht, aber zwischen
einem Schlüsselwort und öffnender Klammer eines Ausdrucks. Und das
trifft auf if (bedingung) ja zu.
Aber gefühlt gibt es ohnehin n+1 verschiedene Stile für C, wobei n die
Anzahl der Personen, die seit Erfindung der Sprache diese genutzt haben
ist. ;-)
Klaus W. schrieb:> Schlaumaier schrieb:>> Mach einen vernünftige AND bzw. OR Abfrage wie sich das gehört.>> Er macht das vernünftig.
JA aber er versteht die eigene Abfrage nicht.
Im Code darunter steht. :
Marcel schrieb:> ich habe b() immer in einer neuen if-Abrfage gepacktif(!a())> {> if(0==b())> {> ...> }> }
Oben macht er eine ODER Abfrage und darunter nochmal eine Spezifische
Abfrage.
Das kann Programm technisch sogar sinnvoll sein. Aber die Aussage
Marcel schrieb:> ich habe b() IMMER in einer neuen if-Abrfage gepacktif(!a())
Ist mist.
Sinn macht das nur wenn man A ODER B überprüft und wenn EINER der
Beiden Bedingungen TRUE / Wahr ist, man in der 2 Abfrage, spezifiziert
will, WENN die B - Bedingung wahr ist, man noch etwas anders macht.
Deshalb meine Aussage. So einfach wie IF-THEN Abfragen sind, so
kompliziert
kann es sein, die richtige Reaktion zu erzeugen.
Erklärung.
Führerschein = false
Nummernschild = keins
Wenn Fahrzeug = AUTO und Speed > 25 dann
Führerschein = True
Nummernschild = klein
Wenn Speed > 50 dann
Nummernschild = groß
end if
end if
Das ist eine vernünftig Abfrage. Wenn das Fahrzeug ein Fahrrad ist
fällt die Abfrage komplett aus.
Ist sogar richtig lt. STVO. Nur bekommt man diese Autos nicht mehr
zugelassen ;)
Marcel schrieb:> was wäre denn vernünftig?
Vernünftig ist, unnötigen Seiteneffekten aus dem Wege zu gehen, auch
wenn es hie und da mal eine zusätzliche Zeile im Quelltext erfordert.
W.S.
W.S. schrieb:> Vernünftig ist, unnötigen Seiteneffekten aus dem Wege zu gehen, auch> wenn es hie und da mal eine zusätzliche Zeile im Quelltext erfordert.
JEPP.
Code muss einfach und übersichtlich bleiben. Weshalb ich
Verbunds-Abfragen (zu viel AND/OR in einer Zeile) fast immer vermeide.
Lieber mehre Blöcke oder alternativ eine Select CASE abfrage. (k.a. wie
die in C heißt).
Macht den Code länger aber ich muss mir nicht alle Verwusselungen
ausdenken.
Schlaumaier schrieb:> Ist mist.
Nein. Wieso?
Der TO kannte das nicht und machte das gleiche mit 2 ifs. Jetzt kennt er
es und macht es künftig wie alle anderen.
Wie mit Klammerregeln: Am Anfang setzt man viele, einige
("offensichtlich unnötige") spart man sich später.
A. S. schrieb:> Wie mit Klammerregeln: Am Anfang setzt man viele, einige> ("offensichtlich unnötige") spart man sich später.
Die Klammerregeln sind der Grund wieso ich C hasse wie die Pest.
Ansonsten ist das nämlich alles das selbe.
Schlaumaier schrieb:> Die Klammerregeln sind der Grund wieso ich C hasse wie die Pest.
Warum hältst Du dann nicht bei Themen, von denen Du keine Ahnung hast,
einfach die Klappe?
Also in Deinem Fall bei allen Themen.
Schlaumaier schrieb:> A. S. schrieb:>> Wie mit Klammerregeln: Am Anfang setzt man viele, einige>> ("offensichtlich unnötige") spart man sich später.>> Die Klammerregeln sind der Grund wieso ich C hasse wie die Pest.>
Warum? Lieber eine Klammer zu viel, wenn sie richtig ist. Größer wird
der entstehende Code dadurch nicht. Später, wenn man sich besser mit dem
Regeln auskennt, kann man immer noch einsparen...
Man sollte in einer if Bedingung auch keine Funktionsaufrufe, welche auf
globale Variablen wirken einbauen. Sodass der Aufruf allein etwas
bewirkt. Das ist ganz schlechter stil. Schwierig debugbar.
if (Mach() ==0) OR TerminateThread()==0) { .. }
Einmal laeuft der Thread noch, das andere Mal nicht..
Teo D. schrieb:> Evtl. so:> if (a() + b()) {...
oder auch so ...
und dann wird a()/b() immer ausgeführt und die short logic umgangen!
if (a() | b()) {
...
}
Apollo M. schrieb:> besser so ...> und dann wird a()/b() immer ausgeführt und die short logic umgangen!>> if (a() | b()) {> ...> }
Nein. Nein. Nein.
Dann macht man den Aufruf der Funktionen vor dem if und speichert das
Ergebnis in lokalen Variablen.
Der Compiler optimiert das dann und es bleibt lesbar.
Mit & statt && kann man sonst mächtig auf die Schnauze fallen.
Dirk B. schrieb:> Mit & statt && kann man sonst mächtig auf die Schnauze fallen.
Deshalb steht da ja auch ein |
Ist natürlich trotzdem ein Hack, aber erlaubt.
Oliver
Die Kurzschlussauswertung ist ganz sicher kein Seiteneffekt oder
irgendein Obfuscationsthema. Sequencepoints entstehen nicht einfach so.
Es macht wenig Sinn, ein effizientes Konstrukt zu diffamieren, weil
Anfänger Operatoren verwechseln und deren Wirkung verwechseln könnten.
Dirk hat ein Beispiel gebracht:
Dirk B. schrieb:> Damit ist dann auch> if(a && *a) {> möglich, wenn a = NULL ist.
Z.B. *p++ ist für Anfänger noch verwirrender: Intuitiv nimmt man (von
links nach rechts) (*p)++ an. Dann lernt man das und nutzt es auch. Das
schöne an C: Sooo viele Konstrukte gibt es nicht.
Oliver S. schrieb:> Ist natürlich trotzdem ein Hack, aber erlaubt.
Erlaubt ist in C viel - darum ist C auch für Profis gemacht
Es zu nutzen, macht einen aber nicht zu einem Profi.
Dirk B. schrieb:> Erlaubt ist in C viel - darum ist C auch für Profis gemacht> Es zu nutzen, macht einen aber nicht zu einem Profi.
Es hobbymäßig zu nutzen führt auch nicht unbedingt zum Desaster :)
Ich kenne viele Hobbyprogrammierer, die ganz gut mit C zurecht kommen.
Ausnahmen gibt es natürlich immer, auch bei den Profis.
Dirk B. schrieb:> Erlaubt ist in C viel - darum ist C auch für Profis gemacht> Es zu nutzen, macht einen aber nicht zu einem Profi.
Das gilt für jede existierende Programmiersprache (außer brainfuck und
ähnliche). Wo ist das Argument?
Oliver
Marcel schrieb:> if(!a())> 2{> 3 if(0==b())> 4 {> 5 ...> 6 }> 7}
Dieser Code hat aber damit nichts zu tun:
Marcel schrieb:> if(a() || b()==0)> 2{> 3 ...> 4}
"wenn a oder b" ist nicht das Gleiche wie "wenn nicht a aber b".
Jonas B. schrieb:> Bei c, wird Funktion b nicht mehr ausgeführt.
Das ist nicht nur bei c so. Das Feature nennt sich bei Delphi "Boolsche
Ausdrücke vollständig".
Schlaumaier schrieb:> Mach einen vernünftige AND bzw. OR Abfrage wie sich das gehört.
Was ist an seiner Abfrage nicht vernünftig? Das erkläre mal.
Schlaumaier schrieb:> Habe ich oben.
Ja hast Du, habe ich mittlerweile auch gelesen. Wäre besser gewesen Du
hättest hier diesbezüglich einfach mal die Griffeln still gehalten.
Die Abfrage ist schon so in Ordnung. Allerdings hat er den Fehler bei
seiner Ausweichlösung. Für seine Ursprungsabfrage
1
if(a()||b()==0)
2
{
3
...
4
}
müßte er noch einen else Zweig anhängen. Also so:
1
if(!a()||b()==0)
2
{
3
if(b()==0)
4
{
5
...
6
}
7
else
8
{
9
...
10
}
Wobei dann in der 2.if-Bedingung und im else-Zweig der gleiche Code
stehen würde. Ist ja noch unübersichtlicher.
Zeno schrieb:> Allerdings hat er den Fehler bei seiner Ausweichlösungxor schrieb:> "wenn a oder b" ist nicht das Gleiche wie "wenn nicht a aber b".
Die 2 Beispiele des TOs sind identisch, was den Aufruf von b betrifft.
Ich denke, darum ging es dem TO.
Die entstehenden if/else - Blöcke sind unterschiedlich, maximal den else
Block des ersten Beispiels könnte man im zweiten Konstrukt nachbilden.
if(a||!b){}else{...} wäre identisch zu if(!a){if(b){...}}
Aber wie gesagt, ich denke dem TO ging es um Kurzschlussauswertung an
sich.
Schlaumaier schrieb:> JA aber er versteht die eigene Abfrage nicht.
ich verstehe die Abfrage schon.
ich es muss nur vermieden werden, dass b() aufgerufen wird, wenn a()
true zurück gibt. wenn a() false ist, dann muss b() aufgerufen werden.
Daher habe ich es immer in zwei verschiedenen if-Abfragen gepackt, da
ich mir nicht sicher war, welche Funktion bei
1
if(a()||b()==0)
letztendlich zuerst ausgeführt wird. Nur weil ich es so hinschreibe,
weiß ich ja nicht was der compiler daraus macht.
xor schrieb:> Dieser Code hat aber damit nichts zu tun:
doch, eigentlich schon. siehe erklärung oben.
1
if(!a())
2
{
3
if(0==b())
4
{
5
}
6
}
Auch hier wird b() nur ausgeführt, wenn a() fals zurück gibt.
Marcel schrieb:> es muss nur vermieden werden, dass b() aufgerufen wird, wenn a()> true zurück gibt. wenn a() false ist, dann muss b() aufgerufen werden.
Oh, ich hab das wohl falsch verstanden, ich dachte Du willst
gewährleisten, das auch b() unbedingt ausgeführt wird.
Ich (seit 30J kein Profi mehr!:) würde bei deiner bewährten Methode
bleiben. Weil sofort ersichtlich ist, wie das tut.
Marcel schrieb:> Auch hier wird b() nur ausgeführt, wenn a() fals zurück gibt.
Die Verwirrung entsteht durch das if. Die Bedingungen (also wann {}
ausgeführt wird) deiner Beispiele sind verschieden.
Es geht zum Verständnis auch ohne je ein if. Also:
a() || b();
Ist identisch zu
If(!a()) {b();}
A. S. schrieb:> Die Verwirrung entsteht durch das if. Die Bedingungen (also wann {}> ausgeführt wird) deiner Beispiele sind verschieden.>> Es geht zum Verständnis auch ohne je ein if. Also:>> a() || b();>> Ist identisch zu>> If(!a()) {b();}
da habe ich mich wieder falsch ausgedrückt :(
1
if(!a())
2
{
3
if(0==b())
4
{
5
code
6
}
7
}
der code in der Funktion b() soll nur ausgefürht werden, wenn a() false
zurück gibt.
Desweiteren soll der allgemeine Code nur ausgeführt werden, wenn b() 0
zurück gibt.
wenn b() 0 zurück gibt, wird a() ab sofort true zurück geben (bis man
eine neue funktion aufruft um das wieder rückgängig zu machen).
b() darf aber auch nur einmalig ausgeführt werden (bis man eine neue
funktion aufruft um das wieder rückgängig zu machen).
aber wenn es ja im standard drinn steht, dass bei
1
if(a()||b()==0)
b() nicht mehr ausgeführt wird, wenn a() true ist, ist ja alles in
ordnung.
Mein Problem war, dass ich mir nicht sicher bin, ob der compiler auch
diese reihenfolge behält und nicht erst b() ausführt und dann a().
Nur weil ich es so hinschreibe, heißt es ja noch nicht, dass der
compiler diese Reihenfolge auch behält (?), oder doch?
A.S. schrieb:> Die 2 Beispiele des TOs sind identisch, was den Aufruf von b betrifft.>> Ich denke, darum ging es dem TO.
Aber eben nur was b betrifft. In der Ursprungsbedingung soll aber auch a
ausgeführt werden wenn es wahr ist und das ist bei seiner 2. Lösung
definitiv nicht der Fall. Seine zweite Lösung beschreibt eigentlich eine
UND-Verknüpfung !a && b.
Marcel schrieb:> Nur weil ich es so hinschreibe, heißt es ja noch nicht, dass der> compiler diese Reihenfolge auch behält (?), oder doch?Apollo M. schrieb:> A. S. schrieb:>>> Sequencepoints entstehen nicht einfach so>> Korrekt, das ist der Schlüsselbegriff
Also ja, es ist für && und || in der Reihenfolge garantiert und das mit
Absicht und weitere Infos unter dem Schlüsselwort.
Nur zur Einordnung: ein = ist kein sequencepoint und es ist nicht klar,
ob links oder rechts davon zuerst ausgeführt wird, z.b. wenn auf beiden
Seiten ein Funktionsaufruf oder ein ++ steht.
Marcel schrieb:> aber wenn es ja im standard drinn steht, dass bei1if (a() || b()==0)> b() nicht mehr ausgeführt wird, wenn a() true ist, ist ja alles in> ordnung.
Du hast an dieser Stelle die falsche Logik gewählt. b() wird zwar hier
bei a() true nicht ausgeführt weil die boolsche Bedingung bereits
erfüllt ist. Dennoch wird der allgemeine Code ausgeführt. Du möchtest
den allgemeinen Code aber nur ausführen wenn zu dem auch noch b() false
ist. Da wäre Dein 2. Ansatz der Richtige. Du könntest es über eine
UND-Verknüpfung machen
1
if(!a()&&!b()){code}
Wenn a() hier true ist wird b() nicht mehr ausgeführt, weil !a() false
ist ist und somit das Ergebnis der UND-Verknüpfung bereits feststeht.
Ich weis jetzt nicht ob man bei C die Kurzschlußauswertung abschalten
kann, bei Delphi z.B. geht das und dann würden boolsche Ausdrücke immer
vollständig ausgewertet werden, auch dann wenn das Ergebnis bereits fest
steht. WEnn Du es sicher machen willst, also unabhängig von
irgendwelchen Compilereinstellungen, dann mach es so:
Zeno schrieb:> WEnn Du es sicher machen willst, also unabhängig von irgendwelchen> Compilereinstellungen
Es geht um C, nicht Delfi. In C ist es nicht abschaltbar.
A. S. schrieb:> Es geht um C, nicht Delfi. In C ist es nicht abschaltbar.
Des Lesens bist Du schon mächtig? Ich hatte Delphi als Bespiel benannt,
könnte ja sein das das beim C-Compiler auch möglich ist, weshalb ich ja
auch im Konjunktiv geschrieben hatte.
Zeno schrieb:> A. S. schrieb:>> Es geht um C, nicht Delfi. In C ist es nicht abschaltbar.> Des Lesens bist Du schon mächtig? Ich hatte Delphi als Bespiel benannt,> könnte ja sein das das beim C-Compiler auch möglich ist, weshalb ich ja> auch im Konjunktiv geschrieben hatte.
Nachdem die Frage des TOs mehrfach eindeutig beantwortet wurde,
empfiehlst Du, das lieber zu ignorieren, weil es in C ja wie in Delphi
sein könnte.
Ich habe es für Dich klargestellt, weil Du den Thread wohl nur teilweise
gelesen hast.
Eine Compileroption für die Deaktivierung der Kurzschlussauswertung
würde vermutlich ziemlich viel Verwirrung stiften, weil beim Lesen des
Quellcodes niemand erwartet, dass die standardisierten Regeln plötzlich
nicht mehr gelten.
Wenn man tatsächlich eine vollständige Auswertung boolescher Ausdrücke
möchte, schreibt man diese einfach etwas anders hin, also bspw.
1
(bool)a()|(bool)b()
statt
1
a()||b()
Dann erkennt jeder sofort die Absicht dahinter.
Alternativ kann man auch – wie oben bereits vorgeschlagen – die
Ergebnisse von a() und b() in temporäre Variablen schreiben und diese
anschließend mit || verknüpfen.
In den allermeisten Fällen stellt die Kurzschlussauswertung aber einen
Vorteil dar, so dass man der Umweg über Bitoperationen oder temporäre
Variablen nur selten benötigt.
Für Pascal-Derivate, in denen zwischen Kurzschluss- und vollständiger
Auswertung unterschieden werden soll, würde sich die Unterscheidung
mittels Zusatz zum Operatornamen nach ISO 10206 (Extended Pascal)
anbieten, also "and" und "or" für vollständige und "and then" und "or
else" für Kurzschlussauswertung. Das wäre auf jeden Fall sauberer als
die Umschaltung per Compileroption.
Die Kurzschlussauswertung ist dann richtig, wenn die Funktionen a() und
b() seiteneffektfrei sind.
Da sie eine leere Parameterliste haben und offensichtlich freie
Funktionen sind, sind es also entweder freie Beobachter-Funktionen (als
Elementfunktionen wäre sie dann const) für globale Datenstrukturen oder
sie haben einen Seiteneffekt darauf. Letzteres ist software-technisch
eine Katastrophe, doch meistens geht es aus dem Namen hervor, was hier
jetzt nicht ersichtlich ist.
A. S. schrieb:> Ich habe es für Dich klargestellt, weil Du den Thread wohl nur teilweise> gelesen hast.
Du bist einfach nur ein arrogantes A........ . Du mußt mir zum einen
nichts klar stellen und zweitens kannst Du nicht beurteilen inwiefern
ich den Thread gelesen habe.
Yalu X. schrieb:> würde sich die Unterscheidung> mittels Zusatz zum Operatornamen nach ISO 10206 (Extended Pascal)> anbieten, also "and" und "or" für vollständige und "and then" und "or> else" für Kurzschlussauswertung
Das mit "and then" und "or else" ist wieder mal so eine GNU-Kacke (auch
wenn's ne ISO ist) und ein Sonderweg. Weder in den mir bekannten
Delphivarianten noch Freepascalvarianten wird so ein Mist gemacht. Ich
habe es gerade auch noch mal gegoogelt und das scheint wirklich ein
Alleinstellungsmerkal bei GNU zu sein. Die können sich an keine
Standards halten und müssen immerwieder ihr eigenes Süppchen kochen -
ist bei den Truppenteilen nichts Neues.
Yalu X. schrieb:> Das wäre auf jeden Fall sauberer als> die Umschaltung per Compileroption.
Was ist daran unsauber? Nichts, weil man das als Programmierer selbst in
der Hand hat. Ich habe bei den meisten meiner Projekte die
Kurzschlußauswertung aktiv, weil dies in den meisten Fällen von Vorteil
ist. Bei Delphi bin ich da aber diesbezüglich komplett frei und ich
könnte sogar im Quelltext selektiv eine vollständige Boolsche Auswertung
aktivieren.
Wilhelm M. schrieb:> Die Kurzschlussauswertung ist [...]
...begrifflich genau solcher Schwachsinn wie der
bürstenlose Gleichstrommotor oder der integrierte
Koprozessor.
"&&" ist kein boolescher Operator (Beweis auf Anfrage) --
sondern eine Kurzschreibweise für geschachtelte Bedingungen.
(Meines Wissens definiert der Standard das auch genau so.)
Es scheint zu den besonders beliebten Teilen der C-Folklore
zu gehören, nützliche Dinge mit möglichst suggestiven, aber
irreführenden Namen zu bezeichnen...
Zeno schrieb:> Yalu X. schrieb:>>> Das wäre auf jeden Fall sauberer als die Umschaltung per>> Compileroption.>> Was ist daran unsauber?
Die "Kurzschlussauswertung" steht seit je her im C-Standard.
Ein Compilerschalter dafür hätte die Bedeutung "Standard
respektieren? Ja [ ] Nein [ ] ".
> Bei Delphi
"Jaja, so hoch ist noch niemand gesprungen, aber wir machen
hier WEITSPRUNG ..."
Die Frage des TO bezog sich auf C, und die Antworten beziehen
sich -- Überraschung! -- auch auf C...
Yalu X. schrieb:> Für Pascal-Derivate, in denen zwischen Kurzschluss- und> vollständiger Auswertung unterschieden werden soll, würde> sich die Unterscheidung mittels Zusatz zum Operatornamen> nach ISO 10206 (Extended Pascal) anbieten, also "and" und> "or" für vollständige und "and then" und "or else" [...]
"Unsere Lösung -- ihr Problem!"
Wenn ich "Kurzschluss-Auswertung" haben will, lese ich den
Standard einfach von hinten nach vorn und schreibe zwei
(oder mehr) Bedingungen auf zwei (oder mehr) Zeilen...
Die Verachtung, dass kein "echter" Programmierer das je so
machen würde, kann ich leicht in Kauf nehmen, da sowieso
kein "echter" Programmierer je Pascal verwenden würde...
Zeno schrieb:> arrogantes A........, Kacke
Was ist denn dir so kurz vor Mitternacht noch über die Leber gelaufen?
Zeno schrieb:> Das mit "and then" und "or else" ist wieder mal so eine GNU-Kacke (auch> wenn's ne ISO ist) und ein Sonderweg.
Du meinst also, der Entwickler von GNU Pascal, der gerade mal 1 Jahr vor
der Veröffentlichung des ISO-Standards die Version 0.01 fertiggestellt
hat, hatte tatsächlich einen so großen Einfluss auf das ISO-Gremium,
dass er kurz vor Ladenschluss das and_then und or_else noch erfolgreich
in den Standard hineinboxen konnte? Ich glaube, du hast überhaupt keine
Vorstellung davon, wie so eine Normung abläuft :D
> Weder in den mir bekannten Delphivarianten noch Freepascalvarianten> wird so ein Mist gemacht.
Borland hat sich auch mit Turbo-Pascal noch nie an irgendwelche
Standards gehalten, warum sollten sie es mit Delphi plötzlich tun? Und
Free Pascal implementiert im Wesentlichen dieselbe Sprache, die auch
Delphi zugrundeliegt.
> Ich habe es gerade auch noch mal gegoogelt und das scheint wirklich> ein Alleinstellungsmerkal bei GNU zu sein.
Das ist kein Wunder, denn zum Zeitpunkt der ersten Veröffentlichung des
ISO-Standards (1990) hat Pascal schon weitgehend ausgedient, so dass es
dafür so gut wie keine Neuentwicklungen von Compilern mehr gab. Nur
Borland und später Embarcadero pflegten Turbo Pascal (das später in
Delphi aufging) für einen sehr überschaubaren Kundenkreis noch weiter.
Für Embarcadero gilt aber nach wie vor:
> Die können sich an keine Standards halten und müssen immerwieder ihr> eigenes Süppchen kochen - ist bei den Truppenteilen nichts Neues.
Das ist ja mittlerweile auch völlig egal: Da die Firma den praktisch
einzigen noch verbleibenden Pascal-Compiler anbietet (GNU Pascal ist
längst eingestellt und Free Pascal ahmt nur nach), braucht es auch keine
internationalen Standards, und Kompatibilität zu irgendetwas ist
ebenfalls kein Thema, da dieses Irgendetwas längst nicht mehr existiert.
Das "and_then" und "or_else" gibt es übrigens nach wie vor in Ada (ohne
die Underscores), als gängige Erweiterung in strukturiertem Text
(SPS-Programmiersprache nach EN/IEC 61131-3) und als "AndAlso" und
"OrElse" in VB.NET:
https://en.wikipedia.org/wiki/Short-circuit_evaluation
Jede der genannten Sprachen für sich hat einen Nutzerkreis, der um ein
Vielfaches größer ist als der von Delphi und Free Pascal zusammen. Die
Verwendung spezieller Operatoren für die Kurzschlussauswertung ist
entgegen deiner Meinung also keineswegs auf GNU beschränkt.
Aber vielleicht geht es dir heute morgen ja schon wieder besser :)
Egon D. schrieb:> "&&" ist kein boolescher Operator (Beweis auf Anfrage) --
Geschenkt. Das ist eine Frage der Definition, und Definitionen
müssen und können nicht bewiesen oder widerlegt werden.
> sondern eine Kurzschreibweise für geschachtelte Bedingungen.
Ersetze "sondern" durch "und auch", dann stimmt es eher, wobei ich mir
unter einer "geschachtelten Bedingung" etwas anderes vorstelle als du,
nämlich eine logische Operation, bei der mindestens ein Operand wieder
eine logischer Operation ist.
> (Meines Wissens definiert der Standard das auch genau so.)
Im Standard werden sie "logical operators" genannt. Da die (klassische)
Logik eine Boolesche Algebra darstellt, ist auch "boolesche Operatoren"
in Ordnung, nur weniger spezifisch, da bspw. auch die Mengenalgebra eine
Boolesche Algebra ist.
Diese (teilweise schöne) Diskussion hier ist nur ein weiterer Beleg
dafür, dass die Zukunft den Funktionalen Programmiersprachen gehört.
Am besten wäre immer noch Lambda-Kalkül... ;)
Church und Kleene waren ihrer Zeit nun schon bald 100 Jahre voraus.
> dass die Zukunft den Funktionalen Programmiersprachen gehört
Nicht so ganz - das Problem sind diese neumodische Sachen, wie Parser,
die jede BNF in Bäume umwandeln können.
In der guten alten Lisp Klammerschreibweise können solche Fragen erst
gar nicht auftauchen.
Wenn du dagegen die C Syntax für eine funktionale Sprache nutzt,
bekommst du dort auch die selbe Verwirrung.
Egon D. schrieb:>> Bei Delphi>> "Jaja, so hoch ist noch niemand gesprungen, aber wir machen> hier WEITSPRUNG ...">> Die Frage des TO bezog sich auf C, und die Antworten beziehen> sich -- Überraschung! -- auch auf C...Egon D. schrieb:> Die Verachtung, dass kein "echter" Programmierer das je so> machen würde, kann ich leicht in Kauf nehmen, da sowieso> kein "echter" Programmierer je Pascal verwenden würde...
Na Egon auch Nase etwas "hoch trag". Macht aber nichts jetzt wissen alle
das Du ein echter Programmierer bist, weil Du nur C programmierst. Dein
Statement ist ja wohl auch mal wieder an Arroganz kaum zu toppen.
Yalu X. schrieb:> Was ist denn dir so kurz vor Mitternacht noch über die Leber gelaufen?
Dann schau mal was A.S. in seinem Post geschrieben hat, auf den ich
geantwortet habe und schau evtl. auch noch mal auf meinen Post auf den
er geantwortet hat.
Da kann man auch anders antworten und nicht so wie A.S. das getan hat.
Ein schlichtes "in C ist das nicht abschaltbar" oder so ähnlich wäre
völlig ausreichend gewesen und hätte meine indirekt gestellte Frage
vollständig beantwortet. Statt dessen meint er, er müsse sein Gegenüber
als ein bissel doof darstellen.
Aber lassen wir das und kehren lieber zum Thema zurück.
Yalu X. schrieb:> Du meinst also, der Entwickler von GNU Pascal, der gerade mal 1 Jahr vor> der Veröffentlichung des ISO-Standards die Version 0.01 fertiggestellt> hat, hatte tatsächlich einen so großen Einfluss auf das ISO-Gremium
Das habe ich so nicht geschrieben. Da reicht es wenn einer im Gremium
der Meinung ist, das dieses Feature top ist. Wenn er dann für seine
Ansichten Mehrheiten gewinnen kann, dann wird so etwas eben in die Norm
auf genommen. Nicht immer ist aber das was derartige Gremien beschließen
immer sinnvoll, ich denke das wissen wir nur zu gut auch aus vielen
andern Bereichen. Kleines Beispiel gefällig: Verordnung 1677/88/EWG,
auch als Gurkenverordnung bekannt. Dort wird ja bekanntlich festgelegt
das die Krümmung einer Gurke nich mehr wie 10mm betragen darf - dabei
wissen alle Gurkenesser das krumme Gurken viel besser schmecken :-).
Yalu X. schrieb:> Borland hat sich auch mit Turbo-Pascal noch nie an irgendwelche> Standards gehalten, warum sollten sie es mit Delphi plötzlich tun? Und> Free Pascal implementiert im Wesentlichen dieselbe Sprache, die auch> Delphi zugrundeliegt.
Borland ist nicht so weit weg vom Standard. Ein großer Sprung
diesbezüglich wurde eigenlich erst in Delphi gemacht. Den Datentyp
Variant z.B. gibt es im Urpascal von N.Wirth und ich meine auch in
Borland Pascal nicht. Zu diesem Zeitpunkt war allerdings OLE auch noch
kein Thema. Ich bin mir noch nicht einmal sicher ob es bei Wirth schon
Objekte gab. Das fand dann erst Einzug durch die Zusammenarbeit mit
Apple. Auch die in diesem Thread besprochene Kurzschlußauswertung ist
kein Feature des Urpascal und wurde von Wirth sogar explizit
ausgeschlossen - er wird seine Gründe gehabt haben.
Aber auch Pascal entwickelt sich halt weiter, auch wenn das hier von
einigen als Alleinstellungsmerkmal von C geseheen wird, und kommt
letztendlich da auch nicht umhin. Auf "and then" und "or else" kann aber
gern verzichtet werden. Das ist so eine Krücke die man bei Java mit &
und | im Gegensatz zu && und || eingeführt hat.
Yalu X. schrieb:> ... hat Pascal schon weitgehend ausgedient,
Das dürfte der Wunsch vieler C-Programmierer sein. Aber merke tod
Geglaubte leben meist länger. Delphi ist noch sehr lebendig, gerade im
September ist eine neu Version (RAD Studio 11) heraus gekommen. Meinst
Du die würden da auch nur einen Cent Entwicklungskosten rein stecken,
wenn da am Ende nichts rum kommen würde? Ganz sicher nicht. Die
Delphi/Freepascal/Lazerus Commutity ist auch recht agil.
Yalu X. schrieb:> für einen sehr überschaubaren Kundenkreis noch weiter
Es gibt deutlich mehr Kunden bzw. Software die auf Delphi setzen als Du
zu glauben scheinst.
Yalu X. schrieb:> Das "and_then" und "or_else" gibt es übrigens nach wie vor in Ada (ohne> die Underscores), als gängige Erweiterung in strukturiertem Text> (SPS-Programmiersprache nach EN/IEC 61131-3) und als "AndAlso" und> "OrElse" in VB.NET:>> https://en.wikipedia.org/wiki/Short-circuit_evaluation>> Jede der genannten Sprachen für sich hat einen Nutzerkreis, der um ein> Vielfaches größer ist als der von Delphi und Free Pascal zusammen.
Das ist auch wieder so eine Behauptung Deinerseits. Das Pascal im
Vergleich zu Phython, Java und allem was irgendwie C heißt schlechter
abschneidet steht außer Diskussion, aber so pauschal wie Du das nun
wieder mal schreibst ist es eben auch nicht.
Yalu X. schrieb:> Aber vielleicht geht es dir heute morgen ja schon wieder besser
Kann ich so nicht sagen - mir ging es gestern durchaus nicht schlechter
als heute.
Yalu X. schrieb:> Jede der genannten Sprachen für sich hat einen Nutzerkreis
Diesen Teil des Satze würde ich so sogar schon fast unterschreiben und
noch etwas ergänzen: "und ihre Daseinsberechtigung."
Ja ich weis jetzt treten gleich wieder einige eingefleichte
C-Programmierer auf den Plan, die meinen das C das Evangelium ist.
Einer schrieb:> Diese (teilweise schöne) Diskussion hier ist nur ein weiterer Beleg> dafür, dass die Zukunft den Funktionalen Programmiersprachen gehört.
Ja, damit wären die hier diskutierten und auch viele andere Probleme
komplett aus der Welt geschafft. Allerdings ist der Weg dorthin noch mit
großen Hindernissen gepflastert, das größte davon heißt Neophobie.
Da immer mehr gängige imperative Programmiersprachen wie C++, Rust und
Python mit funktionalen Elementen versehen werden, wäre zu hoffen, dass
dies vielleicht zu einer langsamen, aber stetigen Migration zur
funktionale Programmierung führt, bei der niemand ins kalte Wasser
geschmissen wird.
So etwas ähnliches ist ja bei der objektorientierten Programmierung
geschehen, die Dank dem sanften Umstieg mittels C++ oder Python
mittlerweile zum Mainstream gehört.
Allerdings ist der Sprung von einer imperativen zu einem deklarativen
Programmierparadigma, wie es die FP umsetzt, ein gutes Stück größer
als zwischen zwei verschiedenen Paradigmen innerhalb der imperativen
Welt. Als Hilfe bei diesem Sprung stellen auch die o.g. funktional
angehauchten imperativen Sprachen zwar ein ein Trampolin dar, aber
leider nur eins mit teilweise gebrochenen Federn.
Mal schauen, was die nächsten 20 Jahre bringen werden :)
Noch ein Kommentar schrieb:> Wenn du dagegen die C Syntax für eine funktionale Sprache nutzt,> bekommst du dort auch die selbe Verwirrung.
Das Problem des TE war nicht die Syntax, sondern die Semantik, und diese
ist in funktionalen Sprachen – unabhängig von der verwendeten Syntax –
tatsächlich sehr viel einfacher.
Yalu X. schrieb:> Alternativ kann man auch – wie oben bereits vorgeschlagen – die> Ergebnisse von a() und b() in temporäre Variablen schreiben und diese> anschließend mit || verknüpfen.
Das wäre ein sehr dummer Compiler und tatsächlich findet das nur bei O0
statt.
Der Compiler wird auch dies bei der Optimierungsstufe > O1 wieder in den
Code einer Kurzschlussauswertung umwandeln. Es sei denn, man macht die
temp. Variablen volatile.
Wilhelm M. schrieb:> Yalu X. schrieb:>> Alternativ kann man auch – wie oben bereits vorgeschlagen – die>> Ergebnisse von a() und b() in temporäre Variablen schreiben und diese>> anschließend mit || verknüpfen.>> Das wäre ein sehr dummer Compiler und tatsächlich findet das nur bei O0> statt.>> Der Compiler wird auch dies bei der Optimierungsstufe > O1 wieder in den> Code einer Kurzschlussauswertung umwandeln. Es sei denn, man macht die> temp. Variablen volatile.
Hmmm, ich habe hier einen »dummen Compiler«, der macht's richtig bei
jeder Optimierungsstufe. Aber bitte jetzt nicht denken das ich
überrascht war…
Norbert schrieb:> Hmmm, ich habe hier einen »dummen Compiler«, der macht's richtig bei> jeder Optimierungsstufe.
puts(...) macht intern letztendlich einen Zugriff auf volatile. Dieser
darf nicht wegoptimiert werden, was ich auch oben geschrieben hatte.
Entferne das puts() und schau Dir das Ergebnis im CompilerExplorer an.
Wilhelm M. schrieb:> Norbert schrieb:>> Hmmm, ich habe hier einen »dummen Compiler«, der macht's richtig bei>> jeder Optimierungsstufe.>> puts(...) macht intern letztendlich einen Zugriff auf volatile. Dieser> darf nicht wegoptimiert werden, was ich auch oben geschrieben hatte.> Entferne das puts() und schau Dir das Ergebnis im CompilerExplorer an.
Also irgend etwas müssen die Funktionen schon machen damit sie nicht weg
optimiert werden. Aber auch das alleinige hoch zählen einer globalen
Variablen funktioniert problemlos.
Wenn nichts, aber auch gar nichts in den Funktionen drin steht, warum
soll man sie dann im executable belassen?
Norbert schrieb:> Also irgend etwas müssen die Funktionen schon machen damit sie nicht weg> optimiert werden.
Wenn sie auf eine programm-globale Variable zugreifen, werden sie nicht
wegoptimiert.
Wenn Du in beiden Funktionen eine globale Variable incrementierst,
erzeugst Du eine Abhängigkeit, die natürlich auch nicht wegoptmiert
werden darf. Du wirst allerdings feststellen, dass ein re-order
stattfindet, d.h. das Inkrement in a() und b() wird zu einem Inkrement
zusammen gefasst. Dann hast Du letztlich Äpfel mit Birnen verglichen.
Nachdenklicher schrieb:> Und weil if kein Funktionsaufruf ist, gehört zwischen das f und die> öffnende Klammer ein Leerzeichen…
Weil natürlich jeder beim Lesen des sourcecodes automatisch vermuten
könnte, dass if(;;) eine function ist
LOL
Yalu X. schrieb:> Wenn man tatsächlich eine vollständige Auswertung boolescher Ausdrücke> möchte, schreibt man diese einfach etwas anders hin, also bspw.> (bool)a() | (bool)b()
Na, da wird bestimmt ein schlauer Wartungsprogrammierer daher kommen und
das wieder zu einem Boole`schen Op abändern -> Ups.
Dann schon besser
Wilhelm M. schrieb:> Du wirst allerdings feststellen, dass ein re-order> stattfindet, d.h. das Inkrement in a() und b() wird zu einem Inkrement> zusammen gefasst.
Du meine Güte, ja, gerne. Weil's keinen Unterschied macht!
Sobald es einen Unterschied machen würde, zB. weil erst etwas in PORTA
und in der nächsten Funktion etwas in PORTB geschrieben wird, dann
wird's auch genau so ausgeführt.
Wo bitte ist das Problem? Bitte um Beispielcode wo der Compiler einen
falschen Programmablauf verursachen würde. Danke!
Norbert schrieb:> Du meine Güte, ja, gerne. Weil's keinen Unterschied macht!> Sobald es einen Unterschied machen würde, zB. weil erst etwas in PORTA> und in der nächsten Funktion etwas in PORTB geschrieben wird, dann> wird's auch genau so ausgeführt.
Ja, das sage ich ja die ganze Zeit: volatile.
Andernfalls hast Du wie in Deinem (modifizierten) Beispiel die
Modifikation eines Objektes: sequence-point.
Norbert schrieb:> Wo bitte ist das Problem? Bitte um Beispielcode wo der Compiler einen> falschen Programmablauf verursachen würde.
Ich rede gar nicht von einem Problem. Und schon gar nicht von einem
Fehler des Compilers.
Solange a() und b() keine sequence-points enthalten, nützt auch das
Einführen von lokalen Variablen nicht, um ein shortcut zu verhindern.
Das war meine Aussage oben.
Wilhelm M. schrieb:> Das wäre ein sehr dummer Compiler und tatsächlich> findet das nur bei O0 statt.
Du redest rückwärts. Wenn ich a() und b() temporären Variablen zuweise
und dann verknüpfe, dann werden a() und b() auch ausgeführt, denn deren
Nebeneffekte müssen in dieser Reihenfolge auftreten.
Wenn der Compiler natürlich beweisen kann, dass es keine Nebeneffekte
gibt, dann darf er dadran gerne rumoptimieren.
Die Aussage, dass das nur bei O0 stattfindet, ist nämlich genau dann
falsch, wenn die Funktion nicht pure ist (aka "der Normalfall"). Alles
andere wäre eine Verletzung des Standards.
Zeno schrieb:> Dann schau mal was A.S. in seinem Post geschrieben hat, auf den ich> geantwortet habe und schau evtl. auch noch mal auf meinen Post auf den> er geantwortet hat.> Da kann man auch anders antworten und nicht so wie A.S. das getan hat.> Ein schlichtes *"in C ist das nicht abschaltbar"* oder so ähnlich wäre> völlig ausreichend gewesen und hätte meine indirekt gestellte Frage> vollständig beantwortet. Statt dessen meint er, er müsse sein Gegenüber> als ein bissel doof darstellen.
Sorry, Zeno, aber Du bist ausgerastet (im Sinne von persönlich
beleidigend geworden), weil ich schrieb (und das ist der vollständige
Post):
A. S. schrieb:> Es geht um C, nicht Delfi. In C ist es nicht abschaltbar.
Der zweite Satz ist identisch zu dem, was Du gut gefunden hättest. Du
entzündest Dich also an meinem: es geht um C, nicht Delfi.
Wilhelm M. schrieb:> Solange a() und b() keine sequence-points enthalten, nützt auch das> Einführen von lokalen Variablen nicht, um ein shortcut zu verhindern.> Das war meine Aussage oben.
Dann gibt es allerdings auch keinen Grund, es verhindern zu wollen. Das
ist auch keine shortcut evaluation, sondern schlicht Teil der normalen
Optimierung. Wenn die Funktion keinen erkennbaren Effekt hat, kann sie
wegoptimiert werden, ganz egal ob da ein || steht, oder nicht.
Was ich meinte, war einfach folgendes.
Die Variante mit den lokalen Variablen erzwingt keine Auswertung der
Funktion b(), wenn b() keinen sequence-point enthält. Das ist immer dann
der Fall, wenn sie eine reine Prädikat-Funktion ist. Und das hat nichts
mit dem Wegoptimieren der Funktion zu tun.
Folgendes Beispiel:
1
#include<stdio.h>
2
3
boola1=true;
4
boolb1=true;
5
6
staticboola(){
7
returna1;
8
}
9
staticboolb(){
10
returnb1;
11
}
12
staticboolnoShortCutOr(constboola,constboolb){
13
returna||b;
14
}
15
intmain(){
16
// if (noShortCutOr(a(), b())) {
17
// puts("Hey");
18
// }
19
autoresa=a();
20
autoresb=b();
21
22
if(resa||resb){
23
puts("Hey");
24
}
25
}
26
[c]
27
28
ergibt
29
30
[c]
31
.LC0:
32
.string"Hey"
33
main:
34
cmpBYTEPTRa1[rip],0
35
jne.L4
36
cmpBYTEPTRb1[rip],0
37
je.L6
38
.L4:
39
pushrax
40
movedi,OFFSETFLAT:.LC0
41
callputs
42
xoreax,eax
43
poprdx
44
ret
45
.L6:
46
xoreax,eax
47
ret
48
b1:
49
.byte1
50
a1:
51
.byte1
Durch den Zugriff auf die beiden programm-globalen Variablen a1 und b1
können die Funktionen nicht wegoptimiert werden. Der Zugriff findet
statt, wie man sieht. Das Wegoptimieren würde allerdings passieren, wenn
sie TU-lokal sind, also static.
Man sieht hier, dass es genau die Kurzschlussauswertung ist. Die ist
dann im Assembler identisch zu:
1
#include<stdio.h>
2
3
boola1=true;
4
boolb1=true;
5
6
staticboola(){
7
returna1;
8
}
9
staticboolb(){
10
returnb1;
11
}
12
intmain(){
13
if(a()||b()){
14
puts("Hey");
15
}
16
}
ergibt auch wieder:
1
.LC0:
2
.string"Hey"
3
main:
4
cmpBYTEPTRa1[rip],0
5
jne.L2
6
cmpBYTEPTRb1[rip],0
7
je.L5
8
.L2:
9
pushrax
10
movedi,OFFSETFLAT:.LC0
11
callputs
12
xoreax,eax
13
poprdx
14
ret
15
.L5:
16
xoreax,eax
17
ret
18
b1:
19
.byte1
20
a1:
21
.byte1
Also, die Aussage, dass das Einführen von lokalen Variablen eine
Auswertung von b() erzwingt ist allgemein falsch. Das funktioniert nur,
wenn ein sequence-point eingeführt wird. Wie vorher schon geschrieben,
kann dies geschehen durch
1) Zugriff auf volatile Objekte,
2) Modifikation eines Objektes
3) ...
4) ... (die Liste ist lang)
Bei 1) ist es eh klar, dass das dann den gewünschten Effekt hat
(Beispiel mit puts() in der Funktion a() und b()). Neben dem erzwungenen
Zugriff kann auch kein reorder mehr stattfinden.
Und bei 2) führt man einen sequence-point ein, und damit ist es keine
Prädikat-Funktion mehr. Als Elementfunktion wäre die Funktion dann eben
nicht mehr const-qualified (ein starkes Argument dafür, die
Unterscheidung zwischen Beobachterfunktionen und Modifikatoren
konsequent zu machen und damit auf die const-correctness zu achten. Aber
das sollte ja eh jedem klar sein).
P.S.: da die obige Liste, welche Aktionen zu einem sequence-point sehr
lang ist, mag zu der Aussage verleiten, dass das mit den lokalen
Variablen immer funktioniert ...
Wilhelm M. schrieb:> Das Wegoptimieren würde allerdings passieren, wenn> sie TU-lokal sind, also static.> Man sieht hier, dass es genau die Kurzschlussauswertung ist. Die ist> dann im Assembler identisch zu:
Ich vermute einfach nur einen lazy Optimierer. Da a1 und b1 bekannt und
konstant sind (in Deinem Code-Beispiel), könnte der Compiler m.E. auf
beide Aufrufe verzichten.
Wenn Du zwei globale Funktionen Ma1 und Mb1 einführst, die a1 und b1
modifizieren (void Ma1(bool b){a1=b;}), dann sollte der Optimierer
wieder beide Funktionen aufrufen.
P.S.: (wobei ich schwimme, was "vor" main erlaubt wäre, also ob "main"
besonders ist).
Wilhelm M. schrieb:> Yalu X. schrieb:>> Alternativ kann man auch – wie oben bereits vorgeschlagen – die>> Ergebnisse von a() und b() in temporäre Variablen schreiben und diese>> anschließend mit || verknüpfen.>> Das wäre ein sehr dummer Compiler und tatsächlich findet das nur bei O0> statt.>> Der Compiler wird auch dies bei der Optimierungsstufe > O1 wieder in den> Code einer Kurzschlussauswertung umwandeln. Es sei denn, man macht die> temp. Variablen volatile.
Natürlich ist klar, dass der Compiler keine vollständige Auswertung
macht, wenn er erkennen kann, dass es auch einfacher geht. Das ist immer
so, nicht nur bei der Kurzschlussauswertung:
https://en.cppreference.com/w/cpp/language/as_if
Die ganze diesbezügliche (und IMHO unnötige) Diskussion ist wohl darauf
zurückzuführen, dass ich mich oben ungenau ausgedrückt habe:
Yalu X. schrieb:> Wenn man tatsächlich eine vollständige Auswertung boolescher Ausdrücke> möchte, schreibt man diese einfach etwas anders hin, also bspw.>> (bool)a() | (bool)b()>> ...>> Alternativ kann man auch – wie oben bereits vorgeschlagen – die> Ergebnisse von a() und b() in temporäre Variablen schreiben und diese> anschließend mit || verknüpfen.
Ich hätte den Satz besser so formulieren sollen:
Wenn man dasselbe Verhalten wie bei einer vollständigen Auswertung
boolescher Ausdrücke möchte, ...
A.S. schrieb:> Sorry, Zeno, aber Du bist ausgerastet (im Sinne von persönlich> beleidigend geworden), weil ich schrieb (und das ist der vollständige> Post):>> A. S. schrieb:>> Es geht um C, nicht Delfi. In C ist es nicht abschaltbar.
Nö A.S. Dieser Satz ist nicht der Satz, weswegen ich nach Deiner Meinung
"ausgerastet" bin.
Das Arrogant bezog sich eher auf den letzten Satz Deines Post's:
A. S. schrieb:> Ich habe es für Dich klargestellt, weil Du den Thread wohl nur teilweise> gelesen hast.
Zeno schrieb:> Das Arrogant bezog sich eher auf den letzten Satz Deines Post's:> A. S. schrieb:>> Ich habe es für Dich klargestellt, weil Du den Thread wohl nur teilweise>> gelesen hast.
Wenn jemand zentrale Punkte eines Threads nicht erfasst, vermute ich,
dass er nicht alles gelesen hat. Und nicht:
Zeno schrieb:> Des Lesens bist Du schon mächtig?
Wilhelm M. schrieb:> Was ich meinte, war einfach folgendes.
Du bist komplett auf dem Holzweg und ziehst aus der Arbeitsweise des
Optimierers falsche Schlüsse.
Wenn Du schon mit Beispielen etwas beweisen willst, musst Du das
Beispiel auch so konstruieren, damit Du den richtigen Effekt siehst.
Also z.B. so:
1
externinta();
2
externintb();
3
4
voidtest1()
5
{
6
intx=a();
7
inty=b();
8
9
if(x||y)
10
puts("test1");
11
}
12
13
voidtest2()
14
{
15
if(a()||b())
16
puts("test2");
17
}
Und wenn Du nun dieses Beispiel kompilierst, siehst Du eben dass der
Compiler bei test1() immer beide Funktionen, a() und b() exakt
nacheinander aufruft.
Bei test2() siehst Du, dass der Compiler b() ausschließlich aufruft,
wenn a() Null zurückgegeben hat. Und das auch völlig unabhängig von der
verwendeten Optimierungsstufe.
Und das ist per Definition so. Der Operator || macht zwingend eine
Kurzschlussauswertung. Und bei test1() darf der Compiler auch nicht
einfach eine Kurzschlussauswertung erfinden, so wie von Dir behauptet.
Bei den bis jetzt hier gezeigten Beispielen kommt der Optimierer nur
deshalb in die Quere, weil er beweisen konnte, dass manche dieser
trivialen Beispiel-Funktionen umsortiert oder auch weggelassen werden
konnten.
Aber genau das hat nichts, aber auch gar nichts mit der
Kurzschlussauswertung des Operators || zu tun, sondern es waren einfach
nur untaugliche Beispiele.
Einer schrieb:> Du bist komplett auf dem Holzweg und ziehst aus der Arbeitsweise des> Optimierers falsche Schlüsse.>> Wenn Du schon mit Beispielen etwas beweisen willst, musst Du das> Beispiel auch so konstruieren, damit Du den richtigen Effekt siehst.> Also z.B. so:> extern int a();> extern int b();> void test1()> {> int x = a();> int y = b();>> if (x || y)> puts("test1");> }> void test2()> {> if (a() || b())> puts("test2");> }
Na, das ist ja ein ganz anderes Beispiel ;-)
Wilhelm M. schrieb:> Also, die Aussage, dass das Einführen von lokalen Variablen eine> Auswertung von b() erzwingt ist allgemein falsch. Das funktioniert nur,> wenn ein sequence-point eingeführt wird. Wie vorher schon geschrieben,> kann dies geschehen durch>> 1) Zugriff auf volatile Objekte,> 2) Modifikation eines Objektes> 3) ...> 4) ... (die Liste ist lang)
Der Compiler muss hier pessimistisch vorgehen, wenn Du ihm komplett die
Möglichkeit nimmst, zu prüfen, ob ggf. eine Ausnahme aus der o.g. Liste
zutrifft. Also festzustellen, dass a() oder b() reine Beobachter sind.
Und: ich sage nicht, dass es dasselbe ist wie die Kurzschlussauswertung.
Ich habe nur gezeigt, dass das Einführen von lokalen Variablen nicht in
jedem Fall dazu führt, dass bei Funktionen ausgeführt werden. Das war
mein Einwand. Ja, es ist ein Spezialfall. Doch es hilft eben nicht in
jedem Fall, lokale Variablen einzuführen. Das ist doch auch klar,
ansonsten wäre der Optimizer nix.
Wilhelm M. schrieb:> Und: ich sage nicht, dass es dasselbe ist wie die Kurzschlussauswertung.
Na, wenn Dich da mal Deine Erinnerung nicht trübt:
Wilhelm M. schrieb:> Der Compiler wird auch dies bei der Optimierungsstufe > O1 wieder in den> Code einer Kurzschlussauswertung umwandeln. Es sei denn, man macht die> temp. Variablen volatile.
Und genau das ist grundlegend falsch.
Der Compiler erfindet eben genau nicht abhängig von der
Optimierungseinstellung eine Kurzschlussauswertung oder auch nicht.
Du verwechselt Ursache und Wirkung bzw. Du begehst einen klassischem
Trugschluss:
Wenn aus A auch B folgt, folgt eben genau nicht aus B auch A. Das ist
falsch. Sondern aus Nicht(B) folgt Nicht(A).
Einfache Logik, aber das bekommen viele nicht auf die Reihe.
"A" bedeutet in diesem Kontext der Quelltext, konkret hier einen
Kurschluss-Operator, und "B" ist das Kompilat
(Assemblerlisting/Objektcode).
Du schaust nun auf das Kompilat, erzeugt durch einen Optimierer, und
schließt dann aus Kompilat "B" auf Quelltext "A". Das ist falsch, das
geht nicht.
Einer schrieb:> Der Compiler erfindet eben genau nicht abhängig von der> Optimierungseinstellung eine Kurzschlussauswertung oder auch nicht.
Die Aussage war, dass das Einführen von lokalen Variablen für die
Ergebnisse von a() und b() die Ausführung von a() und b() erzwingt. Ich
habe oben den Gegenbeweis erbracht. In diesem Fall ist es legitim, ein
Gegenbeispiel anzuführen.