4.6 Inkrement- und Dekrement-Operatoren
Bei den Operatoren ++ und -- handelt es sich
um spezielle Zuweisungsoperatoren. Es gibt sie jeweils in einer Präfix-
und einer Postfix-Version. Alleinstehend entsprechen beide Versionen
+=1 bzw. -=1:
++a; und a++; entsprechen beide
a+=1;
Die Operatoren sind auf ganzzahlige und Pointer-Typen anwendbar und sehr
gebräuchlich in Zählschleifen (int) und Arbeiten in
Arrays (Pointer). Der Operand muß natürlich ein Lvalue sein.
Werden Ausdrücke mit diesen Operatoren als Teilausdrücke in
größere eingebaut, kommt es auf die Stellung des Operators vor oder
nach dem Operanden dagegen sehr wohl an.
4.7 Shift-Operatoren
Die Operatoren << und >> verschieben
den ersten Operanden um
soviele Binärstellen nach links bzw. rechts, wie der zweite Operand
angibt. Beide Operanden sind ganzzahlig.
Bei Verschieben nach links werden von rechts immer binäre Nullen
eingefügt:
- unsigned char a=50;
a=(00110010)2=50,
a<<2=(11001000)2=200
Deshalb entspricht offensichtlich a<<b der Multiplikation
a·2b (bis auf Überlauf).
Bei der Verschiebung nach rechts (also zu den niederwertigen Bits hin) kommt
es auf den Typ des ersten Operanden an: Bei signed-Typen wird
von links das Vorzeichen-Bit eingeschoben, bei
unsigned-Typen Nullen:
| unsigned char a=50;
| a = (00110010)2 = 50
| a>>2 = (00001100)2 = 12
|
| unsigned char a=-50;
| a = (11001110)2 = 206
| a>>2 = (00110011)2 = 51
|
| char a=50;
| a = (00110010)2 = 50
| a>>2 = (00001100)2 = 12
|
| char a=-50;
| a = (11001110)2 = -50
| a>>2 = (11110011)2 = -13
|
Damit entspricht das Rechts-Schieben einer Division durch die entsprechende
Zweierpotenz, allerdings immer mit Rundung nach -unendlich.
Die Shift-Operatoren gibt es auch mit einer Zuweisung kombiniert:
>>= und <<=.
Beispiel:
Für Integerzahlen kann der Zweierlogarithmus wie folgt bestimmt
werden:
- int n,l=0;
cin >> n;
while ((n>>=1)>0) ++l;
cout << l;
Beispiel:
Eine Integerzahl kann man auf die untenstehende Weise im
Dualsystem ausgeben. Dabei wird ausgenutzt, daß in der internen
Darstellung (im Zweierkomplement) das oberste Bit genau dann gesetzt ist,
wenn die Zahl negativ ist.
- int n;
cin >> n;
for ( int i=8*sizeof(int) ; i>0 ; --i , n<<=1 )
cout << ( n<0?'1':'0' );
Die Operatoren sind überladen beispielsweise für Ein-/Ausgabe-Streams
(wie wir auch in diesem Beispiel sehen). Dort haben sie natürlich eine
andere Bedeutung.
4.8 Bitweise Operatoren
Mit den binären Operatoren & | ^
können ganzzahlige Objekte bitweise
mit den logischen Operationen AND, OR, XOR (exklusiv-ODER) verknüpft
werden.
Der unäre Operator ~ steht für die bitweise Inversion einer
ganzen Zahl, also die Bildung ihres Einer-Komplements.
Es folgen die Wahrheitstabellen der vier Operationen für je ein Bit:
|
|
| a | b | a&b |
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 1 | 0 | 0 |
| 1 | 1 | 1 |
|
| a | b | a|b |
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 1 |
|
| a | b | a^b |
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
|
Auch für die (binären) bitweisen Operatoren gibt es kombinierte
Zuweisungsoperatoren: &= |= ^=
Beispiel:
Als Ersatz für gepackte Felder von Wahrheitswerten
wird gerne mit Integer-Zahlen gearbeitet, in deren Darstellung gerade genau
ein Bit gesetzt ist.
Das Verhalten eines Edit-Felds in Windows® wird mit
einem long-Wort
gesteuert, dessen einzelne Bits ("Flags") jeweils eine Steuerfunktion
haben ("ES" steht für "Edit Style"):
- enum
{
ES_LEFT=0, ES_CENTER=1, ES_RIGHT=2, ES_MULTILINE=4, ES_UPPERCASE=8,
ES_LOWERCASE=16, ES_PASSWORD=32, ES_AUTOVSCROLL=64, ES_AUTOHSCROLL=128
// ... weitere weggelassen
};
Ein Steuerwort kann dann mit Hilfe des OR-Operators aus den einzelnen
Flags zusammengesetzt werden:
- long editstyle=ES_MULTILINE|ES_UPPERCASE|ES_AUTOVSCROLL;
Danach hat editstyle den Wert
4|8|64 = (0000100)2 | (0001000)2
| (1000000)2 = (1001100)2 = 76.
(Hier könnte auch + verwendet werden).
Ein Flag wird gesetzt mit OR (hier kann kein + verwendet werden, da
das entsprechende Bit ja schon gesetzt sein könnte):
- editstyle|=ES_AUTOHSCROLL;
Ein einzelnes Flag wird mit AND abgefragt:
- if (editstyle&ES_MULTILINE) ...
Mit XOR kann der Wert eines einzelnen Bits "umgekippt" werden:
- editstyle^=ES_LOWERCASE;
Mit ~a wird ein Bitmuster gesetzt, in dem das einzige
gesetzte Bit aus a gerade das einzige nicht gesetzte ist. Deshalb kann
mit NOT und AND ein einzelnes Flag gelöscht werden:
- editstyle&=~ES_AUTOVSCROLL;
4.9 Logische Operatoren
Die drei logischen Operatoren ! && ||
sind auf ganzzahlige Ausdrücke (insbesondere bool) anwendbar.
In solchen Ausdrücken stehen ganzzahlige Werte !=0 für
true
und der Wert 0 für false -- in alter
C-Tradition, wo es den Typ bool noch nicht gab.
Für den Typ bool folgen die bekannten Wahrheitstabellen für
die drei logischen Operationen NOT, AND, OR, für die es in C++
direkt Operatoren gibt:
| a | !a |
| false | true |
| true | false |
|
| a | b | a&&b |
| false | false | false |
| false | true | false |
| true | false | false |
| true | true | 1 |
|
| a | b | a||b |
| false | false | false |
| false | true | true |
| true | false | true |
| true | true | true |
|
Bemerkenswert bei && und || ist, daß bei ihnen
eine Kurzschlußauswertung (short cut evaluation)
stattfindet.
Manchmal liegt der Wert des logischen Ausdrucks bereits durch den ersten
Operanden fest:
-
false && | a = false | unabhängig von a
| true || | a = true | unabhängig von a
| | | | |
In solchen Fällen wird die Auswertung des ganzen Ausdrucks dort
abgebrochen, d.h. der zweite Operand wird nicht ausgewertet.
Durch die Linksassoziativität der Operatoren läßt sich das auf
mehrere Operanden erweitern:
- a && b && c ...
Hier wird die Auswertung nach dem ersten Operanden abgebrochen, der
den Wert false hat.
Das ist meistens aus Effizienzgründen natürlich erwünscht. Falls
im zweiten Operanden allerdings Seiteneffekte auftauchen, ist Vorsicht
geboten:
- while ( ++a<b && ++c<d ) ... while ( ++a<b || ++c<d ) ...
Links wird c einmal weniger erhöht als a,
nämlich im letzten Schleifendurchgang nicht. Rechts wird c
so lange nicht erhöht, wie noch a<b ist.
Oft kann man sich die Kurzschlußauswertung aber auch zunutze machen:
- if ( i<MAXINDEX && a[i]!=0 ) ...
Ein beliebter Fehler ist die Verwechslung der Operatoren &
und &&, bzw. | und ||. Wenn immer nur mit
0 und 1 gearbeitet wird, ist die Interpretation der
Wahrheitswerte identisch, die Operanden dürfen bei den bitweisen
Operatoren aber in beliebiger Reihenfolge ausgewertet werden, und es findet
keine Kurzschlußauswertung statt. Bei anderen Werten als 1
für "wahr" kann es natürlich zu Fehlinterpretationen kommen
(z.B. 1&2=0, 1&&2=1).
4.10 Der Komma-Operator
Man kann mehrere Ausdrücke zu einem zusammenfassen, indem man sie
durch Kommas getrennt hintereinander schreibt. Sie werden dann von
links nach rechts ausgewertet, und der gesamte Ausdruck hat den Wert
des letzten Ausdrucks. Die Werte der anderen Ausdrücke werden nicht
verwendet, sie sind nur sinnvoll, wenn sie Seiteneffekte haben.
Sinn macht diese Konstruktion vor allem in Schleifen (siehe dazu später).
Beispielsweise können so mehrere Variablen in einer for-Schleife
initialisiert, bzw. verändert werden:
- int i;
char *P;
for ( i=0,P=start ; i<MAX ; ++i,++P ) ...
Außerdem können Aktionen, die die Auswertung der Schleifenbedingung
erst ermöglichen, in die Bedingung eingebaut werden:
- while ( cin >> c , c!=',' ) cout << c;
4.11 Konstante Ausdrücke
An einigen Stellen der C++-Grammatik werden konstante Ausdrücke
gefordert, z.B. bei der Festlegung von Array-Größen und in
Mehrfach-Fallunterscheidungen.
Ein konstanter Ausdruck muß bereits zur Compilationszeit ausgewertet
werden können. Daher müssen alle Operanden konstant sein (also
Literale oder
benannte Konstanten), es dürfen keine Funktionen aufgerufen werden, und
es sind keine Seiteneffekte erlaubt.