Kapitel 9: Pointer (Zeiger)

Inhaltsverzeichnis

Kapitel 11: Schleifen und Kontrollanweisungen

10. Bedingungen

10.1 Einfache Auswahl: IF … THEN

10.1.1 Einzeilige Bedingungen

Während das Computerprogramm zunächst einmal der Reihe nach von der ersten Zeile bis zur letzten abgearbeitet wird, ist es häufig nötig, bestimmte Programmteile nur unter besonderen Voraussetzungen auszuführen. Denken Sie an folgendes Beispiel aus dem Alltag: Wenn es acht Uhr abends ist — und nur dann — soll der Fernseher für die Nachrichten eingeschalten werden. (Ja, es soll tatsächlich Haushalte geben, in denen der Fernseher nicht den ganzen Tag über läuft …)

Wenn es 20:00 Uhr ist Dann schalte den Fernseher ein.

Zunächst tauchen in dem Satz die beiden Schlüsselwörter Wenn und Dann auf. Zwischen den beiden Wörtern steht eine Bedingung (es ist 20:00 Uhr). Diese Bedingung kann erfüllt sein oder nicht — aber nur dann, wenn sie erfüllt ist, wird die anschließende Handlung (schalte den Fernseher ein) durchgeführt.

In FreeBASIC-Syntax sieht das ähnlich aus:

IF bedingung THEN anweisung

Nehmen wir einmal ein einfaches Beispiel: Der Benutzer wird nach seinem Namen gefragt. Wenn dem Programm der Name besonders gut gefällt, findet es dafür lobende Worte.

Quelltext 10.1: Einfache Bedingung (einzeilig)
DIM AS STRING eingabe
INPUT "Gib deinen Namen ein: ", eingabe
IF eingabe = "Stephan" THEN PRINT "Das ist aber ein besonders schoener Name!"
PRINT "Danke, dass du dieses Programm getestet hast."
SLEEP

Die Ausgabe "Das ist aber ein besonders schoener Name!" erscheint nur, wenn die zuvor genannte Bedingung erfüllt ist.

10.1.2 Mehrzeilige Bedingungen (IF-Block)

Nun passiert es häufig, dass unter einer bestimmten Bedingung nicht nur ein einziger Befehl, sondern eine ganze Reihe von Anweisungen ausgeführt werden soll. Alle Anweisungen in eine Zeile zu quetschen, wird schnell unübersichtlich. Wenn daher auf das THEN keine Anweisung, sondern ein Zeilenumbruch erfolgt, bedeutet das für den Compiler, dass die folgenden Zeilen nur unter der gegebenen Bedingung ausgeführt werden sollen. Allerdings muss dann explizit festgelegt werden, wo dieser Anweisungsblock enden soll. Dazu dient der Befehl END IF.

In „Alltagssprache“ könnte das etwa so aussehen:

Wenn du Hunger hast Dann
  Oeffne Keksdose
  Nimm Keks
  Schliesse Keksdose
  Iss Keks
Ende Wenn

Für die FreeBASIC-Umsetzung bauen wir Quelltext 10.1 ein klein wenig um:

Quelltext 10.2: Einfache Bedingung (mehrzeilig)
DIM AS STRING eingabe
INPUT "Gib deinen Namen ein: ", eingabe
IF eingabe = "Stephan" THEN
  PRINT "Das ist aber ein besonders schoener Name!"
  ' Hier koennten jetzt noch weitere Befehle folgen
END IF
PRINT "Danke, dass du dieses Programm getestet hast."
SLEEP

Diese Variante wird auch als IF-Block bezeichnet. Natürlich kann sie auch genutzt werden, wenn nur eine einzige Anweisung ausgeführt werden soll. Manche Programmierer bevorzugen es, IF-Abfragen ausschließlich mit Blöcken umzusetzen, weil dadurch Bedingungsabfrage und resultierende Anweisung klar voneinander getrennt sind. Dies kann sich positiv auf die Lesbarkeit des Quelltextes auswirken.

Apropos Lesbarkeit: Sicher sind Ihnen die Einrückungen in den beiden letzten Beispielen aufgefallen. Solche Einrückungen spielen für den Compiler keine Rolle; ihm ist es völlig egal, ob Sie Ihre Zeilen einrücken und wie weit. Wichtig sind sie jedoch für den Leser des Quelltextes — also für alle, welche die Funktionsweise des Programmes verstehen und vielleicht auch verbessern und weiterentwickeln wollen, und natürlich für den Programmierer selbst!

Um den Quelltext lesbar zu gestalten, werden alle Zeilen, die auf derselben „logischen Ebene“ stehen, gleich weit eingerückt. In Quelltext 10.2 werden die ersten drei Zeilen immer ausgeführt (auch die Bedingungsabfrage findet immer statt). Daher befinden sie sich auf der „obersten“ Ebene, sind also nicht eingerückt. Zeilen 4 und 5 werden nur unter der genannten Bedingung ausgeführt. Sie befinden sich eine Ebene „tiefer“ und werden eingerückt. Da sie sich beide auf derselben Ebene befinden  — ist die Bedingung erfüllt, werden beide Zeilen berücksichtigt — wurden sie auch gleich weit eingerückt. END IF beendet den IF-Block und steht damit wieder in derselben Einrückungsebene wie Zeile 3, ebenso wie die restlichen Zeilen, die unabhängig von der Bedingung in jedem Fall ausgeführt werden.

