Entscheidungen treffen

  • Wie trifft man eine Entscheidung?
  • Den Programmablauf beeinflussen
  • Boolische Ausdrücke formulieren
  • Boolische Ausdrücke verknüpfen

Wie trifft man eine Entscheidung?

Wenn es sich in diesem Kapitel wieder um Datentypen dreht, dann könnte man sich natürlich fragen, ob der Autor dieses Tutorials sonst nichts zu erzählen hat. Tatsächlich ist es so, dass sich immer wieder alles darum dreht, wie man die Bits und Bytes in seinem Rechner interpretiert. Und es zeigt auch, dass die „schweren“ Programmiersprache C und C++ gar nicht so schwer sind, weil sich eigentlich alles immer wieder um Werte und Datentypen - also die Weise, wie man einen Wert interpretiert - dreht.

Um eine Entscheidung zu treffen, muss man einen Wert interpretieren und zwar in der Form, ob man ihn für passend oder unpassend hält. Ob man das eine tut oder das andere. Ja oder nein, Strom an oder aus, wahr oder falsch. Nur mit Aussagen zu arbeiten, die Wahr (true) oder Falsch (false) sind, nennt man Boolesche Algebra. Ein Ausdruck, der sich damit beschäftigt, ob eine Aussage wahr oder falsch ist, nennt man booleschen Ausdruck.

Was auch immer es für Datentypen gibt, für C ist die Sache sehr einfach: Ist der Wert 0, so wird das als false interpretiert, nein, Strom aus, unpassend interpretiert. Hat man einen beliebigen Wert, der sich von 0 unterscheidet, so wird das als passend, Strom an, ja und true.

So trifft man eine Entscheidung: Ist der Wert ungleich 0, dann tue etwas, sonst tue etwas anderes.

Den Programmablauf beeinflussen

Den Programmverlauf steuert man wie zuvor beschrieben. Das Schlüsselwort dafür heißt „if“, gefolgt von einem Ausdruck in runden Klammern:

#include <stdio.h>
 
int main( void )
{
  int decision = 1;
 
  if( decision )   // wenn decision ungleich 0 ist, dann...
  {
    printf( "decision ist ungleich 0\n" );  
  }
 
  return 0;
}

Kompilieren wir das Programm und führen es aus, so sehen wir den Text decision ist ungleich 0 auf dem Monitor. Ändern wir nun die Zeile

  int decision = 1;

auf

  int decision = 0;

so erscheint keine Ausgabe mehr: Das Programm führt die Befehle hinter dem Ausdruck, der die Bedingung beschreibt, nicht mehr aus.

Wir schrieben zuvor aber „Ist der Wert ungleich 0, dann tue etwas, sonst tue etwas anderes.“ Es gibt also die Möglichkeit etwas zu tun, wenn der Wert decision 0 entspricht. Hierfür wird ein 'else' hinter die Anweisungen geschrieben, wo genauso in geschweiften Klammern Anweisungen stehen können.

Den Programmabschnitt der durchgeführt wird, wenn der boolesche Ausdruck true ergibt, nennt man Then-Part (der „Dann-Abschnitt“) und den Abschnitt, der ausgeführt wird, wenn der boolesche Ausdruck false ergibt, nennt man Else-Part (der „Sonst-Abschnitt“). Im nachfolgenden Beispielquelltext habe ich die jeweiligen Parts durch einen Kommentar (Textpassagen zwischen /* und */ werden vom Compiler ignoriert) markiert.

Wir können somit jetzt den Programmablauf anhand einer Variablen verzweigen.

#include <stdio.h>
 
int main( void )
{
  int decision = 1;
 
  if( decision )    /* "Then-Part" */
  {
    printf( "decision ist ungleich 0\n" );  
  }
  else              /* "Else-Part" */
  {
    printf( "decision ist gleich 0\n" );
  }   
 
  return 0;
}

Der Else-Part ist optional, kann also weggelassen werden. Der Then-Part hingegen muss nach dem if geschrieben werden.

Boolische Ausdrücke formulieren

Wir wissen nun, dass wir Entscheidungen mit dem Vergleich gegen 0 durchführen können. Bisher haben wir Existenzabfragen formuliert. Warum die so heißen, erfahren wir bald. Das kann allerdings teilweise zu recht verkorkstem Denkweisen führen: Wenn wir in unseren Beispielprogramm eine Variable mit dem Wert 7 vergleichen wollen, dann müssen wir einen Ausdruck finden, der nur dann ungleich 0 ist, wenn die Variable 7 ist.

Mit unseren bisherigen Mitteln können wir nur folgendes schreiben: Wir ziehen den gesuchten Wert 7 ab, dann ist der Ausdruck decision - 7 genau dann falsch, wenn decision den gesuchten Wert entspricht:

#include <stdio.h>
 
int main( void )
{
  int decision = 7;
 
  if( decision - 7 )
  {
    printf( "Der ungewünschte Fall: decision ist nicht 7\n" );  
  }
  else              
  {
    printf( "Der gewünschte Fall: decision ist gleich 7\n" );
  }   
 
  return 0;
}

Es ist also möglich, aber schön ist das natürlich nicht. Daher gibt es neben den mathematischen Operatoren auch boolesche Operatoren, mit deren Hilfe man Werte vergleichen kann:

Operator Bedeutung
== wahr, wenn gleich
!= wahr, wenn ungleich
> wahr, wenn linke Seite größer als rechte Seite
>= wahr, wenn linke Seite größer oder gleich als rechte Seite
< wahr, wenn linke Seite kleiner als rechte Seite
<= wahr, wenn linke Seite kleiner oder gleich als rechte Seite
! wahr, wenn der Ausdruck an der rechten Seite falsch ist

