Programmieren in C++: Einführung


Kapitel 3: Operatoren


Inhaltsverzeichnis

Dieses Buch ist unter einer Creative Commons-Lizenz lizensiert.


3.1 Verarbeiten von Daten in Variablen

Nach Informationsspeicherung die Informationsverarbeitung

Im letzten Kapitel haben Sie viel über Variablen gelernt und wissen nun ausreichend über Variablen Bescheid. Sie wissen, dass es verschiedene Datentypen gibt – sowohl intrinsische als auch nicht-intrinsische. Sie wissen, wie man Variablen anlegt, Werte in ihnen speichert und Werte zwischen Variablen austauscht. Sie haben gesehen, dass Buchstaben als Zahlen gespeichert werden und die Zuordnung von Zahlen zu Buchstaben und anderen Zeichen in Code-Tabellen stattfindet. Und zuguterletzt haben Sie sogar Arrays kennengelernt. Sie wissen nun, dass man zum Speichern von Informationen in Programmen ganz einfach Variablen benötigt.

Mit der Informationsspeicherung alleine ist es jedoch nicht getan. Computerprogramme speichern ja nicht einfach Daten, sie verarbeiten sie. Und genau hier kommen die Operatoren ins Spiel: Mit ihnen ist es möglich, auf Variablen zuzugreifen und Informationen, die in Variablen gespeichert sind, zu verarbeiten.

int i; 
i = 3; 

Schauen Sie sich obiges Beispiel aus dem vorherigen Kapitel nochmal genau an. Sie wissen, dass int ein Datentyp ist, dass i der Name einer Variablen ist, dass Anweisungen in C++ bekanntlich mit einem Semikolon abgeschlossen werden - was aber ist dieses Gleichheitszeichen?

Das Gleichheitszeichen ist einer der vielen Operatoren, die es in C++ gibt. Es handelt sich hier um den Zuweisungsoperator. Ohne diesen Operator wäre es gar nicht möglich, Werte in Variablen zu speichern. Sie haben den Zuweisungsoperator bereits häufig angewandt und damit Zahlen oder Buchstaben in Variablen abgelegt. Dies ist auch die eigentliche Funktion des Zuweisungsoperators: Werte, die vom Programmierer direkt angegeben sind oder die in anderen Variablen gespeichert sind, in eine Variable zu übertragen und dort abzulegen - sprich den Wert zuzuweisen.

Neben dem Zuweisungsoperator gibt es eine ganze Reihe an Operatoren in C++. Zum Teil sind sie selbsterklärend, zum Teil für Sie wahrscheinlich im Moment völlig unverständlich, weil Sie sich derzeit noch keinen Einsatzbereich vorstellen können. Daher ist dieses Kapitel erfahrungsgemäß ein eher einfaches Kapitel. Vieles wird Ihnen sofort einleuchten, anderes im Moment zu technisch und zu unbedeutend sein. Sie sollen trotzdem in diesem Kapitel einen Überblick über alle Operatoren bekommen, die es in C++ gibt, weil es letztendlich nicht allzu viele sind und Sie somit alle Operatoren wenigstens einmal gesehen haben.

Das wichtigste ist sowieso nicht, dass Sie jeden Operator auswendig kennen, sondern am Ende des Kapitels verstanden haben, was der Sinn und Zweck von Operatoren ist und wie sie grundsätzlich angewandt werden.


3.2 Arithmetische Operatoren

Die vier Grundrechenarten

Arithmetische Operatoren sind +, -, *, / und %. Sie werden für die aus der Mathematik bekannten Grundrechenarten verwendet. Es handelt sich wie aus der Mathematik bekannt ausschließlich um binäre Operatoren. Binäre Operatoren sind Operatoren, die jeweils genau zwei Werte oder Variablen benötigen. Auf beiden Seiten von binären Operatoren muss also jeweils ein Wert oder eine Variable stehen - ansonsten meldet der Compiler einen Fehler.

