Abarbeitung von Operatoren

In C und C++ können in einer einzigen Anweisung mehrere Operatoren aufgeführt werden. Die Reihenfolge, in der die einzelnen Operatoren abgearbeitet werden, ist von der Sprache anhand Abarbeitungsregeln genau vorgegeben.

Details

Grundsätzlich entsprechen die von den Sprachen C und C++ vorgegebenen Abarbeitungsregeln einem intuitiven Verständnis der semantischen Bedeutungen der Operatoren, womit bei alltäglichem Gebrauch der Programmiersprache normalerweise keine Probleme entstehen. Gelegentlich jedoch weisen diese Regeln Besonderheiten auf, die manchmal zu unerwünschten und schwer auffindbaren Fehlern führen. Für jeden Operator sind sowohl eine Rangordnung, als auch eine Abarbeitungsrichtung spezifiziert, welche die Ausführung der Anweisung festlegen.

Rangordnung

Für jeden Operator wird eine Priorität spezifiziert, indem jedem Operator ein Rang zugeordnet wird. Diese (im englischen als operator-precedence bezeichnete) Rangordnung stellt die Assoziativität verschiedenartiger Operatoren aufgrund deren Priorität sicher. Operatoren mit tiefem Rang werden prioritärer behandelt wie solche mit einem höheren. Der prioritärste Rang ist 1. Auf dieser Seite wurde versucht, den wichtigsten Quellen möglichst gerecht zu werden und so wurden insgesamt 19 Ränge verteilt, welche in der Tabelle weiter unten aufgelistet sind.

Beispielsweise werden mathematische Operatoren prioritärer behandelt als Zuweisungsoperatoren, in dem Sinne, dass zuerst eine Rechnung ausgeführt und erst dann das Resultat einer Variablen zugewiesen wird. Die Rangordnung beinhaltet beispielsweise auch das bekannte Assoziativgesetzt (Punkt-vor-Strich-Regel) der Mathematik:




10
14
#include <stdio.h>

int main(){
  printf("%d\n",  4 + 3  * 2);
  printf("%d\n", (4 + 3) * 2);
  return 0;
}

Der Multiplikations-Operator hat eine höhere Priorität wie der Additions-Operator. Somit wird in der ersten Zeile die Multiplikation vor der Addition ausgeführt, genauso, wie es in der Mathematik üblich ist. Eine Klammerung jedoch hat die höchste Priorität und wird stets vor allen anderen Operatoren ausgeführt, weswegen auf der zweiten Zeile zuerst die Addition und erst danach die Multiplikation ausgeführt wird.

Abarbeitungsrichtung

Wenn mehrere Operatoren mit gleichem Rang hintereinander auftreten, so legt die sogenannte Abarbeitungsrichtung fest, ob die Operatoren entweder von links nach rechts → oder von rechts nach links ← abgearbeitet werden. Operatoren mit gleichem Rang haben stets dieselbe Abarbeitungsrichtung. Eine vollständige Auflistung der Abarbeitungsrichtung aller Operatoren findet sich weiter unten.

Die Addition wird beispielsweise von links nach rechts abgearbeitet, im folgenden Beispiel zuerst also a mit b addiert und danach das Resultat mit c. Die Zuweisung jedoch wird von rechts nach links abgearbeitet, womit im folgenden Beispiel zuerst der Wert c der Variablen b zugewiesen wird und dann der Wert, der nun in b gespeichert ist, in a geschrieben wird. Als Folge beinhalten alle drei Variablen denselben Inhalt wie c.

a + b + c;
a = b = c;

Auch für unäre Operatoren wird eine Abarbeitungsrichtung angegeben. Diese ist grundsätzlich von links nach rechts. Nur die beiden Operatoren Post-Inkrement-Operator ++ und Post-Dekrement-Operator -- werden von rechts nach links abgearbeitet. Bei unären Operatoren, die von links nach rechts abgearbeitet werden, steht der Operator links und der Operand rechts. Bei einer Abarbeitung von rechts nach links steht der Operator rechts und der Operand links. Im folgenden Beispiel werden der unäre Negativ-Operator (von links nach rechts) und der unäre Post-Inkrement-Operator (von rechts nach links) aufgeführt.

-a
a++

Die Operatorenklammerung () und die Bereichs-Operatoren haben KEINE Abarbeitungsrichtung. Dies ist deswegen so, da sie im eigentlichen Sinne nur Hinweise für den Compiler sind, wo eine Variable aufzufinden ist, oder wie die Abarbeitungsreihenfolge manuell im Code festgelegt wurde. Für solche Operatoren kann ein Compiler keinen Assemblercode erstellen, und somit kann auch keine Abarbeitungsfolge angegeben werden, da es nichts abzuarbeiten gibt.

Rank 1

Rank 2   →

Rank 3   ←

Rank 4   →

Rank 5   →

Rank 6   →

Rank 7   →

Rank 8   →

Rank 9   →

Rank 10   →

Rank 11   →

Rank 12   →

Rank 13   →

Rank 14   ←

Rank 15   ←

Rank 16   ←

Bemerkungen

Es ist zu beachten, dass eine solche Auflistung in den Standards nie explizit definiert wird. Vielmehr ist die Rangordnung und Abarbeitungsrichtung rein aufgrund der Syntax-Regeln fest vorgegeben. Der Autor empfindet jedoch genauso wie viele anderen Autoren eine Auflistung wie oben als didaktisch sinnvoll. Dennoch handhaben verschiedene Quellen die Umwandlung der Syntax-Regeln in eine solche Rangliste unterschiedlich, weswegen dutzende solcher Ranglisten existieren, welche alle in etwa dasselbe, aber nicht exakt dasselbe aussagen. Allerdings werden beim alltäglichen Programmieren äusserst selten Probleme angetroffen, da viele Operatoren gar nicht erst gemeinsam auftreten und somit eine Rangfolge hinfällig ist.

Es ist zudem zu beachten, dass die hier aufgeführten Regeln zwar die genaue Abarbeitungsreihenfolge der Operatoren spezifizieren, jedoch nicht das Auswerten der hierfür benötigten Operanden. In C und C++ ist nur in den wenigsten Fällen spezifiziert, wann ein Operand innerhalb eines Ausdrucks gelesen oder geschrieben werden soll. Dadurch haben Compiler die Möglichkeit, den Code sehr stark zu optimieren. Sollten allerdings Operanden im selben Ausdruck gleichzeitig gelesen wie auch geschrieben werden, so kann dies in ganz spezifischen Fällen zu undefiniertem Verhalten führen, da jeder Compiler die Operanden in unterschiedlicher Reihenfolge lesen oder schreiben könnte. Mehr Informationen dazu können bei den Sequenz-Punkten nachgelesen werden.