Wie stark die Zeilen eingerückt werden, wird von Programmierer zu Programmierer unterschiedlich gehandhabt. Üblich sind Einrückungen von zwei bis vier Leerzeichen pro Ebene oder, alternativ dazu, die Verwendung von Tabulatoren. Bleiben Sie jedoch innerhalb einer Quelltext-Datei bei einer dieser Möglichkeiten: verwenden Sie entweder immer zwei Leerzeichen oder immer vier Leerzeichen oder immer Tabulatoren. Die Arten zu mischen sorgt lediglich für Verwirrung, da man dann nicht mehr auf den ersten Blick einwandfrei die Ebenentiefe erkennen kann. Leerzeichen und Tabulatoren zu mischen ist sowieso eine schlechte Idee, allein schon, weil unterschiedliche Editoren einen Tabulator auch unterschiedlich weit eingerückt darstellen können. Was in Ihrem Editor dann gleichmäßig aussieht, ist bei einem anderen möglicherweise sehr ungleichmäßig.

Ein sauberer Umgang mit Einrückungen ist vor allem wichtig, wenn mehrere Blöcke ineinander verschachtelt sind:

Quelltext 10.3: Verschachtelte Bedingungen
DIM username AS STRING, alter AS INTEGER
INPUT "Gib deinen Namen ein: ", username
IF username = "Stephan" THEN
  PRINT "Das ist aber ein besonders schoener Name!"
  INPUT "Wie alt bist du? ", alter
  IF alter = 0 THEN
    PRINT "Dann bist du ja vor nicht einmal einem Jahr geboren worden!"
  END IF
END IF
PRINT "Danke, dass du dieses Programm getestet hast."
SLEEP

Die Benutzereingabe des Alters und die daraus resultierende Bedingungsabfrage erfolgen nur, wenn bereits der richtige Name eingegeben wurde. Ansonsten springt das Programm schon von vornherein in die Zeile 10. IF-Blöcke können beliebig ineinander verschachtelt werden. Jedoch muss jeder IF-Block auch mit einem END IF beendet werden.

Note Hinweis:
Statt END IF findet man in manchen Quelltexten auch die zusammengeschriebene Form ENDIF. Diese wird in einigen anderen BASIC-Dialekten für den Abschluss des Blockes verwendet und wird in FreeBASIC aus Kompatibilitätsgründen ebenso unterstützt.

10.1.3 Alternativauswahl

In der bisherigen einfachen Form wird der Code nur ausgeführt, wenn die Bedingung erfüllt ist; ansonsten wird das Programm nach dem END IF fortgesetzt. Man kann allerdings auch Alternativen festlegen, die nur dann ausgeführt werden, wenn die Bedingung nicht erfüllt ist. Dazu dienen die beiden Schlüsselwörter ELSEIF und ELSE.

IF bedingung1 THEN
  anweisungen
ELSEIF bedingung2 THEN
  anweisungen
ELSEIF bedingung3 THEN
  anweisungen
' ...
ELSE
  anweisungen
END IF

Zuerst wird bedingung1 geprüft. Ist sie erfüllt, werden die Anweisungen nach dem THEN ausgeführt. Die ELSEIF-Blöcke werden in diesem Fall übersprungen. Ist bedingung1 dagegen nicht erfüllt, werden der Reihe nach die Bedingungen hinter den ELSEIF-Zeilen geprüft, bis das Programm auf die erste zutreffende Bedingung stößt. Der darauf folgende Anweisungsblock wird dann ausgeführt. Die Anweisungen nach dem ELSE kommen nur dann zum Tragen, wenn keine einzige der abgearbeiteten Bedingungen erfüllt war.

Ein IF-Block kann beliebig viele (auch keine) ELSEIF-Zeilen enthalten, aber höchstens eine ELSE-Zeile.

Quelltext 10.4: Mehrfache Bedingung
DIM AS INTEGER alter
INPUT "Gib dein Alter ein: ", alter
IF alter < 0 THEN
  ' Das Alter ist kleiner als 0
  PRINT "Das Alter kann nicht negativ sein!"
ELSEIF alter < 3 THEN
  ' Das Alter ist mind. 0, aber kleiner als 3
  PRINT "Bist du nicht noch zu jung?"
ELSEIF alter < 18 THEN
  ' Das Alter ist mind. 3, aber kleiner als 18
  PRINT "Du bist noch nicht volljaehrig!"
ELSE
  ' Das Alter ist mind. 18
  PRINT "Dann bist du ja volljaehrig!"
END IF
SLEEP

Wenn Sie Quelltext 10.4 mehrmals mit verschiedenen Alterseingaben ausführen, werden Sie sehen, dass immer nur der erste Abschnitt ausgeführt wird, dessen Bedingung erfüllt ist.

10.2 Bedingungsstrukturen

