6.5 Bit-Operatoren
Mithilfe von Bit-Operatoren können Sie direkt auf die binäre Darstellung der Zahlen zurückgreifen.
Zuerst eine kurze Übersicht, welche Bit-Operatoren es gibt:
Bit-Operator | Bedeutung |
&, &= |
Bitweise AND-Verknüpfung |
|, |= |
Bitweise OR-Verknüpfung |
^, ^= |
Bitweises XOR |
~ |
Bitweises Komplement |
>>, >>= |
Rechtsverschiebung |
<<, <<= |
Linksverschiebung |
Wie schon bei den arithmetischen Operatoren steht Ihnen auch bei den bitweisen Operatoren die erweiterte Zuweisungsschreibweise zur Verfügung.
Hinweis |
Es ist nicht zulässig, als Operanden float bzw. double zu verwenden. Die Operanden müssen bei der Verwendung von Bit-Operatoren immer ein ganzzahliger Datentyp sein. |
6.5.1 Bitweises UND
Steht der &-Operator zwischen zwei Operanden, so handelt es sich um den bitweisen UND-Operator. Dieser ist leicht mit dem unären Adressoperator (siehe scanf()) zu verwechseln.
Der Operator wird hauptsächlich dafür verwendet, einzelne Bits gezielt zu löschen. Folgendes Programmbeispiel soll dies demonstrieren:
/* and.c */ #include <stdio.h> int main(void) { int x=55; printf("x=%d\n",x); x= x&7; printf("x=%d\n",x); /* x=7 */ return 0; }
Nach der Ausführung des Programms werden Sie sich fragen, warum die Verknüpfung mit dem UND-Operator zum Ergebnis 7 führt. Sehen Sie sich dies wieder in der Bitdarstellung an (unter Verwendung der ersten 8 Bits):
Abbildung 6.3 Verwendung des bitweisen UND-Operators
Dabei gelten per Definition folgende Regeln für den bitweisen UND-Operator:
BitA | BitB | BitA&BitB |
0 |
0 |
0 |
0 |
1 |
0 |
1 |
0 |
0 |
1 |
1 |
1 |
Mit dem bitweisen UND-Operator lässt sich sehr gut testen, ob eine Zahl gerade oder ungerade ist. Es muss nur Bit 0 (bzw. das 1. Bit) daraufhin überprüft werden, ob es gesetzt (ungerade, also = 1) oder nicht gesetzt (gerade, also = 0) ist. Folgendes Beispiel demonstriert dies:
/* gerade.c */ #include <stdio.h> int main(void) { int x; printf("Bitte geben Sie eine Zahl ein: "); scanf("%d",&x); if(x&1) // Ist das erste Bit gesetzt? printf("Eine ungerade Zahl\n"); else // Nein, es ist nicht gesetzt. printf("Eine gerade Zahl\n"); return 0; }
6.5.2 Bitweises ODER
Mit dem bitweisen ODER-Operator können Sie gezielt zusätzliche Bits setzen. Verwendet wird dieser wie schon zuvor der bitweise UND-Operator:
char x = 1; x = x|126; // x=127
Auch hierzu die Bitdarstellung:
Abbildung 6.4 Verwendung des bitweisen ODER-Operators
Für den ODER-Operator gilt folgende Verknüpfungstabelle:
BitA | BitB | (BitA|BitB) |
0 |
0 |
0 |
0 |
1 |
1 |
1 |
0 |
1 |
1 |
1 |
1 |
6.5.3 Bitweises XOR
Der exklusive ODER-Operator XOR liefert nur dann eine 1 zurück, wenn beide Bits unterschiedlich sind. Er ist sehr gut geeignet, um Bits umzuschalten. Alle gesetzten Bits werden gelöscht und alle gelöschten gesetzt. Hier sehen Sie ein Beispiel:
char x=20; x = x^55; // x=35
In binärer Darstellung ergibt sich aus dieser Operation folgendes Bild:
Abbildung 6.5 Verwendung des exklusiven ODER-Operators XOR
Für XOR-Verknüpfungen gilt folgende Verknüpfungstabelle:
BitA | BitB | BitA^BitB |
0 |
0 |
0 |
0 |
1 |
1 |
1 |
0 |
1 |
1 |
1 |
0 |
6.5.4 Bitweises Komplement
Der NOT-Operator (~) wirkt sich auf Zahlen so aus, dass er jedes einzelne Bit invertiert. Bei vorzeichenbehafteten Datentypen entspricht das einer Negation mit anschließender Subtraktion von 1:
char x=20; x=~x; /* x= -21 */
Für den NOT-Operator gilt folgende Verknüpfungstabelle:
BitA | ~BitA |
0 |
1 |
1 |
0 |
6.5.5 Linksverschiebung
Mit einer Linksverschiebung (<<) werden alle Bits einer Zahl um n Stellen nach links gerückt. Die rechts entstehenden Leerstellen werden mit 0 aufgefüllt.
Achtung |
Achtung bei Vorzeichen! Ist der Datentyp signed, ändert sich das Vorzeichen, wenn eine 1 in die Bitstelle des Vorzeichens gerückt wird. Falls der linke Operand aber einen negativen Wert hat, so ist das Ergebnis compiler-spezifisch. |
Hier sehen Sie ein Beispiel für eine Linksverschiebung:
/* shift_left.c */ #include <stdio.h> int main(void) { char x=8; printf("x=%d\n",x); x<<=1; // Alle Bits um 1 Stelle nach links printf("x=%d\n",x); return 0; }
Warum aus dem Wert 8 eine 16 wurde, wird aus der folgenden Bitdarstellung ersichtlich:
Abbildung 6.6 Bitverschiebung nach links
Sie werden es bemerkt haben: Hier wurde eine Multiplikation durchgeführt. Auf diese Weise können Zahlen sehr gut potenziert werden. Die Bitstelle um eine Position nach links zu rücken, bedeutet mathematisch eine Multiplikation mit 2. Bei Einrückung um zwei Stellen nach links wird mit 4 multipliziert, bei drei Stellen mit 8, bei vier Stellen mit 16 usw.
Solche Bitverschiebungen können – abhängig vom System – bis zu 40(!)-mal schneller ablaufen als normale arithmetische Berechnungen im Stil von 4*x.
6.5.6 Rechtsverschiebung
Die Rechtsverschiebung mit dem >>-Operator ist das Gegenstück zur Linksverschiebung (<<). Damit können Sie statt einer Multiplikation mit 2 eine Division durch 2 bewirken. Ansonsten gilt das Gleiche wie für die Linksverschiebung.
6.5.7 Rezept für Fortgeschrittene
Oft ist eine Funktion wünschenswert, mit der eine Zahl daraufhin getestet wird, ob ein bestimmtes Bit gesetzt ist, oder mit der sich gezielt einzelne Bits setzen oder löschen lassen. Hierzu ein Listing mit entsprechenden Funktionen:
/* playing_bits.c */ #include <stdio.h> #define BYTE unsigned char /* Funktion : Bit_Test() * val : der Wert, den es zu testen gilt * bit : Bitnummer, die abgefragt wird, ob gesetzt (0-7) * Rückgabewert : (1)=Bit gesetzt; (0)=Bit nicht gesetzt */ int Bit_Test(BYTE val, BYTE bit) { BYTE test_val = 0x01; /* dezimal 1 / binär 0000 0001 */ /* Bit an entsprechende Pos. schieben */ test_val = (test_val << bit); /* 0=Bit nicht gesetzt; 1=Bit gesetzt */ if ((val & test_val) == 0) return 0; /* nicht gesetzt */ else return 1; /* gesetzt */ } /* Funktion : Bit_Set() * val : Wert, bei dem das Bit gesetzt werden soll * bit : Bitnummer, die gesetzt werden soll (0-7) * Rückgabewert : keiner */ void Bit_Set(BYTE *val, BYTE bit) { BYTE test_val = 0x01; /* dezimal 1 / binär 0000 0001 */ /* Bit an entsprechende Pos. schieben */ test_val = (test_val << bit); *val = (*val | test_val); /* Bit an Pos. bit setzen */ } /* Funktion : Bit_Clear() * val : Wert, bei dem das Bit gelöscht werden soll * bit : Bitnummer, die gelöscht werden soll (0-7) * Rückgabewert : keiner */ void Bit_Clear(BYTE *val, BYTE bit) { BYTE test_val = 0x01; /* dezimal 1 / binär 0000 0001 */ /* Bit an entsprechende Pos. schieben */ test_val = (test_val << bit); *val = (*val & (~test_val)); /* Bit an Pos. bit löschen*/ } int main(void) { BYTE wert = 0; /* Test, ob Bit 0 gesetzt ist */ printf("%s\n",Bit_Test(wert, 0)?"gesetzt":"nicht gesetzt"); Bit_Set(&wert, 0); /* Bit 0 setzen */ /* Wieder testen, ob Bit 0 gesetzt ist */ printf("%s\n",Bit_Test(wert, 0)?"gesetzt":"nicht gesetzt"); Bit_Clear(&wert, 0); /* Bit 0 wieder löschen */ /* Wieder testen, ob Bit 0 gesetzt ist */ printf("%s\n",Bit_Test(wert, 0)?"gesetzt":"nicht gesetzt"); return 0; }
Die Funktionen können Sie natürlich Ihren eigenen Bedürfnissen entsprechend anpassen. Sie dienen nur als Anregung für weitere Spielereien mit Bits und Bytes.
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.