Forum: PC-Programmierung Zwei bedingungen in einer if-Abfrage


von Marcel (Gast)


Lesenswert?

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

von Jonas B. (jibi)


Lesenswert?

>wenn die Funktion a() 1 Zurück gibt, wird die funktion b() noch
>ausgeführt?

Bei c, wird Funktion b nicht mehr ausgeführt.

: Bearbeitet durch User
von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

Marcel schrieb:
> wenn die Funktion a() 1 Zurück gibt, wird die funktion b() noch
> ausgeführt?

Nein.

von Schlaumaier (Gast)


Lesenswert?

Mach einen vernünftige AND bzw. OR Abfrage wie sich das gehört.

von Dirk B. (dirkb2)


Lesenswert?

Marcel schrieb:
> Wenn ja/nein, ist das immer so? Oder liegt das am Compiler (oder
> Einstellungen)?

In C und C++ ist das im Standard definiert.

Das läuft unter der Bezeichnung 
https://de.wikipedia.org/wiki/Kurzschlussauswertung

von Oliver S. (oliverso)


Lesenswert?

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

von Dirk B. (dirkb2)


Lesenswert?

Damit ist dann auch
1
char *a;
2
if(a && *a) {
möglich, wenn a = NULL ist.

von Marcel (Gast)


Lesenswert?

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?

von A. S. (Gast)


Lesenswert?

Marcel schrieb:
> was wäre denn vernünftig?

genau so. ;-)
Marcel schrieb:
> if(a() || b()==0)


Manche lieben Klammern, andere Vergleiche auf !=0 und Minimalisten 
schreiben:

> if(a() || !b())

von Klaus W. (mfgkw)


Lesenswert?

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?

von Nachdenklicher (Gast)


Lesenswert?

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>

von FuSa (Gast)


Lesenswert?

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.

von Teo D. (teoderix)


Lesenswert?

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()) {....

von Nop (Gast)


Lesenswert?

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 ^^

von Tobias S. (x12z34)


Lesenswert?

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)

von A. S. (Gast)


Lesenswert?

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)

von Nachdenklicher (Gast)


Lesenswert?

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. ;-)

von Schlaumaier (Gast)


Lesenswert?

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 ;)

von W.S. (Gast)


Lesenswert?

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.

von Schlaumaier (Gast)


Lesenswert?

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.

von A. S. (Gast)


Lesenswert?

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.

von Schlaumaier (Gast)


Lesenswert?

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.

von Hmmm (Gast)


Lesenswert?

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.

von Erwin D. (Gast)


Lesenswert?

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...

von Purzel H. (hacky)


Lesenswert?

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..

: Bearbeitet durch User
von Apollo M. (Firma: @home) (majortom)


Lesenswert?

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()) {
 ...
}

: Bearbeitet durch User
von Dirk B. (dirkb2)


Lesenswert?

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.

von Oliver S. (oliverso)


Lesenswert?

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

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Dirk B. schrieb:
> Mit & statt && kann man sonst mächtig auf die Schnauze fallen.

Ja, mit a = 1 und b = 2 ergibt & falsch, aber && wahr.

von A. S. (Gast)


Lesenswert?

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.

von Dirk B. (dirkb2)


Lesenswert?

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.

von Yalu X. (yalu) (Moderator)


Lesenswert?

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.

von Oliver S. (oliverso)


Lesenswert?

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

von xor (Gast)


Lesenswert?

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".

von Zeno (Gast)


Lesenswert?

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.

von Schlaumaier (Gast)


Lesenswert?

Zeno schrieb:
> Was ist an seiner Abfrage nicht vernünftig? Das erkläre mal.

Habe ich oben.

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

A. S. schrieb:
> Sequencepoints entstehen nicht einfach so

Korrekt, das ist der Schlüsselbegriff, aber das kapieren sowieso nur 
wenige der Spackos!

von Zeno (Gast)


Lesenswert?

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.

von A.S. (Gast)


Lesenswert?

Zeno schrieb:
> Allerdings hat er den Fehler bei seiner Ausweichlösung

xor 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.

von Marcel (Gast)


Lesenswert?

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.

von Teo D. (teoderix)


Lesenswert?

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.

von A. S. (Gast)


Lesenswert?

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();}

von Marcel (Gast)


Lesenswert?

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?

von Zeno (Gast)


Lesenswert?

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.

von A. S. (Gast)


Lesenswert?

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.

von Zeno (Gast)


Lesenswert?

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:
1
if ( !a() )
2
  if ( !b() )
3
  {
4
     code
5
  }