Bedingungen können, wie gesagt, wahr oder falsch sein. Nun würde sich zur Verwaltung von Wahrheitswerten natürlich ein BOOLEAN anbieten. FreeBASIC steht hier jedoch stark in der Tradition alter BASIC-Dialekte, die es ermöglichen, Wahrheitswerte auch in aufwändige Rechnungen einzubauen. Daher verwendet FreeBASIC — wie schon sein Vorgänger QuickBASIC — für Vergleiche die Ergebnisse 0 für falsch und -1 für wahr. Allerdings wird jeder beliebige Zahlenwert ungleich 0 als wahr interpretiert. Beispielsweise würde auch dies hier funktionieren:

IF 2+3 THEN PRINT "Der Ausdruck '2+3' ist wahr."

2+3 ist nicht 0, und daher wird der anschließende Text angezeigt. Aus diesem Grund wird in diesem Buch zwischen dem Wahrheitswert wahr und dem BOOLEAN true unterschieden. Nichtsdestotrotz kann der Wert eines Vergleiches (0 oder -1) auch einem BOOLEAN zugewiesen und damit in einen der Werte false oder true konvertiert werden.

10.2.1 Vergleiche

Bisher haben wir einfache Vergleiche wie = (ist gleich) und < (ist kleiner als) verwendet. Dass bei beiden Vergleichen Zahlenwerte zurückgegeben werden, kann man übrigens leicht mithilfe von PRINT überprüfen:

PRINT 5 = 5     ' offensichtlich wahr; also wird -1 ausgegeben
PRINT 5 < 5     ' falsch; die Ausgabe ist 0
PRINT 6-2*(5=5) ' Rechenausdruck mit dem Ergebnis 8
SLEEP

Folgende Vergleichsoperatoren können verwendet werden:

Operator Bedeutung Beispiel Wert

>

größer als

5 > 5

0

3 > 8

0

<

kleiner als

5 < 5

0

3 < 8

-1

=

gleich

5 = 5

-1

3 = 8

0

>=

größer oder gleich

5 >= 5

-1

3 >= 8

0

<=

kleiner oder gleich

5 <= 5

-1

3 <= 8

-1

<>

ungleich

5 <> 5

0

3 <> 8

-1

Tabelle 10.1: Liste der Vergleichsoperatoren

Nicht nur Zahlen können miteinander verglichen werden, sondern auch Zeichenketten. Während die Prüfung auf Gleichheit bzw. auf Ungleichheit noch sehr einleuchtend ist, benötigen die Operatoren > und < bei Zeichenketten noch eine genauere Erläuterung. Die Zeichenketten werden hierbei von vorn nach hinten Zeichen für Zeichen verglichen (solange bis das Ergebnis feststeht). Ausschlaggebend ist dabei der ASCII-Code des Zeichens (vgl. dazu Kapitel 13.4 und Anhang C). Zu beachten ist dabei, dass Großbuchstaben einen kleineren ASCII-Code besitzen als Kleinbuchstaben.

Quelltext 10.5: Vergleich von Zeichenketten
PRINT "Ist 'b' kleiner als 'a'?         "; "b"    < "a"
PRINT "Ist 'Anna' kleiner als 'Anne'?   "; "Anna" < "Anne"
PRINT "Ist 'Kind' kleiner als 'Kinder'? "; "Kind" < "Kinder"
PRINT "Ist 'Z' kleiner als 'a'?         "; "Z"    < "a"
SLEEP
Ausgabe:
Ist 'b' kleiner als 'a'?          0
Ist 'Anna' kleiner als 'Anne'?   -1
Ist 'Kind' kleiner als 'Kinder'? -1
Ist 'Z' kleiner als 'a'?         -1

Gerade das letzte Beispiel scheint den Vergleichsoperator für eine alphabetische Sortierung (für die Programmierung eines Telefonbuchs o. ä.) ungeeignet zu machen. Doch dafür gibt es Abhilfe: Die zu vergleichenden Zeichenketten können zuvor komplett in Kleinbuchstaben (oder alternativ natürlich auch komplett in Großbuchstaben) umgewandelt werden. Darauf werden wir in Kapitel 15.2.6 zu sprechen kommen.

Tip Für Fortgeschrittene:
Die Vergleichsoperatoren überprüfen in FreeBASIC lediglich die Gleichheit der Werte. Die Gleichheit der Datentypen kann z. B. mit TYPEOF() (zur Compilier-Zeit) oder IS (bei UDTs mit RTTI-Funktionalität) geprüft werden.

10.2.2 Logische Operatoren

Bedingungen setzen sich manchmal aus mehreren Teilaspekten zusammen. Im folgenden Beispiel wird der Zutritt ab 18 Jahren gestattet. Jugendliche dürfen nur eintreten, wenn sie mindestens 14 Jahre alt sind und von ihren Eltern begleitet werden.

Wenn Alter >= 18 Oder (Alter > 13 Und Begleitung = "Eltern") Dann Zutritt

In FreeBASIC heißen die entsprechenden Operatoren OR (oder) bzw. AND (und). Wenn Sie in einer Bedingung beide Operatoren mischen, sollten Sie zusammengehörige Ausdrücke wie im obigen Beispiel mit Klammern umschließen, um sicher zu gehen, dass sie in der richtigen Reihenfolge ausgewertet werden. Wären die Klammern anders gesetzt, wäre auch eine andere Bedingung gefordert.