Die Operatoren geben einen booleschen Wert zurück, nämlich true oder false, also genau das, was wir benötigen, um eine Entscheidung zu treffen.

Wir können unsere Frage jetzt also leichter und verständlicher formulieren:

#include <stdio.h>
 
int main( void )
{
  int decision = 7;
 
  if( decision == 7 )
  {
    printf( "Der gewünschte Fall: decision ist gleich 7\n" );
  }
  else              
  {
    printf( "Der ungewünschte Fall: decision ist nicht 7\n" );  
  }   
 
  return 0;
}

Nehmen wir noch ein ein paar Vergleiche als Beispiel, damit man sie mal gesehen hat:

Beispiel Bedeutung
if( decision > 7 ) Wenn decision größer als 7 ist, dann…
if( decision <= 7 ) Wenn decision kleiner oder gleich 7 ist, dann…
if( !(decision <= 7) ) Wenn decision nicht kleiner oder gleich 7 (also größer als 7) ist , dann…
if( !(decision > 7) ) Wenn decision nicht größer als 7 (also kleiner oder gleich 7) ist , dann…

Schachteln von Abfragen

If-Abfragen kann man auch schachteln:

#include <stdio.h>
 
int main( void )
{
  int mayBeDigit = '0';
 
  if( mayBeDigit >= '0' )
  {
    if( mayBeDigit <= '9' )
    {
      printf( "'%c' ist eine Ziffer\n", mayBeDigit );
    }  
    else
    {
      printf( "'%c' ist keine Ziffer\n", mayBeDigit );
    }   
  }
  else
  {
    printf( "'%c' ist keine Ziffer\n", mayBeDigit );
  }   
 
  return 0;
}

Die booleschen Vergleichsoperatoren (==, !=, > usw.) funktionieren genauso wie mathematische Operatoren: Es wird ein Ergebnis zurückgeliefert, das entweder 0 (für false) oder 1 (für true) ist. Damit lassen sich auch Ausdrücke schreiben wie:

int a = 4 > 1;

a ist nun 1 (true).

Die logische Negation (!-Operator) entspricht der mathematischen Negation. -a ist der negative Wert von a. !a ist das logische Gegenteil von a: aus true wird false, aus false wird true.

Boolische Ausdrücke verknüpfen

Das vorherige Beispiel zeigt ein neues Problem auf, um das wir uns noch schnell kümmern wollen: Wir stellen zwei Fragen, die eigentlich zusammen gehören, um eine Entscheidung zu treffen. Weil wir zwei Fragen stellen, bekommen wir auch zwei Möglichkeiten, die Antwort 'false' zu erhalten und hier wird in beiden Fall gleich reagiert. Es wird also Quelltext wiederholt, was man möglichst vermeiden sollte.

Um diese beiden Fragen zusammenzuführen, müssen wir sie zu einer großen Frage verknüpfen. Diese Verknüpfung von Ausdrücken nennt man Junktion.

In C werden zwei Junktoren verwendet: das logische Und (auch logische Multiplikation) und das logische Oder (auch logische Addition genannt). Die Operatoren heißen hier && und || und sie geben wieder einen booleschen Wert zurück.

Im obenstehenden Beispiel muss ein Wert größer als '0' sein oder der '0' entsprechen UND die der Wert muss kleiner als '9' sein oder der '9' entsprechen, dann ist es eine Ziffer, sonst nicht.

Der &&-Operator liefert nur dann true, wenn beide Ausdrücke links und rechts true ergeben, sonst false - einer reicht nicht. Der ||-Operator hingegen liefert true wenn mindestens einer der beiden Ausdrücke true ergibt - hier reicht eine Seite.

Formulieren wir das Programm in C:

#include <stdio.h>
 
int main( void )
{
  int mayBeDigit = '0';
 
  if( mayBeDigit >= '0' && mayBeDigit <= '9' )
  {
    printf( "'%c' ist eine Ziffer\n", mayBeDigit );
  }  
  else
  {
    printf( "'%c' ist keine Ziffer\n", mayBeDigit );
  }   
 
  return 0;
}

Da wir nur noch eine Frage haben, müssen wir uns auch nur noch um einen Then-Part und einen Else-Part kümmern, die Wiederholung fällt damit weg.

Wenn wir die Frage andersherum formulieren, drehen sich alle Operatoren um: Wenn der Wert kleiner als '0' ist ODER größer als '9', dann ist es keine Ziffer, sonst ist es eine Ziffer:

#include <stdio.h>
 
int main( void )
{
  int mayBeDigit = '0';
 
  if( mayBeDigit < '0' || mayBeDigit > '9' )
  {
    printf( "'%c' ist keine Ziffer\n", mayBeDigit );
  }  
  else
  {
    printf( "'%c' ist eine Ziffer\n", mayBeDigit );
  }   
 
  return 0;
}

Man sollte Fragen so formulieren, dass sie leicht verständlich sind.

  if( mayBeDigit >= '0' && mayBeDigit <= '9' )

entspricht

  if( !(mayBeDigit < '0' || mayBeDigit > '9') )

aber die erste Variante ist für die meisten vermutlich leichter zu verstehen.

Ziel dieser Lektion

Du solltest nun wissen, wann ein Wert als true (wahr) interpretiert wird und wann als false (falsch) und Du kannst boolesche Ausdrücke mit Vergleichen und Negationen formulieren. Du solltest Abfragen machen können und boolesche Werte miteinander verknüpfen können, um aufwendigere Abfrage zusammen zu fassen.

Nimm Dir etwas Zeit, um einige kleine Programme mit Vergleichen zu schreiben, auszuprobieren und die Resultate nachzuvollziehen.

Danach schauen wir uns in der nächsten Lektion Wiederholungen an.