So wird b nur dann ausgeführt wenn a() false ist.

von A. S. (Gast)


Lesenswert?

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.

von Zeno (Gast)


Lesenswert?

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.

von A. S. (Gast)


Lesenswert?

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.

von Yalu X. (yalu) (Moderator)


Lesenswert?

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.

: Bearbeitet durch Moderator
von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Zeno (Gast)


Lesenswert?

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.

von Egon D. (Gast)


Lesenswert?

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...

von Egon D. (Gast)


Lesenswert?

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...

von Egon D. (Gast)


Lesenswert?

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...

von Yalu X. (yalu) (Moderator)


Lesenswert?

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 :)

von Yalu X. (yalu) (Moderator)


Lesenswert?

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.

von Einer (Gast)


Lesenswert?

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.

von Noch ein Kommentar (Gast)


Lesenswert?

> 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.

von Zeno (Gast)


Lesenswert?

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.

von Yalu X. (yalu) (Moderator)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

Yalu X. schrieb:
> (bool)a() | (bool)b()

Kann man auch schreiben als:
1
 !!a() | !!b()

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Norbert (Gast)


Lesenswert?

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…
1
bool a(void) {
2
    puts("a()");
3
    return 1;
4
}
5
bool b(void) {
6
    puts("b()");
7
    return 1;
8
}
9
int main(void) {
10
    auto resa = a();
11
    auto resb = b();
12
    if (resa || resb) {
13
        puts("Hey");
14
    }
15
    return 0;
16
}
Für alle O0…O3 und auch Os gibt's "a() b() Hey"

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Norbert (Gast)


Lesenswert?

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?

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Walter K. (walter_k488)


Lesenswert?

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

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

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
1
if (noShortCutOr(a(), b()) {
2
// ...
3
}

von Norbert (Gast)


Lesenswert?

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!

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von S. R. (svenska)


Lesenswert?

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.

: Bearbeitet durch User
von Klaus W. (mfgkw)


Lesenswert?

Peter D. schrieb:
> Kann man auch schreiben als: !!a() | !!b()

Wenn schon verwirren, dann gleich so:
1
!(!a() & !b())

von A.S. (Gast)


Lesenswert?

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.

von Rolf M. (rmagnus)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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
bool a1 = true;
4
bool b1 = true;
5
6
static bool a() {
7
    return a1;
8
}
9
static bool b() {
10
    return b1;
11
}
12
static bool noShortCutOr(const bool a, const bool b) {
13
    return a || b;
14
}
15
int main() {
16
//    if (noShortCutOr(a(), b())) {
17
//        puts("Hey");
18
//    }
19
    auto resa = a();
20
    auto resb = 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
        cmp     BYTE PTR a1[rip], 0
35
        jne     .L4
36
        cmp     BYTE PTR b1[rip], 0
37
        je      .L6
38
.L4:
39
        push    rax
40
        mov     edi, OFFSET FLAT:.LC0
41
        call    puts
42
        xor     eax, eax
43
        pop     rdx
44
        ret
45
.L6:
46
        xor     eax, eax
47
        ret
48
b1:
49
        .byte   1
50
a1:
51
        .byte   1

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
bool a1 = true;
4
bool b1 = true;
5
6
static bool a() {
7
    return a1;
8
}
9
static bool b() {
10
    return b1;
11
}
12
int main() {
13
    if (a() || b()) {
14
        puts("Hey");
15
    }
16
}

ergibt auch wieder:
1
.LC0:
2
        .string "Hey"
3
main:
4
        cmp     BYTE PTR a1[rip], 0
5
        jne     .L2
6
        cmp     BYTE PTR b1[rip], 0
7
        je      .L5
8
.L2:
9
        push    rax
10
        mov     edi, OFFSET FLAT:.LC0
11
        call    puts
12
        xor     eax, eax
13
        pop     rdx
14
        ret
15
.L5:
16
        xor     eax, eax
17
        ret
18
b1:
19
        .byte   1
20
a1:
21
        .byte   1

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 ...

von A. S. (Gast)


Lesenswert?

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).

von Yalu X. (yalu) (Moderator)


Lesenswert?

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, ...

von Zeno (Gast)


Lesenswert?

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.

von A. S. (Gast)


Lesenswert?

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?

von Einer (Gast)


Lesenswert?

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
extern int a();
2
extern int b();
3
4
void test1() 
5
{
6
  int x = a();
7
  int y = b();
8
  
9
  if (x || y)
10
    puts("test1");
11
}
12
13
void test2()
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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Einer (Gast)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

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
Noch kein Account? Hier anmelden.