Forum: Compiler & IDEs Vorzeitiger Abbruch von boolschen Verknüpfungen


von Bronco (Gast)


Lesenswert?

Hallo zusammen,

wenn ich folgendes programmiere:
1
bool ergebnis = BerechneA() && BerechneB();
und die Funktion "BerechneA()" liefert false zurück, dann steht jetzt 
schon fest, dass das Ergebnis false sein wird und daher wird meines 
Wissens die Funktion "BerechneB()" gar nicht mehr aufgerufen.

Frage:
Ist dieses Verhalten Compiler-spezifisch oder ist es in C/C++ so 
festgelegt?
Und wie nennt man das im Fachjargon?

Danke!

von B. S. (bestucki)


Lesenswert?

Bronco schrieb:
> Ist dieses Verhalten Compiler-spezifisch oder ist es in C/C++ so
> festgelegt?

Das ist in C so festgelegt:
> Unlike the bitwise binary & operator, the && operator guarantees left-
> to-right evaluation; if the second operand is evaluated, there is a
> sequence point between the evaluations of the first and second operands.
> If the first operand compares equal to 0, the second operand is not
> evaluated.
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
Kapitel 6.5.13

von (prx) A. K. (prx)


Lesenswert?

Bronco schrieb:
> Und wie nennt man das im Fachjargon?

short-circuit evaluation

von Udo S. (urschmitt)


Lesenswert?

Bronco schrieb:
> bool ergebnis = BerechneA() && BerechneB();

Dieses "Zeilensparen" stammt noch aus der Zeit, als man jede Zeile 
einzeln in eine Lochkarte stanzen musste.
Heute gibt dir keiner Geld dafür, daß du den gleichen Code in 2 zeilen 
weniger packen kannst.
Also machs sauber nacheinander.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Udo Schmitt schrieb:
> Dieses "Zeilensparen"

Was genau magst Du meinen?

Und was willst Du "sauber nacheinander" machen?

von Oliver S. (oliverso)


Lesenswert?

Nennt sich z.B "short cicuit"

Und ist im C-und C++-Standard so festgelegt.

Für den logical and operator liest sich das z.B so:

>Unlike &, && guarantees left-to-right
>evaluation: the second operand is not evaluated if the first operand is  false.

Oliver

von Dussel (Gast)


Lesenswert?

Hier scheint es wohl um C zu gehen, oder? In Java gibt es für genau 
diesen Fall spezielle Operatoren

von (prx) A. K. (prx)


Lesenswert?

Dussel schrieb:
> Hier scheint es wohl um C zu gehen, oder?

Das soll im "GCC" Forum gelegentlich vorkommen.

von (prx) A. K. (prx)


Lesenswert?

Aber wer es etwas ausführlicher haben will: 
http://rosettacode.org/wiki/Short-circuit_evaluation

von Bronco (Gast)


Lesenswert?

Okay, danke!

von Yalu X. (yalu) (Moderator)


Lesenswert?

Dussel schrieb:
> Hier scheint es wohl um C zu gehen, oder? In Java gibt es für genau
> diesen Fall spezielle Operatoren

Nämlich &&, genauso wie in C, oder nicht?

von Markus F. (mfro)


Lesenswert?

Udo Schmitt schrieb:
> Bronco schrieb:
>> bool ergebnis = BerechneA() && BerechneB();
>
> Dieses "Zeilensparen" stammt noch aus der Zeit, als man jede Zeile
> einzeln in eine Lochkarte stanzen musste.
> Heute gibt dir keiner Geld dafür, daß du den gleichen Code in 2 zeilen
> weniger packen kannst.
> Also machs sauber nacheinander.

"Sauber nacheinander" heißt dann aber u.U. auch "schön langsam". Der 
"Kurzschluß" ist ein durchaus erwünschtes Programmierkonstrukt:
1
if (!zweimillionste_stelle_von_pi_berechnet(&stelle) &&
2
        berechne_zweimillionste_stelle_von_pi(&stelle))
3
    printf("zweimillionste Stelle von pi = %d\n", stelle);
Hat eher mit Zyklen- als mit Zeilensparen zu tun. Hier würd' ich lieber 
nicht "alles sauber nacheinander" machen wollen ;).

Aufpassen muß man natürlich darauf, daß der zweite Aufruf nach dem '&&' 
nirgendwo irgendwelche Seiteneffekte hat, von denen der weiteren 
Programmablauf abhängt.

