Advanced Services | Operatoren 1 |
Operatoren wirken auf Literale und/oder Variablen (oder auf das Ergebnis eines Methodenaufrufes). Die Literale oder Variablen nennt man in diesem Zusammenhang auch Operanden. Ergebnis dieser Wirkung ist ein Wert. Ein einfaches und bekanntes Beispiel ist der Mutliplikationsoperator *. Er braucht zwei Operanden. So hat 17*a den Wert 34, falls die Variable a mit dem Wert 2 belegt ist. Der Mutliplikationsoperator ist ein Beispiel für einen binären Operator, er braucht zwei Operanden, um wirken zu können. Damit haben wir schon eine mögliche Einteilung der Operatoren.
Einteilung der Operatoren nach der Anzahl der Operanden | Beispiel | |
---|---|---|
unäre Operatoren | Operator wirkt auf einen Operanden | -a |
binäre Operatoren | Operator wirkt auf zwei Operanden | a*b |
ternäre Operatoren | Operator wirkt auf drei Operanden | a<b ? a : b |
Mehr Möglichkeiten gibt es nicht. Ein Operator, der auf drei Operanden wirkt, ist eine Besonderheit,
es gibt in C/C++, Java und C# nur einen Operator diesr Art, den Operator ? : für den bedingten Ausdruck.
Man kann die Operatoren auch nach der Stellung zu den Operanden ordnen. Der Mutliplikationsoperator
steht zwischen den Operanden (a*b), beim Subtraktionsoperator gibt es zwei Möglichkeiten, im Falle
a - b steht er zwischen den Operanden und ist binär, im Falle -a steht er vor dem Operanden und
ist unär. Für manche Operatoren gibt es also mehr als eine Möglichkeit, auf einen Operanden
zu wirken. Die erste Stellung nennt man infix, die zweite prefix (oder präfix). Steht der
Operator nach dem Operanden, so nennt man ihn einen postfix-Operator. Der berühmteste PostfixOperator
ist ++, dem die Sprache C++ ihren Namen verdankt. Von diesem Operator gibt es aber auch eine Prefixversion.
Einteilung der Operatoren nach der Stellung zu den Operanden | Beispiel | |
---|---|---|
prefix-Operatoren | Operator steht vor dem Operanden | -a |
infix-Operatoren | Operator steht zwischen den Operanden | a + b |
postfix-Operatoren | Operator steht nach dem Operanden | a++ |
Assoziativität
Verwendet man denselben Operator mehrmals, so muß man sich entscheiden, bei welchem Operator man
mit der Auswertung des Ausdrucks anfängt. Dies ist wichtiger, als man zunächst glauben mag.
Hierzu zwei Beispiele:
Falls links- und rechtsassoziative Auswertung zum gleichen Ergebnis führen, nennt man in der
Mathematik einen Operator schlicht assoziativ und man kann dann Auswerten, wie man will. Ein Compiler
aber hat pro Operator nur eine Möglichkeit, für ihn ist ein Operator entweder linksassoziativ
oder rechtsassoziativ. Und damit stellt sich die Frage, wie der Compiler einen Ausdruck der Form
a/b/c auswertet. Der Tabelle der Operatoren kann man entnehmen, daß alle arithmetischen Operatoren
linksassoziativ sind. Die Klammerungen unter 1. und 3. entsprechen also der "Denkweise" unseres Compilers.
Präzedenz (Priorität, Vorrang)
Hat man zwei verschiedene Operatoren, so stellt sich nicht mehr die Frage, von links oder von rechts,
sondern welcher von den beiden hat Vorrang. Die aus der Mathematik bekannte Vorrangsregel "Punkt vor Strich"
gilt auch für alle Programmiersprachen. Da es aber für Programmiersprachen wesentlich mehr Operatoren
gibt als die für die vier Grundrechenarten gebrächlichen Operatoren, gibt es auch sehr viel
mehr Vorrangsregeln. C# kennt insgesamt nicht weniger als 13 Präzedenzstufen. Auch erfahrene
Programmierer haben diese Liste nicht unbedingt im Kopf. In Zweifelsfällen behilft man sich mit
einem Blick in eben diese Liste oder man setzt runde Klammern.
Runde Klammern haben die höchste Präzedenz.
Die 5 arithmetische Operatoren sind in gewissem Sinn die einfachsten Operatoren in C#, weil man sie zum großen Teil noch aus der Schule kennt. Sie sind im folgenden in einem Auszug aus der Operatorentabelle zusammengefaßt. Man beachte, daß + und - sowohl unär als auch binär gebraucht werden können.
Präzedenz | Operator | Bezeichnung | Assoziativität | Operandentyp |
---|---|---|---|---|
2 | Unäre Operatoren | |||
+ | unäres Plus | rechts | numerisch | |
- | unäres minus | rechts | numerisch | |
3 | Multiplikation/Division | |||
* | Multiplikation | links | numerisch | |
/ | Division | links | numerisch | |
% | Modulo | links | numerisch | |
4 | Addition/Subtraktion | |||
+ | Addition | links | numerisch, String | |
- | Subtraktion | links | numerisch |
Hier einige Beispiele mit arithmetischen Operatoren
C#-Ausdruck |
Auswertung durch den Compiler |
|
1) | a + 2*(b - c)/b | a + ((2*(b - c))/b) |
2) | -b/2*a | (-b/2)*a |
3) | -b/(2*a) | -b/(2*a) |
4) | -b/2/a | (-b/2)/a |
5) | - -b | -(-b) |
6) | + +b | +(+b) |
Beachten sie, daß die normale Verarbeitungsrichtung des Compilers von links nach rechts ist, anders
gesagt, der Compiler arbeitet linksassoziativ. Bei den Beispielen 5 und 6 dagegen arbeitet der Compiler
von rechts nach links (siehe Tabelle oben) und deswegen sind solche Konstrukte legal.
Ergebnistyp
Bei der Addition von zwei int-Variablen erwartet man sozusagen autonmatisch, daß das Ergebnis
vom Typ int ist. Wie sieht es aber aus, wenn man zwei int-Variablen dividiert oder
wenn man eine int-Variable mit einer double-Variable multipliziert ? betrachten wir den
folgenden Porgrammaussschnitt.
int a = 13 , b = 5 , c ; double x, y ; x = a / b ; System.out.println("x = "+x); // x hat den Wert 2.0 c = a * x ; // Compiletime error : possible loss of precision
Nach der Division hat x "überraschenderweise" den Wert 2.0 und obwohl x und a einen ganzzahligen Wert haben, kann ich das Ergebnis nicht der Variablen c zuweisen. Es gibt zwei einfache Regeln, mit denen sich das Verhalten des Compilers leicht erklären läßt.
Also ergibt die Division von zwei int-Variablen wieder einen int-Wert. Nach dieser Regel
ist dann etwa 26/7 gleich 3. Diesen Vorgang nennt man Ganzzahldivision, die Mathematiker schreiben dafür
26 div 7 = 3 .
Der Restoperator % (modulo)
Man kann diesen Operator als Ergänzung zur Ganzzahldivision auffassen. Während die Ganzzahldivision den
ganzen Anteil liefert, liefert der Restoperator den zugehörigen Rest. In mathematischer Schreibweise:
26 div 7 = 3
26 mod 7 = 5 (Rest bei der GanzzahlDivision durch 7)
Für die erste Zeile schreiben wir 26 / 7 und für die zweite Zeile
schreiben wir 26 % 7 . Der erste Ausdruck hat den Wert 3 und der zweite
Ausdruck den Wert 5.
Übung
Gibt es in dem folgenden Programm eine Fehlermeldung des Compilers (compiletime error) oder
gibt es einen Fehler zur Laufzeit (runtime error) oder gibt es gar keinen Fehler ?
public class Modulo2 { public static void Main(String[] args) { System.out.println("26%7 = " + 26%7 ); System.out.println("7%26 = " + 7%26 ); System.out.println("-26%7 = " + (-26%7) ); System.out.println("26%-7 = " + 26%-7 ); System.out.println("26.5%7.4 = " + 26.5%7.4 ); System.out.println("0%26 = " + 0%26 ); System.out.println("26%0 = " + 26%0 ); } }
Präzedenz | Operator | Bezeichnung | Assoziativität | Operandentyp |
---|---|---|---|---|
6 | Relationale Operatoren 1 | |||
< | kleiner | links | boolesch | |
<= | kleiner gleich | links | boolesch | |
> | größer | links | boolesch | |
>= | größer gleich | links | boolesch | |
7 | Relationale Operatoren 2 | |||
== | Vergleich auf Gleichheit | links | alle | |
!= | Vergleich auf Ungleichheit | links | alle |
Beispiele mit relationalen Operatoren
Folgendes ist zu beachten: Der Operator
<=
im ersten Beispiel ist ein
zusammengesetzer Operator,bestehend aus
<
und = .
Hier müssen die beiden Zeichen
ohne Abstand unmittelbar nebeneinander stehen. Ebenso besteht der Vergleichsoperator aus zwei Gleichheitszeichen
ohne Abstand. Die Regel ist durchgängig. Bei allen Operatoren, die durch zwei Zeichen dargestellt werden, darf
zwischen diesen Zeichen kein blank stehen.
Achtung : Konstrukte mit relationalen Operatoren ergeben immer einen booleschen Wert, also true oder false.
Präzedenz | Operator | Bezeichnung | Assoziativität | Operandentyp |
---|---|---|---|---|
8 | bitweise AND / vollständiges boolesches AND | |||
& | bitweises/boolesches AND | links | integral, boolesch | |
9 | bitweise XOR / boolesches XOR | |||
^ | bitweises/boolesches XOR | links | integral, boolesch | |
10 | bitweise OR / vollständiges boolesches OR | |||
| | bitweises/boolesches OR | links | integral, boolesch | |
11 | boolesches AND (shortcut-evaluation) | |||
&& | boolesches AND | links | boolesch | |
12 | boolesches OR (shortcut-evaluation) | |||
| | | boolesches OR | links | boolesch |
Zu beiden Seiten eines booleschen Operators müssen Ausdrücke stehen, die einen booleschen Wert darstellen, deshalb werden boolesche Operatoren meist zusammen mit relationalen Operatoren verwendet. Das Ergebnis der Verknüpfung ist wieder ein boolescher Wert.
Mit den ersten beiden Beispielen werden Bereiche auf dem Zahlenstrahl angegeben, die von einigen Zahlen
erfüllt werden und von einigen nicht. Sie können also jeweils Zahlen finden, die die Bedingung
erfüllen oder auch nicht. Je nachdem ergibt sich als Wert der Verknüpfung dann true oder false.
Die im dritten Beispiel angegeben Bedingung ist nicht erfüllbar (warum?). Sie ergibt immer false.
Die vierte Bedingung dagegen ist wieder erfüllbar (suchen Sie entsprechende Zahlen).
Bei den anderen drei Operatoren hängt die Bedeutung vom Typ des Operanden ab, sie sind also überladen.
Bevorzugen Sie für die Verknüpfung von Wahrheitswerten die Operatoren mit shortcut-evaluation.
Brauchen Sie ein boolesches XOR, dann müssen Sie natürlich den Operator der Stufe 9 verwenden.