Die arithmetischen Operatoren ermöglichen die Ausführung der vier Grundrechenarten: Mit + können Werte oder Variablen addiert werden, mit - subtrahiert werden, mit * multipliziert und mit / dividiert werden. Der Operator % ist der Modulo-Operator: Er führt ebenso wie / eine Division aus, gibt jedoch als Ergebnis den ganzzahligen Restwert der Division zurück.

int a, b, c; 

a = 6; 
b = 4; 
c = a + b; 
c = a - b; 
c = a * b; 
c = a / b; 
c = a % b; 

Obige Code-Zeilen demonstrieren die Anwendung von arithmetischen Operatoren: Auf beiden Seiten - also rechts und links der Operatoren - steht jeweils eine Variable. Achten Sie auch darauf, dass normalerweise bei einer Informationsverarbeitung mit Hilfe von Operatoren fast immer auch der Zuweisungsoperator in der gleichen Zeile steht. Andernfalls würde beispielsweise eine Addition durchgeführt werden, das Ergebnis der Addition aber gar nicht gespeichert werden. Der C++-Compiler würde hier nicht meckern, weil es sich um gültigen C++-Code handelt - nur haben Sie nicht viel von einer Addition, wenn Sie das Ergebnis nirgendwo speichern.

Während die Operatoren +, - und * wie von der Mathematik gewohnt arbeiten, muss auf / und % näher eingegangen werden. Im obigen Beispiel handelt es sich bei a, b und c um Variablen vom Typ int. Bei einer Division zweier Variablen vom Typ int und der Speicherung des Ergebnisses in einer Variablen vom Typ int geht der Nachkommaanteil verloren. Während also die Division von 6 und 4 eigentlich 1,5 ergibt, wird in c lediglich die Zahl 1 gespeichert. Es findet hierbei auch keine Auf- oder Abrundung statt - der Nachkommaanteil geht einfach verloren, weil er nicht in einer Variablen vom Typ int gespeichert werden kann.

Was passiert bei der Modulo-Operation? Das Programm dividiert 6 und 4. Das ganzzahlige Ergebnis lautet hierbei 1, wobei ein Restwert von 2 übrigbleibt. Der Restwert wird von der Modulo-Operation zurückgegeben, so dass in der Variablen c 2 gespeichert wird.


3.3 Logische Operatoren

Verknüpfen von Wahrheitswerten

Logische Operatoren ermöglichen es, Wahrheitswerte zu verknüpfen. Die logischen Operatoren && und || sind binäre Operatoren, der logische Operator ! ist ein unärer Operator. Unäre Operatoren erwarten nur einen Operanden, während binäre Operatoren zwei Operanden erwarten.

bool b1, b2, r; 

b1 = true; 
b2 = false; 
r = b1 && b2; 
r = b1 || b2; 
r = !b1; 

Der Operator && ist das logische UND. Dieser Operator gibt als Ergebnis den Wahrheitswert true zurück, wenn genau beide Operanden true sind. Ist auch nur einer der beiden Operanden false, gibt das logische UND als Ergebnis false zurück.

Das || ist das logische ODER. Das logische ODER gibt als Ergebnis den Wahrheitswert true zurück, wenn mindestens einer der beiden Operanden true ist. Sind beide Operanden false, gibt das logische ODER als Ergebnis false zurück.

Der unäre logische Operator ! gibt false zurück, wenn der Operand true ist, und gibt true zurück, wenn der Operand false ist. Dieser Operator heißt NICHT-Operator. Er dreht den Wahrheitswert des Operanden einfach um.

Wie bei den arithmetischen Operatoren muss auch bei den logischen Operatoren das Ergebnis explizit gespeichert werden, wenn es nicht verloren gehen soll. Die letzte Zeile im obigen Beispiel, in der der logische Operator ! angewandt wird, liefert als Ergebnis den Wahrheitswert false zurück. Die Variable b1 ist nach dieser Zeile jedoch noch immer auf true gesetzt – der Wert der Variablen b1 ändert sich nicht!


3.4 Bitweise Operatoren

Bits setzen, löschen und verschieben

