4.2    Operatoren

Operatoren dienen dazu, Werte und Variablen miteinander zu verknüpfen. In diesem Fall nennt man die Variablen selbst die Operanden und die Berechnung, die man mit den Operanden ausführen möchte (z. B. die Operation +), nennt man Operator. Neben mathematischen Berechnungen rufen Sie mit Operatoren beispielsweise auch Funktionen auf oder führen logische Verknüpfungen der Bits zweier Zahlen durch. Die Variable, womit oder worauf die Operatoren angewendet werden, ist also der Operand. Eine solche Verknüpfung aus Operand und Operator erzeugt einen Rückgabewert, der wiederum als Operand verwendet werden kann. Ein einfaches Beispiel hierzu wäre:

z = x + y;

Hier werden die beiden Operanden x und y mit dem Additionsoperator + miteinander verknüpft. Das Ergebnis dieser Operation wird wiederum als Operand genutzt, um es mithilfe des Operators = dem Operanden z zuzuweisen. Eine solche Zusammenfassung von mehreren Operationen wird auch als Ausdruck bezeichnet. Ein Ausdruck kann logisch wahr oder falsch sein und lässt sich auch mit bestimmten Befehlen auf seinen Wahrheitsgehalt überprüfen. Sie erfahren noch mehr über logische Operatoren, wenn wir das Schlüsselwort if behandeln.

In C lassen sich die Operatoren in folgende drei Gruppen einteilen:

Für jeden Operator gibt es eine Rangordnung bzw. Priorität (engl. operator precedence). Sinngemäß haben beispielsweise die arithmetischen Operatoren +, -, * oder / eine höhere Priorität als der Zuweisungsoperator =, weshalb auch immer erst die arithmetische Berechnung durchgeführt und dann der Variablen das Ergebnis zugewiesen wird. Wie die Prioritätswerte intern verwaltet werden, hängt sehr stark vom Compiler und auch von der Programmiersprache ab. Die Prioritätswerte sorgen aber in jedem Fall dafür, dass z. B. die Regel »Punkt vor Strich« beachtet wird. Dass diese Regel wirklich vom Compiler beachtet wird, können Sie anhand des nächsten Beispiels prüfen. Auch gilt hier die bekannte Punkt-vor-Strich-Regelung der Mathematik, bei der der Multiplikationsoperator eine höhere Priorität als der Additionsoperator hat:

5 + 10 * 2

In diesem Fall wird zunächst 10 mit 2 multipliziert und dann zu 5 addiert, das Ergebnis lautet also 25. Würde man strikt nach »Schema F« vorgehen, bekäme man als Ergebnis 15*2=30 heraus, was falsch wäre. Sie können in C aber auch – wie in der Mathematik üblich – bei der Addition die üblichen Klammern verwenden, wenn Sie zuerst 5 und 10 addieren und dann mit 2 multiplizieren wollen:

(5 + 10) * 2

Diese Beschreibung hat Ihnen gezeigt, dass viele Abarbeitungsregeln von Operatoren in C genauso funktionieren wie im täglichen Gebrauch in mathematischen Berechnungen. Trotzdem gibt es in der Programmierung natürlich immer wieder einige Besonderheiten.

Korrekter Gebrauch von Klammern in C

In der Mathematik verwenden Sie oft eckige statt runde Klammern, um z. B. eine Formel übersichtlicher darzustellen. Dies geht nicht in C, denn die eckigen Klammern haben eine andere Bedeutung, als die runden Klammern. So greifen Sie z. B. mit a=b[10] auf die 11. Variable in einem Datenfeld zu, aber mit a=b(10) rufen Sie die Funktion b mit dem Parameter 10 auf und weisen das Ergebnis anschließend der Variablen a zu.

Wenn Sie Operatoren mit derselben Rangfolge verwenden, gibt es auch noch eine vorgeschriebene Abarbeitungsrichtung (auch Assoziativität genannt), mit der festgelegt wird, ob die Operatoren von links nach rechts (linksassoziativ) oder von rechts nach links (rechtsassoziativ) abgearbeitet werden. Führen Sie beispielsweise zwei Multiplikationen wie folgt durch:

a * b * c

Hier wird in diesem Fall von links nach rechts gerechnet. Zunächst wird also a mit b multipliziert und dann das Ergebnis mit c. Verwenden Sie hingegen den Zuweisungsoperator, z. B.

a = b = c

dann wird der Ausdruck von rechts nach links gearbeitet. Zuerst wird der Wert von c an b und dann der Wert a zugewiesen.

Überblick zur Rangordnung und Abarbeitungsrichtung

Einen Überblick zur Rangordnung und Abarbeitungsrichtung der einzelnen Operatoren finden Sie im Anhang A, »Übersichtstabellen wichtiger Sprachelemente«, dieses Buches.

Bei der Abarbeitung einzelner Operatoren eines Ausdrucks entstehen Zwischenergebnisse, die nach der Berechnung entfernt werden. Der Wert eines solchen Ausdrucks wird als Rvalue (manchmal übersetzt als R-Wert) bezeichnet. Ein Beispiel:

100 + 100;

Auch wenn ein solcher Ausdruck keinen wirklichen Effekt hat, da der Wert der Berechnung sofort wieder verworfen wird, ist es möglich, ihn in C so zu verwenden wie im letzten Beispiel.

Werte hingegen, die in einem bestimmten Speicherbereich gespeichert sind, werden als Lvalues (manchmal übersetzt als L-Werte) bezeichnet. Solche Werte werden von Operatoren benötigt, die einen Wert speichern oder die Speicheradresse des Wertes verwenden. Ein Beispiel wäre:

int a;
a = 100; // lvalue = rvalue

Ein Lvalue ist daher immer ein Wert, der im Speicher lokalisierbar ist. Auch der Standard (seit C11) empfiehlt mittlerweile, sich das L in Lvalue als »lokalisierbar« (locator value) zu merken und sich den Begriff Rvalue nur noch als den Wert eines Ausdrucks (value of an expression) vorzustellen. So können Sie beispielsweise mit Operatoren einen Wert nur an lokalisierbare Werte zuweisen. Daher ist Folgendes auch nicht möglich:

100 = a; // rvalue = lvalue  -> Fehler!!!

Das Literal 100 ist hier nicht lokalisierbar, weshalb die Zuweisung nicht erlaubt ist. Somit muss beispielsweise links neben dem Zuweisungsoperator immer ein Lvalue stehen, der auch änderbar ist – es ist ja auch möglich, Variablen mit const als nicht mehr änderbar zu qualifizieren.

Jetzt sind Sie zumindest ein wenig mit den Begriffen Lvalues und Rvalues vertraut, und wenn Sie einmal einen Fehler wie Invalid lvalue oder lvalue required vor sich haben, wissen Sie zumindest, was es damit auf sich hat: Sie haben dann z. B. versehentlich 100=a statt a=100 geschrieben, oder eben die falsche Klammer (z. B. eine runde statt eine eckige) verwendet.