von thestrangler (Gast)


Lesenswert?

Udo Schmitt schrieb:
> Heute gibt dir keiner Geld dafür, daß du den gleichen Code in 2 zeilen
> weniger packen kannst.
> Also machs sauber nacheinander.

Also in 3 Zeilen:

bool ergebnis = BerechneA();
if(ergebnis)
  ergebnis = BerechneB();

oder

bool ergebnis = false;
ergebnis = BerechneA();
if(ergebnis) ergebnis = BerechneB();

Beide Varianten sind meiner Meinung nach deutlich schlechter lesbar als

bool ergebnis = BerechneA() && BerechneB();

von Dussel (Gast)


Lesenswert?

A. K. schrieb:
> Das soll im "GCC" Forum gelegentlich vorkommen.
Ja, aber im Home-Bereich ist das nicht so ersichtlich ;-)
Ich bin direkt von der Startseite zu dem Beitrag gekommen. Erst nach dem 
Absenden habe ich gesehen, welches Forum das ist.

Yalu X. schrieb:
> Dussel schrieb:
>> Hier scheint es wohl um C zu gehen, oder? In Java gibt es für genau
>> diesen Fall spezielle Operatoren
>
> Nämlich &&, genauso wie in C, oder nicht?
Da wird nur solange ausgewertet, bis der Ausdruck sicher bestimmt ist. 
Ein einfaches & oder | im Ausdruck wertet immer beide Ausdrücke aus.
http://openbook.galileocomputing.de/javainsel/javainsel_02_004.html#dodtp44c70ec5-17d6-4493-bac6-218399d01cf8

von Yalu X. (yalu) (Moderator)


Lesenswert?

Dussel schrieb:
> Ein einfaches & oder | im Ausdruck wertet immer beide Ausdrücke aus.

Ja, die bitweisen Operatoren, werten immer beide Operanden aus, weil es
streng genommen keine logischen, sondern Rechenoperatoren (wie + und -)
sind, auf die die Kurzschlussauswertung für die meisten Werte nicht
anwendbar ist.

Das ist in C aber exakt gleich.

: Bearbeitet durch Moderator
von Bronco (Gast)


Lesenswert?

thestrangler schrieb:
> Beide Varianten sind meiner Meinung nach deutlich schlechter lesbar als
> bool ergebnis = BerechneA() && BerechneB();

Einen Punkt hat der Udo schon.

Bei
1
bool ergebnis = IrgendetwasUnwichtiges() && SuperGAUVerhindern();

wird wahrscheinlich nicht jedem sofort klar, dass der SuperGAU nur dann 
verhindert wird, wenn irgendetwas unwichtiges true ist.
1
bool ergebnis = IrgendetwasUnwichtiges();
2
if ( ergebnis  ) 
3
{ 
4
  ergebnis = SuperGAUVerhindern(); 
5
  // Ups, Fehler: Den SuperGAU sollte ich IMMER verhindern!!!
6
}

von Udo S. (urschmitt)


Lesenswert?

thestrangler schrieb:
> bool ergebnis = false;
> ergebnis = BerechneA();
> if(ergebnis) ergebnis = BerechneB();
>
> Beide Varianten sind meiner Meinung nach deutlich schlechter lesbar als
>
> bool ergebnis = BerechneA() && BerechneB();

Sind schlechter lesbar, dafür aber richtig.

Es geht doch darum, daß berechneB() unbedingt ausgeführt werden muss, 
weil nämlich berechneB() irgendein (globalen) Wert berechnet, der später 
gebraucht wird. (So zumindest war mein Verständnis der Anfangsfrage.)

Und genau das ist so in dem Einzeiler nicht erkennbar und somit für 
jemand anderen, der den Code nicht selbst geschrieben hat nicht ohne 
weiteres zu erkennen.
Wenn also BerechneA() und BerechneB() auf jeden Fall ausgeführt werden 
müssen, weil sie eben noch mehr machen als nur das boolsche Ergebnis für 
den Vergleich zurückzuliefern, dann sollte das auch im Programm deutlich 
werden und eher so aussehen:

bool ergebnisA = berechneA();
bool ergebnisB = berechneB();
if (ergebnisA && ergebnisB) ...