Die bitweisen Operatoren &, |, ~, ^, << und >> ermöglichen das Setzen, Löschen und Verschieben von Bits. Im Buch Allgemeine Grundlagen der Programmierung wird auf die Bedeutung von &, |, ~ und ^ ausführlich eingegangen. Es handelt sich hierbei um die bitweisen UND-, ODER-, NICHT- und EXKLUSIVES-ODER-Operatoren. Im Rahmen dieses Buchs werden die bitweisen Operatoren nicht näher vorgestellt, da sie lediglich für sehr gezielte Operationen zum Verarbeiten von Bits benötigt werden, die zum Erlernen der Programmiersprache C++ nicht entscheidend sind.

Wenn Ihnen die Operatoren << und >> aus den bisherigen Beispielprogrammen bekannt vorkommen: Sie haben mit diesen Operatoren tatsächlich gearbeitet gehabt und Variablen in die Standardausgabe std::cout "geschoben". Warum die bitweisen Operatoren im Zusammenhang mit std::cout keine Bits verschieben, sondern Informationen auf den Bildschirm ausgeben - also eine völlig andere Bedeutung haben – werden Sie noch in diesem Kapitel erfahren.


3.5 Vergleichsoperatoren

Werte und Variablen vergleichen

Vergleichsoperatoren ermöglichen wie bereits der Name sagt den Vergleich von Werten und Variablen. Sie können mit den Operatoren ==, !=, >, <, >= und <= auf Gleichheit, Ungleichheit, Größer oder Kleiner oder Größer-Gleich bzw. Kleiner-Gleich überprüfen. Es handelt sich bei allen Vergleichsoperatoren um binäre Operatoren.

Vergleichsoperatoren liefern als Ergebnis einen Wahrheitswert zurück - also entweder true oder false. Das Ergebnis hängt davon ab, ob der Vergleich richtig ist oder nicht.

int a, b; 
bool r; 

a = 5; 
b = 10; 
r = a == b; 
r = a != b; 
r = a > b; 
r = a < b; 
r = a >= b; 
r = a <= b; 

Vergleichsoperatoren werden vor allem in Kontrollstrukturen benötigt, wenn abhängig von bestimmten Bedingungen Code ausgeführt werden muss oder nicht. Kontrollstrukturen lernen Sie im folgenden Kapitel kennen.

Achten Sie darauf, dass Sie für eine Zuweisung = schreiben, für eine Überprüfung auf Gleichheit jedoch ==. Gerade Anfängern in den Programmiersprachen C und C++ passiert es immer wieder, dass sie auf Gleichheit überprüfen wollen, jedoch das zweite Gleichheitszeichen vergessen und daher eine Zuweisung schreiben.


3.6 Kombinierte Zuweisungsoperatoren

Programmierer sind faule Leute

Kombinierte Zuweisungsoperatoren sind Zuweisungsoperatoren, die mit anderen Operatoren kombiniert sind. Sinn und Zweck ist letztendlich eine abgekürzte Schreibweise für kürzeren, übersichtlicheren Code.

int a, b; 

a = 5; 
b = 10; 
a = a + b; 

Was geschieht im obigen Programm? Es werden die Variablen a und b mit Hilfe des arithmetischen Operators + addiert, woraufhin die Summe mit Hilfe des Zuweisungsoperators = in der Variablen a gespeichert wird. Dies lässt sich mit einem kombinierten Zuweisungsoperator auch wie folgt schreiben.

int a, b; 

a = 5; 
b = 10; 
a += b; 

Achten Sie auf die letzte Code-Zeile. Diese Zeile führt die gleiche Berechnung aus wie im vorherigen Beispiel: b wird zu a hinzuaddiert, und das Ergebnis wird in a gespeichert.

Genauso wie mit Hilfe des arithmetischen Operators + ein kombinierter Zuweisungsoperator += gebildet werden kann, können mit anderen Operatoren folgende kombinierte Zuweisungsoperatoren gebildet werden: -=, *=, /=, %=, &=, |=, ^=, <<= und >>=.


3.7 Inkrement- und Dekrement-Operator

Programmierer sind richtig faul

Sehen Sie sich folgenden Code an.

int a, b; 

a = 5; 
b = 1; 
a += b; 