In FreeBASIC-Syntax sieht die Bedingung folgendermaßen aus:

IF Alter >= 18 OR (Alter > 13 AND Begleitung = "Eltern") THEN Zutritt
Note Hinweis:
Es gibt bei der Abarbeitung der Operatoren eine feste Reihenfolge, ähnlich wie die Rechenvorschrift „Punkt vor Strich“. Sie wird durch die sogenannten Vorrangregeln bestimmt, die in Anhang F aufgelistet sind, und kann durch den Einsatz von runden Klammern geändert werden. Wenn Sie unsicher sind, welche Operatoren Vorrang haben, sollten Sie auf Klammern zurückgreifen, auch um eine bessere Lesbarkeit des Quelltextes zu gewährleisten.

AND prüft also, ob beide Bedingungen (links und rechts vom AND) wahr sind, während OR überprüft, ob mindestens eine der beiden Bedingung zutrifft. Ganz richtig ist das allerdings noch nicht — verglichen werden nämlich die einzelnen Bits der beiden Ausdrücke. Dazu ist es wichtig zu wissen, dass der Computer im Binärsystem rechnet.

10.2.3 Das Binärsystem

Werte werden im Prozessor durch zwei Zustände (z. B. Strom an/Strom aus) festgehalten; im übertragenen Sinn kann man von zwei Werten 0 und 1 sprechen. Dies entspricht einer Speichereinheit bzw. 1 Bit. 8 Bit werden zusammengefasst zu 1 Byte, welches 28 = 256 Zustände annehmen kann, je nachdem, welche Bit gesetzt sind und welche nicht.
Ein möglicher Zustand wäre z. B. folgender:

Bit 7

Bit 6

Bit 5

Bit 4

Bit 3

Bit 2

Bit 1

Bit 0

0

1

0

0

1

0

1

1

Wird dieser Zustand als Ganzzahl interpretiert, dann zählt von rechts nach links jedes Bit doppelt so viel wie das davor. Bit 0 zählt also als 1, Bit 1 als 2, Bit 2 als 4 usw. Dies entspricht im wesentlichen der Darstellung einer Zahl im Dezimalsystem, nur dass die einzelnen Stellen nicht mehr Einer, Zehner, Hunderter, Tausender usw. repräsentieren, sondern Einer, Zweier, Vierer, Achter usw. Die oben dargestellte Zahl hätte also (von rechts gelesen) den Wert

1 · 1 + 1 · 2 + 0 · 4 + 1 · 8 + 0 · 16 + 0 · 32 + 1 · 64 + 0 · 128 = 75

Man spricht hierbei vom Binärsystem oder, mathematisch etwas genauer, vom Dualsystem (Programmierer verwenden beide Begriffe synonym). Dass die Nummerierung der Bits mit 0 beginnt, hat übrigens einen ganz praktischen Grund, denn dadurch lässt sich die Wertigkeit eines Bits ganz einfach als Zweierpotenz berechnen: 20 = 1, 21 = 2, 22 = 4, …

Mit diesem System können mit einem Byte Zahlen von 0 bis 255 dargestellt werden. Für größere Zahlen werden mehrere Byte „zusammengesetzt“. Auch negative Zahlen können so dargestellt werden — dazu wird vereinbart, dass das höchstwertige Bit kennzeichnet, ob die Zahl positiv oder negativ ist. Für den Wert -1 sind alle Bit gesetzt, für -2 alle außer Bit 0 usw. In Quelltext 10.6 wird die Funktion BIN() verwendet, um die Binärdarstellung von Dezimalzahlen zu erhalten. Sie können etwas experimentieren, wie negative Werte binär dargestellt werden.

Tip Hintergrundinformation:
Die von FreeBASIC verwendete Darstellung für Ganzzahlen heißt Zweierkomplementdarstellung. Es ist die heute am häufigsten verwendete Art, positive und negative Zahlen in binärer Form darzustellen.

Auch andere Informationen wie Farbwerte auf dem Monitor oder Zeichen innerhalb einer Zeichenkette werden letztendlich über das Binärsystem in Bytes gespeichert.

10.2.4 AND und OR als Bit-Operatoren

Auch wenn wir zuvor so getan haben, also ob es sich bei AND und OR um logische Operatoren handelt, ist das nicht ganz richtig. Sofern ausschließlich BOOLEAN bzw. nur die Werte 0 und -1 verwendet werden, ist alles in Ordnung, allerdings verknüpfen die beiden Operatoren die Werte bitweise. Das bedeutet: Die Werte werden Bit für Bit miteinander verglichen und im Rückgabewert entsprechend gesetzt oder nicht. AND setzt das Bit im Rückgabewert genau dann, wenn das Bit auch in beiden Operanden gesetzt ist. OR setzt das Bit genau dann, wenn es in mindestens einem der Operanden gesetzt ist.