Zumindest so hatte ich die Anfangsfrage verstanden, daß nämlich der 
Abbruch einer boolschen Auswertung zu Problemen führt weil dann 
berechneB() nicht mehr ausgeführt wird.
Wenn ich das missverstanden haben sollte, dann habt ihr recht, ein

bool ergebnis = BerechneA() && BerechneB();

ist völlig ok und übersichtlich.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Udo Schmitt schrieb:
> Zumindest so hatte ich die Anfangsfrage verstanden, daß nämlich der
> Abbruch einer boolschen Auswertung zu Problemen führt weil dann
> berechneB() nicht mehr ausgeführt wird.

Ich glaube, das hast du nur in die Frage hineininterpretiert.

Wer Funktionen mit derartigen Seiteneffekten zimmert und dann in
einer Kurzschlussoperation benutzt, hat aber sowieso ein ganz anderes
Problem.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Udo Schmitt schrieb:
> Zumindest so hatte ich die Anfangsfrage verstanden, daß nämlich der
> Abbruch einer boolschen Auswertung zu Problemen führt weil dann
> berechneB() nicht mehr ausgeführt wird.

Ich glaube, er meinte genau das Gegenteil.

Bronco schrieb:
> Ist dieses Verhalten Compiler-spezifisch oder ist es in C/C++ so

Das habe ich so interpretiert, dass er die Kurzschlussauswertung gerne
nutzen möchte, er sich aber nicht sicher war, ob sie wirklich auch in
jedem C-Compiler implementiert ist.

von Oliver S. (oliverso)


Lesenswert?

Bronco schrieb:
> wird wahrscheinlich nicht jedem sofort klar, dass der SuperGAU nur dann
> verhindert wird, wenn irgendetwas unwichtiges true ist.

Tja, es ist so wie es ist: man sollte die Grundregeln der Sprache halt 
kennen.
Vermuten reicht nicht.

Oliver

von Dussel (Gast)


Lesenswert?

Yalu X. schrieb:
> Ja, die bitweisen Operatoren, werten immer beide Operanden aus, weil es
> streng genommen keine logischen, sondern Rechenoperatoren (wie + und -)
> sind, auf die die Kurzschlussauswertung für die meisten Werte nicht
> anwendbar ist.
Ich weiß nicht, ob das in Java auch so ist. In dem verlinkten Buch steht 
es als neue Operatoren. Und da in Java Wahrheitswerte keine Zahlen sind, 
denke ich, dass das in Java durchaus neue logische Operatoren sein 
können.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Dussel schrieb:
> Ich weiß nicht, ob das in Java auch so ist. In dem verlinkten Buch steht
> es als neue Operatoren. Und da in Java Wahrheitswerte keine Zahlen sind,
> denke ich, dass das in Java durchaus neue logische Operatoren sein
> können.

Du hast recht, ich aber auch :)

Hab's gerade ausprobiert: In Java scheint der &-Operator polymorph zu
sein. Es gibt ihn sowohl für int- als auch für boolean-Operanden. Im
ersten Fall werden die beiden Operanden wie in C bitweise und-verknüpft,
im zweiten liefern die bitweise und die logische Und-Verknüpfung sowieso
dasselbe Ergebnis, da die boolean-Werte de facto nur ein (relevantes)
Bit enthalten.

Der Compiler stellt dabei sicher, dass, wenn einer der beiden Opernden
boolean ist, dies auch für den anderen gilt. Somit kann man den
&-Operator tatsächlich problemlos auch für logisches Und verwenden.

In C hingegen werden boolean-Werte vor der eigentlichen Verknüpfung nach
int konvertiert, was die Verwendung von & als Logikoperation etwas
problemantisch macht, denn

  2 && 1         (logisches Und) ergibt wie erwartet 1 (true),
  true & true    ebenfalls,

aber

  2 & true       (entspricht 2 & 1) ergibt 0 (false)

Dieser dritte Fall führt in Java zu einer Fehlermeldung.

: Bearbeitet durch Moderator
von Falk B. (falk)


Lesenswert?

@ Bronco (Gast)

>bool ergebnis = IrgendetwasUnwichtiges() && SuperGAUVerhindern();

>wird wahrscheinlich nicht jedem sofort klar, dass der SuperGAU nur dann
>verhindert wird, wenn irgendetwas unwichtiges true ist.