Die Variable a wird in diesem Fall um den Wert 1 erhöht; man könnte auch sagen inkrementiert. Um Variablen lediglich um den Wert 1 zu erhöhen, gibt es den speziellen Inkrement-Operator ++. Es handelt sich hierbei um einen unären Operator. Folgender Code macht also das gleiche wie obiger Code.

int a; 

a = 5; 
++a; 

So wie es einen Inkrement-Operator gibt, gibt es auch einen Dekrement-Operator. Mit -- wird der Wert einer Variablen um 1 verringert.

Inkrement- als auch Dekrement-Operatoren können auch hinter einer Variablen angegeben sein. Ob sie vor oder hinter einer Variablen stehen kann entscheidend sein. Betrachten Sie folgendes Beispiel.

int a, b; 

a = 5; 
b = ++a; 
b = a++; 

Nachdem obiger Beispiel-Code zu Ende gelaufen ist, ist in der Variablen b der Wert 6, in der Variablen a der Wert 7 gespeichert. Was ist passiert? Steht der Inkrement-Operator vor der Variablen, wird die Variable erhöht und der um 1 erhöhte Wert zurückgegeben. Steht der Inkrement-Operator hinter der Variablen, wird die Variable erhöht, jedoch der vorherige noch nicht um 1 erhöhte Wert zurückgegeben. In beiden Fällen erhält also im obigen Beispiel die Variable b den Wert 6 zugewiesen.

Was für den Inkrement-Operator gilt, gilt natürlich auch für den Dekrement-Operator. Auch hier kann es entscheidend sein, ob der Operator vor oder hinter der Variablen angegeben ist.

Spielt es für die Programmlogik keine Rolle, ob Sie den Operator vor oder hinter die Variable stellen, dann stellen Sie ihn davor - der Operator arbeitet unter Umständen schneller als wenn er hinter der Variablen steht.


3.8 Präzedenz-Tabelle

Reihenfolge der Operator-Ausführung

Betrachen Sie folgenden Code und raten Sie, welches Ergebnis in der Variablen r gespeichert wird.

int a, b, c, r; 

a = 2; 
b = 3; 
c = 4; 
r = a + b * c; 

Speichert C++ in der Variablen r den Wert 20 oder 14? C++ hält sich an die Punkt-vor-Strich-Regel und errechnet für die Variable r den Wert 14. Wie sieht es aber im folgenden Beispiel aus?

bool a, b, c, r; 

a = false; 
b = true; 
c = true; 
r = a && b != c; 

Über den logischen Operator && und den Vergleichsoperator != werden drei Variablen vom Typ bool verknüpft. Was wird nun für ein Wert in der Variablen r gespeichert?

In welcher Reihenfolge Operatoren ausgeführt werden, legt in jeder Programmiersprache die Präzedenz-Tabelle fest. In dieser ist angegeben, welche Priorität jeder Operator besitzt. Je höher die Priorität, umso eher wird der Operator ausgeführt. Nur weil der Operator * eine höhere Priorität als der Operator + besitzt, kommt im ersten Beispiel tatsächlich 14 raus. Wären die Prioritäten anders definiert, könnte stattdessen auch zuerst + und danach * ausgeführt werden - überhaupt kein Problem für eine Programmiersprache. Die Ausführungsreihenfolge wird einfach definiert und fertig.

Tabelle 3.1. Präzedenz-Tabelle von C++
Bezeichnung Operatorsymbol Priorität Bewertungsreihenfolge
Klammern () [] 14 Von links nach rechts
Komponentenauswahl . -> 14 Von links nach rechts
Arithmetische Negation - 13 Von rechts nach links
Logische Negation ! 13 Von rechts nach links
Bitlogische Negation ~ 13 Von rechts nach links
Inkrement ++ 13 Von rechts nach links
Dekrement -- 13 Von rechts nach links
Arithmetische Operatoren * / % 12 Von links nach rechts
+ - 11 Von links nach rechts
Shift-Operatoren << >> 10 Von links nach rechts
Vergleichsoperatoren > >= < <= 9 Von links nach rechts
== != 8 Von links nach rechts
Bit-Operatoren & 7 Von links nach rechts
^ 6 Von links nach rechts
| 5 Von links nach rechts
Logische Operatoren && 4 Von links nach rechts
|| 3 Von links nach rechts
Zuweisungsoperatoren = += -= *= /= %= >>= <<= &= ^= |= 2 Von rechts nach links
Sequenzoperator , 1 Von rechts nach links