Das soll in folgendem Codebeispiel verdeutlicht werden. Dazu bedienen wir uns der Funktion BIN(), die den Binärwert einer Zahl als String zurückgibt. BIN() kann neben der umzuwandelnden Zahl optional noch ein zweiter Parameter mitgegeben werden, der angibt, wie viele Stellen der Binärzahl zurückgegeben werden sollen — ggf. wird die Zahl abgeschnitten oder mit Nullen aufgefüllt. Durch eine einheitliche Länge lässt sich die Ausgabe besser formatieren und damit leichter lesen. Im folgenden Quelltext werden alle Binärwerte mit vier Stellen ausgegeben.

Quelltext 10.6: Bit-Operatoren AND und OR
DIM AS INTEGER z1 = 6, z2 = 10

PRINT "Verknuepfung AND"
PRINT z1, BIN(z1, 4)
PRINT z2, BIN(z2, 4)
PRINT "---", "----"
PRINT z1 AND z2, BIN(z1 AND z2, 4)

PRINT "Verknuepfung OR"
PRINT z1, BIN(z1, 4)
PRINT z2, BIN(z2, 4)
PRINT "---", "----"
PRINT z1 OR z2, BIN(z1 OR z2, 4)
SLEEP
Ausgabe:
Verknuepfung mit AND
 6            0110
 10           1010
---           ----
 2            0010

Verknuepfung mit OR
 6            0110
 10           1010
---           ----
 14           1110

Wenn Sie AND zur Bestimmung eines Wahrheitswertes verwenden wollen, stoßen Sie hier auf ein Problem: die Zahlen 2 und 4 z. B. werden beide als wahr interpretiert, 2 AND 4 jedoch ist 0 und damit falsch. Sie sollten in diesem Fall unbedingt auf die Vergleichoperatoren =, < usw. zurückgreifen oder sicherstellen, dass nur die Werte 0 und -1 verglichen werden.

10.2.5 ANDALSO und ORELSE

Alternativ dazu existieren die (nun tatsächlich logischen) Operatoren ANDALSO und ORELSE. Diese arbeiten jedoch etwas anders: Zunächst werden beide Seiten immer als Wahrheitswert definiert, womit 2 ANDALSO 4 = -1 (wahr) ist. Zum anderen wird die Auswertung nur so lange durchgeführt, bis das Ergebnis feststeht. In der Praxis bedeutet das für ANDALSO: Wenn die linke Seite bereits falsch ist, kann der Gesamtausdruck nicht mehr wahr sein, und die rechte Seite wird daher gar nicht mehr ausgewertet. Wenn umgekehrt bei ORELSE die linke Seite wahr ist, dann ist auch der Gesamtausdruck wahr. Auch hier wird die rechte Seite nicht mehr ausgewertet.

Ein einfaches Beispiel:

Quelltext 10.7: ANDALSO und ORELSE
DIM AS INTEGER z1 = 3, z2 = 5
IF z1 < 2 ANDALSO z2 > 4 THEN PRINT "Hoppla ..."
SLEEP

Wenn beide Operanden vom Typ BOOLEAN sind, liefern ANDALSO und ORELSE als Rückgabewert ein BOOLEAN, ansonsten einen der Werte -1 und 0. Wenn Sie die Operatoren allerdings nur zur Bedingungsabfrage einsetzen und nicht innerhalb von Rechenausdrücken, macht das keinen Unterschied.

Sinnvoll sind die beiden Operatoren z. B. dann, wenn auf der rechten Seite aufwendige Berechnungen durchgeführt werden müssen, die dann, wenn die linke Seite nicht erfüllt ist, komplett wegfallen können. Andererseits kann es auch passieren, dass die rechte Seite gar nicht ausgewertet werden darf, da die erforderlichen Bedingungen nicht erfüllt sind und die Auswertung zu einem Fehler führen würde. ANDALSO findet man daher auch oft im Zusammenhang mit Pointerzugriffen, vor denen geprüft wird, ob der Pointer überhaupt korrekt gesetzt ist.

' Vermeidung der aufwendigen Berechnung, wenn Vorbedingung nicht erfuellt
IF bedingung ANDALSO aufwendigeBerechnung() > 0 THEN tueWas
 
' Vermeidung eines unerlaubten Null-Pointer-Zugriffs
IF meinPointer ANDALSO *meinPointer = 10 THEN tueWas

In beiden Fällen wird zunächst überprüft, ob der erste Ausdruck wahr ist, also ob bedingung bzw. meinPointer einen Wert ungleich Null besitzen. Ob meinPointer tatsächlich ein korrekter Pointer auf eine Zahl ist, kann damit zwar nicht sichergestellt werden, aber zumindest wird so ein Nullpointer abgefangen. Die Abfrage des gespeicherten Wertes erfolgt auf jeden Fall nur dann, wenn meinPointer kein Nullpointer ist.

Warning Achtung:
ANDALSO und ORELSE führen nicht automatisch zu einem schnelleren Code! Falsch angewendet kann es im Extremfall sogar zu einer Verlangsamung kommen.

10.2.6 Weitere Bit-Operatoren: XOR, EQV, IMP und NOT