Das ist so oder so ein Programmierfehler! Denn so eine 
SuperGAUverhinderungsfunktion hat in dieser Konstruktion SO oder SO 
NICHTS zu suchen! Das ist einer der "netten" Seiteneffekte von C, die 
man tunlichst NICHT nutzen sollte. Eigentlich sollten solche Funktionen 
MIT Seiteneffekten (Schreibzugriff auf globale Daten, IOs etc.) NIE in 
logischen Ausdrücken auftauchen! Dort dürfen nur rein passive Funktionen 
stehen! (Wenn man es sauber machen will)

von Stefan R. (srand)


Lesenswert?

A. K. schrieb:
> Dussel schrieb:
>> Hier scheint es wohl um C zu gehen, oder?
>
> Das soll im "GCC" Forum gelegentlich vorkommen.

gcc übersetzt auch Java.

von Thorsten (Gast)


Lesenswert?

Ich verwende gerne Konstrukte wie:

if (foo && foo->bar && foo->bar->foobar) {
  ...
}

um auf verschachtelte Pointer-Strukturen zuzugreifen, wenn nicht 
sichergestellt ist, dass auch alle Pointer auf etwas sinnvolles zeigen. 
Da ist dann das Short-Cirucuit Verhalten äußerst hilfreich und man 
vermeidet mehrfach verschachtelte if-Konstrukte.

von Bronco (Gast)


Lesenswert?

Yalu X. schrieb:
>> Zumindest so hatte ich die Anfangsfrage verstanden, daß nämlich der
>> Abbruch einer boolschen Auswertung zu Problemen führt weil dann
>> berechneB() nicht mehr ausgeführt wird.
>
> Ich glaube, er meinte genau das Gegenteil.

Ich kann's Euch sagen ;-)

Ich benutze Kurzschlussauswertung (auch wenn ich ihren Namen nicht 
kannte) bewußt in Konstrukten wie
1
if ( (pointer) != 0 && (pointer->Funktion() == OKAY) )

Jetzt hab ich folgendes gebaut:
1
bool success = true; // Soll anzeigen, ob es 10x geklappt hat
2
for (i = 0; i < 10; i++)
3
{
4
  success &= Bitte10xAufrufen();
5
}
Das funktioniert, weil "&=" bitweise verknüpft und keine 
Kurzschlussauswertung stattfindet.

Dann dachte ich mir: "100% korrekt ist das aber nicht, bool und bitweise 
zu vermischen" und hab folgendes draus gemacht:
1
bool success = true; // Soll anzeigen, ob es 10x geklappt hat
2
for (i = 0; i < 10; i++)
3
{
4
  success = success && Bitte10xAufrufen();
5
}
Und da bin ich prompt auf die Nase geflogen: Sobald success einmal false 
ist, wird Bitte10xAufrufen() nicht mehr aufgerufen.

Wenn man in den Assembler-Code schaut, ist es auch absolut logisch:
Bitweise AND (&) und OR (|) ergibt echte Rechenbefehl der ALU.
Bootlsches AND (&&) und OR (||) ergibt Programmfluss (Call, Compare, 
Jump), und damit wird die Auswertung des zweiten Operanten ggf. einfach 
übersprungen.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Bronco schrieb:
> Jetzt hab ich folgendes gebaut:
>  bool success = true; // Soll anzeigen, ob
>  es 10x geklappt hat
>  for (i = 0; i < 10; i++)
>  {
>    success &= Bitte10xAufrufen();
>  }

Es ist halt wichtig, dass Bitte10xAufrufen() für "true" eine 1 und nicht
irgendeine andere von 0 verschiedene Zahl zurückgibt. Idealerweise ist
Bitte10xAufrufen() vom Typ bool, dann bist du auf der sicheren Seite.

>  success = success && Bitte10xAufrufen();

Es funktioniert auch mit &&, wenn du die Operanden vertauschst:
1
  success = Bitte10xAufrufen() && success;

von Hans (Gast)


Lesenswert?

Oder halt eine Zeile mehr spendieren. Tut in dem Fall ja nicht weh.
1
bool callSuccess = Bitte10xAufrufen();
2
success = success && callSuccess;

von Udo S. (urschmitt)


Lesenswert?

Yalu X. schrieb:
> Es funktioniert auch mit &&, wenn du die Operanden vertauschst:
>   success = Bitte10xAufrufen() && success;