Anhand dieser Tabelle können Sie nun auch erkennen, ob der Operator && oder der Operator != zuerst ausgeführt wird - es ist der Vergleichsoperator. Daher wird im vorherigen Beispiel in der Variablen r das Ergebnis false gespeichert.

Die beiden Operatoren mit der höchsten Priorität haben wir noch gar nicht kennengelernt. Betrachten Sie folgendes modifizierte Beispiel.

int a, b, c, r; 

a = 2; 
b = 3; 
c = 4; 
r = (a + b) * c; 

Nun wird zuerst die Addition ausgeführt und dann die Multiplikation. Das heißt, r erhält nun als Ergebnis 20. Die Klammern sind ebenfalls ein Operator, und zwar der Operator mit der höchsten Priorität. Das bedeutet, durch entsprechende Klammerung können wir jederzeit die Auswertungsreihenfolge von Operatoren ändern. Setzen Sie Klammern auch dann, wenn Sie nicht sicher sind, ob die von Ihnen verwendeten Operatoren tatsächlich in der Reihenfolge ausgeführt werden, wie Sie es sich vorstellen. Die Klammern schaden nicht, erzwingen aber genau die Ausführungsreihenfolge, die Sie gerne hätten.

Die beiden in der Präzedenz-Tabelle als Komponentenauswahl aufgeführten Operatoren . und -> ignorieren wir erstmal. Diese Operatoren sind auch als Zugriffsoperatoren bekannt. Der Zugriffsoperator -> wird im Zusammenhang mit Zeigern und Objekten benötigt. Der Zugriffsoperator . wird uns beschäftigen, wenn wir Strukturen in einem späteren Kapitel kennenlernen.

Stehen mehrere Operatoren mit gleicher Priorität hintereinander, so erfolgt die Auswertungsreihenfolge der Reihe nach - entweder von links nach rechts oder von rechts nach links. Auch hier hilft nur der Blick in die Präzedenz-Tabelle. Über den Daumen gepeilt gilt: Binäre Operatoren werden von links nach rechts ausgeführt, unäre Operatoren von rechts nach links.


3.9 Kontextabhängigkeit

Plus ist nicht immer Plus

Welche Bedeutung ein Operator hat, hängt ganz entscheidend davon ab, welchen Typ seine Operanden haben. Auf diese Besonderheit wurden Sie bereits aufmerksam gemacht, als der bitweise Operator << kurz vorgestellt wurde. Laut der Beschreibung in diesem Kapitel verschiebt er Bits. In den Beispielprogrammen, die Sie bisher gesehen haben, hat er aber eine ganze andere Funktion: Dort gibt er Daten auf die Standardausgabe aus.

#include <iostream> 

int main() 
{ 
  int i = 2; 

  std::cout << (i << 2) << std::endl; 
} 

Im obigen Beispielprogramm wird der Operator << in zwei unterschiedlichen Situationen verwendet. In den runden Klammern verschiebt er Bits in der Variablen i, und zwar um zwei Stellen nach links. Außerhalb der runden Klammern gibt er Daten auf die Standardausgabe aus.

Die Bedeutung des Operators << hängt vom Typ seiner Operanden ab. Da in den Klammern eine Variable vom Typ int verarbeitet wird, kommt er seiner ursprünglichen Bedeutung nach und verschiebt Bits. Außerhalb der Klammern wird er jedoch in Zusammenhang mit std::cout eingesetzt. Und std::cout basiert auf einem Datentypen, für den der Operator << eine neue Bedeutung erhalten hat.