Neben AND und OR wird auch der Operator XOR (eXclusive OR) häufig verwendet, der umgangssprachlich als entweder — oder aufgefasst werden kann. Es muss also genau eines der verglichenen Bits gesetzt sein. Sind beide Bits gesetzt, ergibt sich der Wert 0, genauso wenn keines der beiden Bits gesetzt ist.

Etwas exotischer sind die beiden Operatoren EQV (EQuivalent Value) und IMP (IMPlication). EQV prüft die Gleichheit beider Bits, also ob beide gesetzt sind oder beide nicht. Bei IMP ist der Hintergrund etwas komplizierter: Es wird geprüft, ob aus der ersten Aussage die zweite folgt. Praktisch bedeutet es, dass das Bit immer gesetzt wird, außer wenn es im linken Ausdruck gesetzt war, im rechten jedoch nicht.

NOT schließlich dreht die Bitwerte einfach um: wo das Bit gesetzt war, ist es im Ergebnis nicht gesetzt und umgekehrt. Im Gegensatz zu den bisherigen Operatoren wird NOT lediglich auf einen Operanden angewandt. NOT 0 z. B. ist -1 und NOT -1 ist 0 (auf den Gesamtwert betrachtet).

Noch einmal zusammengefasst: Die Bit-Operatoren AND, OR, XOR, EQV, IMP und NOT vergleichen die beiden übergebenen Ausdrücke (bei NOT nur ein übergebener Ausdruck) Bit für Bit und setzen das entsprechende Bit im Ergebnis je nachdem auf den Wert 0 oder 1. Tabelle 10.2 stellt das Verhalten der Operatoren in einer Übersicht zusammen.

Schlüsselwort Ausdruck1 Ausdruck2 Ergebnis

AND

0

0

0

0

1

0

1

0

0

1

1

1

OR

0

0

0

0

1

1

1

0

1

1

1

1

XOR

0

0

0

0

1

1

1

0

1

1

1

0

EQV

0

0

1

0

1

0

1

0

0

1

1

1

IMP

0

0

1

0

1

1

1

0

0

1

1

1

NOT

0

-

1

1

-

0

Tabelle 10.2: Bit-Operatoren

Sofern diese Operatoren ausschließlich für BOOLEAN bzw. ausschließlich für -1 und 0 verwendet werden (aber nur dann), verhalten sie sich wie logische Operatoren.

10.3 Mehrfachauswahl: SELECT CASE

Wenn eine Variable auf mehrere verschiedene Werte geprüft werden soll, ist das Konstrukt IF … THEN … ELSEIF … ELSE … END IF oft recht umständlich. Ein denkbarer Fall wäre eine Tastaturabfrage, auf die in Abhängigkeit von der gedrückten Taste reagiert werden soll. Im Folgenden soll auf die Tasten W-A-S-D bzw. 8-4-5-6 zur Steuerung einer Spielfigur reagiert werden. Da es egal sein soll, ob dabei die Shift-Taste gedrückt wurde oder nicht, wird der Tastenwert zuerst mit LCASE() in Kleinbuchstaben umgewandelt.

Quelltext 10.8: Mehrfache Bedingung (2) mit IF
DIM AS STRING taste
PRINT "Druecke eine Steuerungstaste."
taste = INPUT(1)

IF LCASE(taste) = "w" or taste = "8" THEN
  PRINT "Taste nach oben gedrueckt"
ELSEIF LCASE(taste) = "s" or taste = "5" THEN
  PRINT "Taste nach unten gedrueckt"
ELSEIF LCASE(taste) = "a" or taste = "4" THEN
  PRINT "Taste nach links gedrueckt"
ELSEIF LCASE(taste) = "d" or taste = "6" THEN
  PRINT "Taste nach rechts gedrueckt"
ELSE
  PRINT "keine Steuerungstaste gedrueckt"
END IF
SLEEP

Wie Sie sehen, steckt in diesem Quellcode eine Menge Schreibarbeit — und damit verbunden ein erhöhtes Risiko an Tippfehlern.

10.3.1 Grundaufbau

Eine alternative Bedingungsstruktur bietet SELECT CASE. Hier wird nur einmal die Bedingung angegeben; danach folgen nur noch die gewünschten Vergleichswerte. SELECT CASE hat folgenden Grundaufbau:

SELECT CASE Ausdruck
  CASE Ausdrucksliste1
    Anweisung1
  CASE Ausdrucksliste2
    Anweisung2
  ' ...
  CASE ELSE
    Anweisung
END SELECT

Mithilfe von SELECT CASE kann man Quelltext 10.8 folgendermaßen umformulieren:

Quelltext 10.9: Mehrfache Bedingung (2) mit SELECT CASE
DIM AS STRING taste
PRINT "Druecke eine Steuerungstaste."
taste = INPUT(1)

SELECT CASE LCASE(taste)
  CASE "w", "8"
    PRINT "Taste nach oben gedrueckt"
  CASE "s", "5"
    PRINT "Taste nach unten gedrueckt"
  CASE "a", "4"
    PRINT "Taste nach links gedrueckt"
  CASE "d", "6"
    PRINT "Taste nach rechts gedrueckt"
  CASE ELSE
    PRINT "keine Steuerungstaste gedrueckt"
END SELECT
SLEEP

