![]() |
|
|||||
i >>> nDer Operator >>> verschiebt eine Variable (Bitmuster) bitweise um n Schritte nach rechts, ohne das Vorzeichen der Variablen zu berücksichtigen (vorzeichenloser Rechts-Shift). So werden auf der linken Seite (MSB) nur Nullen eingeschoben; das Vorzeichen wird mitgeschoben. Bei einer positiven Zahl hat dies keinerlei Auswirkungen, und das Verhalten ist wie beim >>-Operator.
Die Ausgabe ist für den negativen Operanden besonders spannend: 64 >>> 1 = 32 -64 >>> 1 = 2147483616 Ein <<<-Operator macht allerdings keinen Sinn, da beim Links-Shiften sowieso nur Nullen eingefügt werden. Division und Multiplikation mit VerschiebeoperatorenWird ein Wert um eine Stelle nach links geschoben, so kommt in das niedrigste Bit (das Bit ganz rechts) eine Null hinein und alle Bits schieben sich eine Stelle weiter. Das Resultat ist, dass die Zahl mit 2 multipliziert wird. Natürlich müssen wir mit einem Verlust von Informationen rechnen, wenn das höchste Bit gesetzt ist, denn dann wird es herausgeschoben, aber das Problem haben wir auch schon bei der normalen Multiplikation. Es gibt in Java keinen Operator, der die Bits rollt, der also die an einer Stelle herausfallenden wieder an der anderen Seite einfügt. Wenn ein einmaliger Shift nach links mit 2 multipliziert, so würde eine Verschiebung um zwei Stellen nach links eine Multiplikation mit 4 bewirken. Allgemein gilt: Bei einem Shift von i nach links ergibt sich eine Multiplikation mit 2i. Wir können dies dazu nutzen, beliebige Multiplikationen durch Verschiebung nachzubilden.
Diese Umsetzung ist nicht immer einfach, und es gibt tatsächlich kein Verfahren, welches eine optimale Umsetzung liefert. Doch arbeiteten viele Prozessoren auf diese Weise intern die Multiplikation ab, und ein Compiler nutzt dies gern zur Optimierung der Laufzeit. Eine Verschiebeoperation ist bei vielen Prozessoren schneller als eine Multiplikation. Doch ist hier Obacht zu geben, denn eine lange Folge von Verschiebungen ist nicht schneller, sondern langsamer als eine direkte Multiplikation. Neben der Addition kommt selbstverständlich auch die Subtraktion in Frage. Ersetzen wir im oberen Beispiel das Plus durch ein Minus, so bekämen wir eine Multiplikation mit 6. Natürlich müssen wir auf die Überläufe der Zwischenergebnisse bei großen Zahlen achten. Diese würde es bei einer echten Multiplikation nicht geben. Was wir am Beispiel der Verschiebung nach links gezeigt haben, funktioniert genauso mit einem Shift nach rechts. Jetzt wird bei einmaliger Verschiebung durch 2 dividiert. Jetzt können beliebige Ausdrücke mit * und / und einer Konstante auf Verschiebungen und einfachen arithmetischen Operationen abgebildet werden. Die Bitoperatoren in Assembler verglichen mit <<<, << und >>Auch in Assembler gibt es zwei Gruppen von Schiebeoperatoren: die arithmetischen Schiebebefehle (SAL und SAR), die das Vorzeichen des Operanden beachten, und die logischen Schiebebefehle (SHL und SHR), die den Operanden ohne Beachtung eines etwaigen Vorzeichens schieben. Die Befehle SAL und SHL haben die gleiche Wirkung. So ist >>> der Bitoperator, in dem das Vorzeichen nicht beachtet wird, wie SHR in Assembler. Es gibt in Java auch keinen Bitoperator <<<, da - wie in Assembler - SAL = SHL gilt (<<< würde die gleiche Wirkung haben wie <<). Bits rotierenJava hat zwar Operatoren zum Verschieben von Bits, aber nicht zum Rotieren. Beim Rotieren werden Bits um eine bestimmte Stelle verschoben, die herausfallenden Bits kommen aber auf der anderen Seite wieder rein. Eine Funktion ist leicht geschrieben: Der Trick dabei ist, die herausfallenden Bits vorher zu extrahieren und auf der anderen Seite wieder einzusetzen. public static int rotateLeft( int v, int n ) { return (v << n) | (v >>> (32 - n)); } public static int rotateRight( int v, int n ) { return (v >>> n) | (v << (32 - n)); } Die Funktionen rotieren jeweils n Bits nach links oder rechts. Da der Datentyp int ist, ist die Verschiebung n in dem Wertebereich von o bis 31 erlaubt. 2.9.5 Setzen, Löschen, Umdrehen und Testen von Bits
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Beispiel Anwendung aller Bitoperatoren:
int setBit( int n, int pos ) { return n | (1 << pos); } int clearBit( int n, int pos ) { return n & ~ (1 << pos); } int flipBit( int n, int pos ) { return n ^ (1 << pos); } boolean testBit( int n, int pos ) { int mask = 1 << pos; return (n & mask) == mask; // alternativ: return (n & 1<<pos)!=0;} |
In Java gibt es ebenso wie in C(++) einen Operator, der drei Operanden benutzt. Dies ist der Bedingungsoperator, der auch Konditionaloperator, ternärer Operator beziehungsweise trinärer Operator genannt wird. Er erlaubt es, den Wert eines Ausdrucks von einer Bedingung abhängig zu machen, ohne dass dazu eine if-Anweisung verwendet werden muss. Die Operanden sind durch ? beziehungsweise : voneinander getrennt:
ConditionalOrExpression ? Expression : ConditionalExpression
Der erste Ausdruck muss vom Typ boolean sein und bestimmt, ob das Ergebnis Expression oder ConditionalExpression ist. Der Bedingungsoperator kann eingesetzt werden, wenn der zweite und dritte Operand ein numerischer Typ, boolescher Typ oder Referenztyp ist. Der Aufruf von Methoden, die demnach void zurückgeben, ist nicht gestattet.
Eine Anwendung für den trinären Operator ist oft eine Zuweisung an eine Variable:
Variable = Bedingung ? Ausdruck1 : Ausdruck2;
Der Wert der Variablen wird jetzt in Abhängigkeit von der Bedingung gesetzt. Ist sie erfüllt, dann erhält die Variable den Wert des ersten Ausdrucks, andernfalls wird der Wert des zweiten Ausdrucks zugewiesen.
Beispiel So etwa für ein Maximum:
max = ( a > b ) ? a : b; Dies entspricht beim herkömmlichen Einsatz für if/else: if ( a > b ) max = a; else max = b; |
Mit dem Rückgabewert können wir alles Mögliche machen, etwa ihn direkt ausgeben. Das wäre mit if/else nur mit temporären Variablen möglich.
System.out.println( ( a > b ) ? a : b );
Der Bedingungsoperator findet sich häufig in kleinen Funktionen. Dazu einige Beispiele:
Beispiel Um das Maximum oder Minimum zweier Ganzzahlen zurückzugeben, definieren wir die kompakte Funktion:
static int min( int a, int b ) { return a < b ? a : b; } static int max( int a, int b ) { return a > b ? a : b; } |
Beispiel Der Absolutwert einer Zahl wird zurückgegeben durch
x >= 0 ? x : -x |
Beispiel Der Groß-/Kleinbuchstabe im ASCII-Alphabet soll zurückgegeben werden. Dabei ist c vom Typ char.
// Konvertierung in Großbuchstaben c = (char)(Character.isLowerCase(c) ? (c-'a'+'A' ) : c); // Konvertierung in Kleinbuchstaben c = (char)(Character.isUpperCase(c) ? (c-'A'+'a' ) : c); Da diese Umwandlung nur für die 26 ASCII-Buchstaben funktioniert, ist es besser, Bibliotheksmethoden für alle Unicode-Zeichen zu verwenden. Dazu dienen die Funktionen toUpperCase() und toLowerCase(). |
Beispiel Es soll eine Zahl n, die zwischen 0 und 15 liegt, zur Hexadezimalzahl konvertiert werden.
(char)( (n < 10) ? ('0' + n) : ('a' - 10 + n ) ) |
Die Anwendung des trinären Operators führt schnell zu schlecht lesbaren Programmen und sollte daher vorsichtig eingesetzt werden. In C(++) führt die unbeabsichtigte Mehrfachauswertung in Makros zu schwer auffindbaren Fehlern. Gut, dass uns das in Java nicht passieren kann. Durch ausreichende Klammerung muss sichergestellt werden, dass die Ausdrücke auch in der beabsichtigten Reihenfolge ausgewertet werden. Im Gegensatz zu den meisten Operatoren ist der Bedingungsoperator rechtsassoziativ. (Die Zuweisung ist ebenfalls rechtsassoziativ.) Die Anweisung
b1 ? a1 : b2 ? a2 : a3
ist demnach gleichbedeutend mit
b1 ? a1 : ( b2 ? a2 : a3 )
Beispiel Wollen wir eine Funktion schreiben, die für eine Zahl n abhängig vom Vorzeichen -1, 0 oder 1 liefert, lösen wir das Problem mit geschachteltem trinären Operator.
int sign( int n ) { return (n < 0) ? -1 : (n > 0) ? 1 : 0; } |
Der trinäre Operator liefert als Ergebnis einen Ausdruck zurück, der auf der rechten Seite einer Zuweisung verwendet werden kann. Da er rechts vorkommt, nennt er sich auch rvalue. Er lässt sich nicht derartauf der linken Seite einer Zuweisung einsetzen, dass er eine Variable auswählt, der ein Wert zugewiesen wird.1
Beispiel Folgende Anwendung des trinären Operators ist in Java nicht möglich:34
((richtung>=0) ? hoch : runter) = true; |
Obwohl sich in Java die Operatoren fast alle auf primitive Datentypen beziehen, gibt es doch eine bemerkenswerte Verwendung des Plus-Operators. Objekte vom Typ String können durch den Plus-Operator mit anderen Strings verbunden werden. Dies wurde in Java eingeführt, da ein Aneinanderhängen von Zeichenketten oft benötigt wird. Im Kapitel über die verschiedenen Klassen wird String noch etwas präziser dargestellt. Insbesondere werden die Gründe dargelegt, die zur Einführung eines String-Objekts auf der einen Seite, aber auch zur Sonderbehandlung in der Sprachdefinition auf der anderen Seite führten.
Listing 2.11 HelloName.java
// Ein Kleines ,Trallala'-Programm in Java class HelloName { public static void main( String args[] ) { // Zwei Strings deklarieren String name, intro; // Ersetze "Ulli" durch deinen Namen name = "Ulli"; // Nun die Ausgabe intro = "Tri Tra Trallala \"Trullala\", sage " + name; System.out.println( intro ); } }
Nachdem ein String intro aus verschiedenen Objekten zusammengesetzt wurde, läuft die Ausgabe auf dem Bildschirm über die Funktion println() ab.
Tri Tra Trallala "Trullala", sage Ulli
Die Funktion schreibt den String auf die Konsolenausgabe und setzt hinter die Zeile noch einen Zeilenvorschub. (Obwohl das \n nicht unbedingt plattformunabhängig ist, wollen wir es trotzdem nutzen.2) Das Objekt System.out definiert den Ausgabekanal.
Beispiel Da der Plus-Operator für Zeichenketten streng von links nach rechts geht, bringt das mit eingebetteten arithmetischen Ausdrücken mitunter Probleme. Diese müssen dann geklammert werden, wie im Folgenden zu sehen:
"Ist 1 größer als 2? " + (1>2 ? "nein" : "ja"); |
Wäre der Ausdruck um den Bedingungsoperator nicht geklammert, dann würde der Plus-Operator den Ausdruck 1>2 auswerten, in einen String umwandeln und diesen an die erste Zeichenkette anhängen. Jetzt käme das Fragezeichen. Dies ist aber nur für boolesche Werte erlaubt, aber links stände die Zeichenkette. Das wäre ein Fehler.
1In C(++) kann dies durch *((Bedingung) ? &a : &b) = Ausdruck; über Pointer gelöst werden.
2Besser ist es, das Absatzendezeichen aus der Systemeigenschaft zu nehmen. Dazu dient die Anweisung System.getProperty("line.separator"), die einen String liefert.
| << zurück |
Copyright © Galileo Press GmbH 2003
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.