Die in diesem Kapitel vorgestellten Operatoren besitzen eine Standardfunktion für intrinsische Datentypen. Diese haben Sie in diesem Kapitel kennengelernt. Wenden Sie jedoch die Operatoren im Zusammenhang mit nicht-intrinsichen Datentypen an, haben die Operatoren eine völlig andere Bedeutung oder können eventuell gar nicht angewandt werden. Welche Bedeutung Operatoren im Zusammenhang mit nicht-intrinsischen Datentypen haben, hängt ganz allein von der Definition der nicht-intrinsischen Datentypen ab. Wenn Sie später eigene Datentypen erstellen werden, werden Sie sehen, wie Sie für Ihre eigenen Datentypen Funktion für Operatoren programmieren. Dies wird im Buch Programmieren in C++: Aufbau ausführlich vorgestellt.

Der Datentyp, auf dem std::cout basiert, ist so programmiert, dass er einen Operator << akzeptiert und damit arbeiten kann. Die Funktionsweise des Operators sieht hierbei so aus, dass der rechts vom Operator stehende Operand genommen und an die Standardausgabe weitergereicht wird. Obwohl der Operator also vom Aussehen mit dem bitweisen Operator identisch ist, besitzt er eine völlig andere Funktion. Das Definieren von Funktionen für Operatoren im Zusammenhang mit nicht-intrinsischen Datentypen ist eine der Stärken von C++ - eine Spracheigenschaft, die C- oder Java-Programmierern nicht zur Verfügung steht.

Sehen Sie sich folgendes Beispielprogramm an, um einen Eindruck zu bekommen, welchen Vorteil das Anpassen von Operatoren für nicht-intrinsische Datentypen hat.

#include <iostream> 
#include <string> 

int main() 
{ 
  std::string a, b, c; 

  a = "Hallo, "; 
  b = "Welt"; 
  c = a + b; 
  std::cout << c << std::endl; 
} 

Hier werden zwei Variablen a und b, beide mit nicht-intrinsischen Datentyp std::string, mit einem + verknüpft. Das +, so haben Sie es in diesem Kapitel gelernt, addiert Zahlen. Nachdem hier jedoch links und rechts vom + keine Zahlen stehen, sondern Variablen vom Typ std::string, stellt sich die Frage, was die Bedeutung von + in diesem Zusammenhang ist. Möglicherweise haben Sie eine Idee?

Der Plus-Operator ist für Variablen vom Typ std::string so definiert worden, dass Zeichenketten verknüpft und aneinandergehängt werden können. Das obige Beispielprogramm gibt demnach Hallo, Welt auf den Bildschirm aus. Der Vorteil der Neudefinition von + für Variablen vom Typ std::string ist, dass der Code einfacher zu lesen und zu verstehen wird. Die Tatsache, dass das + in diesem Zusammenhang zu einer Verknüpfung von Zeichenketten führt, ist für die meisten Programmierer einleuchtend und hilft, die Lesbarkeit des Codes zu erhöhen.

Das Ändern der Funktionsweise von Operatoren im Zusammenhang mit intrinsischen Datentypen ist in C++ übrigens nicht möglich. Wenn Sie also den Operator + anwenden, um zwei Variablen vom Typ int zu verknüpfen, dann wird wie in diesem Kapitel vorgestellt immer automatisch eine Addition ausgeführt. Da gibt es keine Ausnahme.


3.10 Aufgaben

Übung macht den Meister

Sie können die Lösungen zu allen Aufgaben in diesem Buch als ZIP-Datei erwerben.

  1. Entwickeln Sie eine C++-Anwendung, die den Anwender zur Eingabe von drei Zahlen auffordert. Das Programm soll den Wert 10 zur ersten eingegebenen Zahl hinzuaddieren, das Ergebnis mit der zweiten eingegebenen Zahl multiplizieren und dann durch die dritte eingegebene Zahl dividieren. Die Berechnung soll hierbei innerhalb einer einzigen Code-Zeile erfolgen. Das Ergebnis soll auf den Bildschirm ausgegeben werden.

  2. Entwickeln Sie eine C++-Anwendung, die den Anwender zur Eingabe einer vierstelligen Zahl auffordert. Das Programm soll daraufhin die Quersumme der vierstelligen Zahl errechnen und das Ergebnis auf den Bildschirm ausgeben.