Der Ablauf ist ähnlich wie bei IF. Zuerst wird der Ausdruck ausgewertet und dann in die erste CASE-Zeile gesprungen, auf welche der Ausdruck zutrifft. Alle Zeilen zwischen dieser und der nächsten CASE-Zeile werden ausgeführt, danach fährt das Programm am Ende des SELECT-Blockes fort. Trifft keine der Ausdruckslisten zu, werden die Zeilen hinter dem CASE ELSE ausgeführt, sofern vorhanden.

In jeder Ausdrucksliste können ein oder auch mehrere Vergleichswerte stehen, die dann durch Komma voneinander getrennt werden. Diese Liste kann beliebig lang werden. Es gibt auch noch weitere Möglichkeiten zur Angabe der Vergleichswerte, auf die in Kapitel 10.3.2 eingegangen wird.

Da der auszuwertende Ausdruck — hier der in Kleinbuchstaben umgewandelte Wert der Tastatureingabe — nur einmal angegeben werden muss, wird das Programm deutlich übersichtlicher. Allerdings gibt es auch einen programmtechnischen Vorteil: Die Umwandlung in Kleinbuchstaben, die ja durchaus etwas Rechenzeit kostet, muss nur einmal erledigt werden statt viermal wie in Quelltext 10.8. Und sollte sich der auszuwertende Ausdruck einmal ändern, muss dies auch nur an einer Stelle geschehen anstatt in jeder ELSEIF-Zeile. Dafür sind Sie aber auf einen einzigen Ausdruck für den gesamten Block eingeschränkt.

Zu beachten ist, dass immer nur einer der CASE-Abschnitte ausgeführt wird, auch wenn der Ausdruck bei mehreren Abschnitten angegeben wird. In einem solchen Fall wird immer der erste passende Abschnitt gewählt.

Tip Vergleich mit anderen Sprachen:
Im Gegensatz zur entsprechenden C-Funktion switch wird der SELECT-Block nach Abarbeitung des passenden CASE-Abschnitts verlassen; die Verwendung einer break-Anweisung ist nicht nötig. Allerdings lassen sich auch in FreeBASIC die SELECT-Blöcke durch Kontrollanweisungen vorzeitig verlassen (siehe dazu Kapitel 11.5.2). Es ist dagegen nicht möglich, in den folgenden CASE-Abschnitten fortzufahren!

10.3.2 Erweiterte Möglichkeiten

Neben der einfachen Auflistung aller passenden Werte gibt es für die Ausdrucksliste noch weitere Möglichkeiten:

  • Bereichsangaben:
    Mit wert1 TO wert2 kann überprüft werden, ob der Ausdruck im Bereich von wert1 bis wert2 liegt. Er muss also größer oder gleich wert1 sowie kleiner oder gleich wert2 sein. Bereichsangaben sind auch bei Zeichenketten möglich, jedoch sind hier die Besonderheiten von String-Vergleichen zu beachten (siehe Kapitel 10.2.1).

  • Vergleichsoperatoren:
    Die üblichen Vergleichsoperatoren <, >, <= und >= können eingesetzt werden, indem vor den Vergleichsoperator das Schlüsselwort IS gesetzt wird.

  • Alle drei Varianten (Einzelwerte, Bereichsangaben, Vergleichsoperatoren) können beliebig kombiniert werden, indem man sie durch Kommata getrennt auflistet.

Dazu ein Beispiel aus der FreeBASIC-Referenz (dient nur zur Veranschaulichung und ist nicht ohne Änderung lauffähig):

Quelltext 10.10: Ausdruckslisten bei SELECT CASE
 SELECT CASE a
  ' ist a = 5?
  CASE 5

  ' ist a ein Wert von 5 bis 10?
  ' Die kleinere Zahl muss zuerst angegeben werden
  CASE 5 TO 10

  ' ist a groesser als 5?
  CASE IS > 5

  ' ist a gleich 1 oder ein Wert von 3 bis 10?
  CASE 1, 3 TO 10

  ' ist a gleich 1, 3, 5, 7 oder b + 8?
  CASE 1, 3, 5, 7, b + 8
END SELECT

10.3.3 SELECT CASE AS CONST

Unter bestimmten Bedingungen kann der Compiler angewiesen werden, den SELECT-Block effizienter umzusetzen und dadurch die Ausführungsgeschwindigkeit zu erhöhen (was natürlich nur dann ins Auge fällt, wenn der Block während des Programmablaufs sehr oft ausgeführt werden muss).

  • Der Ausdruck muss eine Ganzzahl sein.

  • Die Ausdruckslisten dürfen nur Konstanten oder einfache Ausdrücke enthalten, also Ausdrücke, die keine Variablen enthalten. Die FreeBASIC-internen mathematischen Funktionen dürfen ebenfalls verwendet werden.

  • Der Operator IS steht nicht zur Verfügung.

  • Die Differenz zwischen dem kleinsten und dem größten Vergleichswert kann maximal 4096 betragen. Möglich sind also z. B. Werte im Bereich von 0 bis 4096 oder von 4 bis 4100.

Die Syntax lautet hier

SELECT CASE AS CONST Ausdruck