Ist aber meiner Meinung nach gaz schlechter Stil. Was passiert wenn ein 
Kollege zwei Jahre später in dem Ausdruck was ändert oder erweitert. Da 
wird ganz schnell ein Fahler eingebaut.
Dann lieber wie ich es oben besagt habe oder wie Hans es auch gesagt 
hat.

Der wichtigste (und meiner Meinung nach auch völlig ok) Verwendungsfall 
ist der einen pointer oder Objekt auf null zu prüfen ehe man ihn 
referenziert/benutzt.

von Udo S. (urschmitt)


Lesenswert?

Falk Brunner schrieb:
> Eigentlich sollten solche Funktionen
> MIT Seiteneffekten (Schreibzugriff auf globale Daten, IOs etc.) NIE in
> logischen Ausdrücken auftauchen! Dort dürfen nur rein passive Funktionen
> stehen! (Wenn man es sauber machen will)

Sehe ich ganz genauso. Siehe Beitrag oben.

von Oliver S. (oliverso)


Lesenswert?

Bronco schrieb:
> success &= Bitte10xAufrufen();

Bronco schrieb:
> success = success && Bitte10xAufrufen();

Die beiden Zeilen machen aber nicht das selbe, nicht mal das gleiche...

&= ist nicht ... = ... && ...

Oliver

von Amateur (Gast)


Lesenswert?

Ich hatte das Gefühl, dass es dem TO darum ging, dass die zweite 
Funktion erst gar nicht aufgerufen wird. Wenn die erste Funktion false 
liefert.

Ich würde es mit:
bool ergebnis = BerechneA();
ergebnis = BerechneB() && ergebnis;
versuchen.

Irgendwo in den tiefen von C gibt es doch noch die Grundregel:
"Es geht von links nach rechts", wenn irgendeine Hierarchie nicht was 
Anderes sagt.

von Falk B. (falk)


Lesenswert?

@ Bronco (Gast)


>bool success = true; // Soll anzeigen, ob es 10x geklappt hat
>for (i = 0; i < 10; i++)
>{
>  success &= Bitte10xAufrufen();
>}


>Das funktioniert, weil "&=" bitweise verknüpft und keine
>Kurzschlussauswertung stattfindet.

Nein, das hat damit nix zu tun.

>Dann dachte ich mir: "100% korrekt ist das aber nicht, bool und bitweise
>zu vermischen"

Ist es auch nicht.

> und hab folgendes draus gemacht:

>bool success = true; // Soll anzeigen, ob es 10x geklappt hat
>for (i = 0; i < 10; i++)
>{
>  success = success && Bitte10xAufrufen();
>}

>Und da bin ich prompt auf die Nase geflogen: Sobald success einmal false
>ist, wird Bitte10xAufrufen() nicht mehr aufgerufen.

Falsch. Deine Logik ist fehlerhaft. Die Logik des Programms STIMMT! Auch 
mit Kurzschlußauswertung. Denn sobald EINMAL success == FALSE ist, kann 
es niemals 10x TRUE sein.

von Karl H. (kbuchegg)


Lesenswert?

Amateur schrieb:

> Irgendwo in den tiefen von C gibt es doch noch die Grundregel:
> "Es geht von links nach rechts", wenn irgendeine Hierarchie nicht was
> Anderes sagt.

Vorsicht. Das ist eine grobe Verallgemeinerung, die so nicht gilt.

In
1
   c = a() + b();

hast du keineswegs die Gewähr, dass a() vor b() aufgerufen wird. Das das 
bei
1
   c = a() && b();
so ist, liegt daran, dass && einen Sequence Point impliziert.

D.h. wenn du schon eine Grundregel haben willst, dann halte dich lieber 
an: die Reihenfolge der Auswertungen ist undefiniert, es sei denn es 
handelt sich um einen Sonderfall, für den explizit feststeht, wie die 
Reihenfolge sein muss.
Wichtig, denn das verwechseln viele. Operator Precedence bzw. 
Rechts/Links Assoziation definiert nicht die Auswertereihenfolge! Diese 
Dinge regeln zb, wie die Einzelergebnisse von zb
1
    d = a() + b() * c();
miteinander verrechnet werden müssen. Sie legen aber nicht fest, in 
welcher Reihenfolge die Einzelergebnisse erzielt werden!

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.