Der Hintergrund für die Einschränkungen ist, dass bei diesem Aufruf die durch die Ausdruckslisten festgelegten Sprungmarken bereits beim Compilieren festgelegt werden. Deswegen dürfen die Ausdruckslisten nur Werte enthalten, die bereits beim Compilieren feststehen (also keine Variablen).

10.4 Bedingungsfunktion: IIF()

Als dritte Möglichkeit steht die Funktion IIF() zur Verfügung. Diese funktioniert ein gutes Stück anders als die beiden vorher genannten Bedingungsblöcken: Je nachdem, ob die übergebene Bedingung wahr oder falsch ist, wird einer von zwei Werten zurückgegeben.

Rueckgabe = IIF(Bedingung, Rueckgabewert_Wenn_Wahr, Rueckgabewert_Wenn_Falsch)

Es ist eine Kurzform von

IF Bedingung THEN
  Rueckgabe = Rueckgabewert_Wenn_Wahr
ELSE
  Rueckgabe = Rueckgabewert_Wenn_Falsch
END IF

Die beiden möglichen Rückgabewerte müssen denselben Datentyp besitzen und zum Datentyp der Variablen Rueckgabe passen. Die Stärke von IIF() besteht darin, dass es auch in Ausdrücken eingebaut werden kann, z. B. in eine Berechnung oder einer Textausgabe. Dazu ein paar Beispiele:

Quelltext 10.11: Bedingungen mit IIF
' Geld um 100 reduzieren, aber nicht unter 0
geld = IIF(geld > 100, geld-100, 0)
' Gehaltstruktur abhaengig von den Dienstjahren
gehalt = 1000 + IIF(dienstjahre > 10, dienstjahre*50, dienstjahre*20)
' Rueckmeldung: Einlass erst ab 18
PRINT "Du darfst hier " & IIF(alter>=18, "selbstverstaendlich", "nicht") & " rein!"

10.5 Welche Möglichkeit ist die beste?

Wenn eine feststehende Variable oder ein Rechenausdruck auf mehrere verschiedene Ergebnisse hin geprüft werden soll, ist meistens SELECT CASE die beste Wahl. Der Quelltext ist damit am einfachsten zu lesen und zu warten. Allerdings kann SELECT CASE nur einen einzigen Ausdruck abprüfen; auf den Inhalt zweier oder mehr verschiedener Variablen kann nicht gleichzeitig geprüft werden. In diesem Fall bleibt nur der Einsatz von IF (verschachtelte SELECT-Blöcke sind natürlich möglich, und ob sie sinnvoll sind, muss im Einzelfall geprüft werden). Die Stärke von IF besteht in der hohen Flexibilität.

Als Beispiel für einen Einsatz von SELECT CASE: Sie wollen ein tastaturgesteuertes Menü programmieren. Am Bildschirm werden fünf verschiedene Menüeinträge angezeigt, jeder mit einer Angabe versehen, mit welcher Taste er aufgerufen werden kann. In diesem Fall ist SELECT CASE ideal geeignet. Als weiteres Beispiel kann eine „Übersetzung“ von Schulnoten in ihre Textbedeutung dienen (was sich jedoch leichter über ein Array regeln lässt).

Dagegen lässt sich allein schon bei einer Abfrage von Benutzernamen und Passwort ein SELECT CASE nicht sinnvoll einsetzen. Sowohl IF als auch SELECT CASE haben also, je nach konkreten Fall, ihre Stärken.

IIF() wiederum lässt sich (ausschließlich) dann gewinnbringend einsetzen, wenn ein Rückgabewert in Abhängigkeit von der Bedingung gefordert ist. Ein IIF() lässt sich immer auch durch einen IF-Block ersetzen, benötigt dann aber oft mehr Schreib- und Speicheraufwand, da der Rückgabewert in der Regel zusätzlich in einer Variablen zwischengespeichert werden muss.

10.6 Fragen zum Kapitel

  1. Welchen Sinn haben Einrückungen im Quelltext?

  2. Welche Werte werden in FreeBASIC als wahr und welche als falsch interpretiert?

  3. Nach welchem System werden Zeichenketten verglichen?

  4. Was ist der Unterschied zwischen einem logischen Operator und einem Bit-Operator?

  5. Wie muss eine Bedingung formuliert werden, wenn der Wert der Variablen a zwischen 3 und 8 oder aber zwischen 12 und 20 liegen soll?

  6. Wieder eine kleine Programmieraufgabe: Das Programm soll den Benutzer nach Namen, Passwort und Alter fragen. Wenn der Name und das Passwort richtig sind, wird je nach Alter (verschiedene Altersbereiche wie „höchstens 14“, „zwischen 14 und 18“ und „mindestens 18“) eine andere Meldung ausgegeben.
    Das Programm soll dann dahingehend erweitert werden, dass es nicht nur einen Benutzernamen mit zugehörigem Passwort erkennt, sondern zwei verschiedene Benutzernamen, jedes mit einem eigenen Passwort.


Kapitel 9: Pointer (Zeiger)

Inhaltsverzeichnis

Kapitel 11: Schleifen und Kontrollanweisungen