9. Bitlevel-Verarbeitung in C Ein Bit stellt die kleinste im Rechner darstellbare Informationseinheit dar. Die kleinste adressierbare Einheit in einem Speicher ist aber das Byte. C stellt deshalb für Bits keinen eignen Datentyp zur Verfügung. Bei der maschinennahen Programmierung werden häufig bitweise Operationen notwendig. Deshalb stellt C diese in 3 Gruppen von Operationen bereit: 1. Einerkomplement (die bitweise Negation als unärer Operator), 2. Die binären Operatoren bitweise logisches UND, bitweise logisches ODER, bitweise logisches exklusives ODER, 3. Die Verschiebeoperationen 1 Bitlevel-Verarbeitung in C Für die Verarbeitung von Bits wird immer von vorzeichenlosen Variablen ausgegangen, d.h. - Byte unsigned char - Wort unsigned int - Doppelwort unsigned long Die durch Bitoperationen entstandenen Werte kann man über den Zuweisungsoperator (=) wiederum vorzeichenlosen Variablen zuweisen. Auf (Bit-) Werte kann man die üblichen Vergleichsoperationen ==,!=, <, >, <=, >= anwenden. Es wird für den Vergleich der zugrunde liegende Binärwert herangezogen. Es sind spezielle Zuweisungsoperationen definiert (z.b. &=). 2
9.1 Bitoperationen Um Ausgangswerte für Bitoperationen bereitzustellen und dann unsigned-variablen zuzuweisen, verwendet man häufig Hexadezimalzahlen, gelegentlich auch Oktalzahlen. Hexadezimalkonstanten werden durch die Zeichenfolge 0x eingeleitet und bestehen je nach Länge der unsigned Variablen aus einer Folge von Hexadezimalzahlen 0,1,..., 9,A,B,...F. Ist bei einer Hexadezimalkonstante die angegebene Länge kürzer als die unsigned Variable, wird links mit Nullen aufgefüllt. Beispiel: unsigned i=0xaf; Diese Darstellung hätte man auch durch die Zuweisung i=175 erzielen können. 3 Einerkomplement-Operator ~ Wird der einstellige Einerkomplementoperator ~ auf eine unsigned-variable angewendet, wird eine bitweise Komplementbildung erreicht. Beispiel: unsigned i=0xaf; Nach der Zuweisung i = ~i enthält i folgende Bitdarstellung: 1111 1111 0101 0000 Beachte: Das Einerkomplement ist nicht mit dem Zweierkomplement als Darstellungsart für vorzeichenbehaftete, negative Zahlen zu verwechseln. Das Zweierkomplement entsteht aus dem Einerkomplement, indem an der wertniedrigsten Stelle eine binäre 1 addiert wird! 4
bitweises logisches UND & Für die bitweisen binären Operationen gilt für das Einzelbit die Boolsche Algebra. Mit der Operation & wird ein bitweises logisches UND als Operation ausgeführt. Beispiel: unsigned i=0xaf; unsigned k, j=0xf1; Bitdarstellung für j: 0000 0000 1111 0001 Nach der Operation k = i & j enthält k folgende Bitdarstellung: 0000 0000 1010 0001 Bei einer Zuweisung j = j & i kann man auch kurz schreiben j & = i. 5 bitweises logisches ODER Mit der Operation wird ein bitweises logisches ODER als Operation ausgeführt. Beispiel: unsigned i=0xaf; unsigned k, j=0xf1; Bitdarstellung für j: 0000 0000 1111 0001 Nach der Operation k = i j enthält k folgende Bitdarstellung: 0000 0000 1111 1111 Bei einer Zuweisung j = j i kann man auch kurz schreiben j = i. 6
bitweises exklusives ODER ^ Mit der Operation ^ wird ein bitweises exklusives ODER als Operation ausgeführt. Beispiel: unsigned i=0xaf; unsigned k, j=0xf1; Bitdarstellung für j: 0000 0000 1111 0001 Nach der Operation k = i ^ j enthält k folgende Bitdarstellung: 0000 0000 0101 1110 Bei einer Zuweisung j = j ^ i kann man auch kurz schreiben j ^ = i. 7 bitweise Rechtsverschiebung >> Mit der Operation >> wird ein bitweises Rechtsverschieben ausgeführt. Der Wert des linken Operanden wird um so viele Bits nach rechts verschoben, wie sein Binärwert angibt. Durch das Rechtsverschieben werden links entsprechend viele binäre 0 eingeschoben. Beispiel: unsigned i=0xaf; unsigned k, j=0x6; Bitdarstellung für j: 0000 0000 0000 0110 Nach der Operation k = i >> j enthält k folgende Bitdarstellung: 0000 0000 0000 0010 Bei einer Zuweisung j = j >> i kann man auch kurz schreiben j >> = i. 8
bitweise Linksverschiebung << Mit der Operation << wird ein bitweises Linksverschieben als Operation ausgeführt. Der Wert des linken Operanden wird um so viele Bits nach links verschoben, wie sein Binärwert angibt.durch das Linksverschieben werden rechts entsprechend viele binäre 0 eingeschoben. Beispiel: unsigned i=0xaf; unsigned k, j=0x6; Bitdarstellung für j: 0000 0000 0000 0110 Nach der Operation k = i << j enthält k folgende Bitdarstellung: 0010 1011 1100 0000 Bei einer Zuweisung j = j << i kann man auch kurz schreiben j << = i. 9 Bemerkungen zu Verschiebeoperationen (1) Beim bitweisen Verschieben als Operation müssen natürlich die Längen der Variablen beachtet werden. Wenn zu viele Bits verschoben werden, entstehen dann nur 0- Werte durch das Auffüllen, bzw. gültige Bits werden herausgeschoben. Beispiel: unsigned i=0xaf; unsigned k, j=0x10; Bitdarstellung für j: 0000 0000 0001 0000 Nach der Operation k = i << j enthält k folgende Bitdarstellung: 0000 0000 0000 0000 10
Bemerkungen zu Verschiebeoperationen (2) Beispiel: unsigned char i=0xaf; unsigned char k, j=0x06; Bitdarstellung für i: 1010 1111 Bitdarstellung für j: 0000 0110 Nach der Operation k = i << j enthält k folgende Bitdarstellung: 1100 0000 Beachten Sie, dass eine Linksverschiebung einer unsigned Variablen um n Bits, einer Multiplikation mit 2 n entspricht. Beispiel: unsigned i=3; i<<=3; // Wert von i ist 24 Eine Rechtsverschiebung einer unsigned Variablen um n Bits entspricht einer Division durch 2 n entspricht. Beispiel: unsigned i=128; i>>=3; // Wert von i ist 16 11 Maskierungen, Packen und Entpacken Bitoperationen werden in Programmen hauptsächlich zum Packen und Entpacken von Informationen benutzt. Innerhalb der Rechnerbetriebssysteme werden viele Informationen über Datum, Zeit, Hardwareausrüstung als bitweise gepackte Informationen bereitgestellt. Zum Packen und Entpacken werden bitweise Operationen, wie insbesondere das bitweise UND und Verschiebeoperationen benötigt. Mit dem bitweisen UND schneidet man mit Hilfe einer geeigneten Maske die relevanten Bits aus einem Byte oder einem Wort bzw. Doppelwort heraus. 12
Setzen/Rücksetzen von Bitpositionen Am Beispiel von 8-Bit Feldern (unsigned char) unsigned char bitfeld =0x00; // Ausgangspunkt: kein Bit ist gesetzt Wenn eine bestimmte Bitposition i (0<=i<=7) auf 1 gesetzt werden soll: unsigned char setz = 0x01; set=setz<<i; // Verschieben der Eins auf Position i bitfeld = setz; // log. Oder von 1 auf Position i Wenn eine bestimmte Bitposition i (0<=i<=7) auf 0 gesetzt werden soll: unsigned char ruecksetz = 0x01; ruecksetz = ruecksetz<<i; ruecksetz = ~ruecksetz; // jetzt alles Einsen, bis auf Stelle i mit 0 bitfeld &= ruecksetz; // log. UND von 0 auf Position i Innerhalb setz bzw. ruecksetz können mehrere Bits gleichzeitig gesetzt werden. 13 Überprüfen von Bitpositionen (1) Am Beispiel von 8-Bit Feldern (unsigned char) Wenn geprüft werden soll, ob eine bestimmte Bitposition i (0<=i<=7) auf 1 steht: unsigned char ergebnis; unsigned char maske = 0x01; maske=maske<<i; ergebnis = bitfeld & maske; if (ergebnis!=0) { /*Position ist gesetzt*/ } else { /*Position ist nicht gesetzt*/ } // Verschieben der Eins auf Position i // log. UND von 1 auf Position i Der Begriff maske kommt von Maskieren. 14
Überprüfen von Bitpositionen (2) Am Beispiel von 8-Bit Feldern (unsigned char) Wenn geprüft werden soll, ob mehrere Bitpositionen auf 1 stehen, ist ein unterschiedliches Vorgehen nötig, je nachdem wie die Fragerstellung lautet: steht eine von mehreren gefragten Bitpositionen auf 1? stehen alle der gefragten Bitpositionen auf 1? unsigned char maske, gesamtmaske=0x00, ergebnis; for (i=0;i<n_bitpos;i++) { maske = 0x01; maske = maske<< bitpos[i]; // Verschieben der Eins auf Position i gesamtmaske = maske; } ergebnis = bitfeld & gesamtmaske; // log. UND if (ergebnis!=0) { /* mindestens eine Position ist gesetzt*/ } else (if (ergebnis == gesamtmaske) { /* alle Positionen sind gesetzt*/ } else { /* keine Position gesetzt */} 15 Bitfelder (1) Die Sprache C stellt als Hilfskonstrukt zur Vermeidung von Bitoperationen auf dem Niveau Maskieren, Packen und Entpacken, sogenannte Bitfelder zur Verfügung. Diese stellen aber keine Reihungen im üblichen Sinne dar, sondern sind eine Spezialvariante eines Verbundes (Struktur). Man kann in einer Struktur-Deklaration unsigned-int-elemente benennen und deren Anzahl von Bits angeben. Maximal können 16 Bits angegeben werden! Beispiel: struct beispiel { unsigned a : 4; a,b und c sind Elementnamen unsigned b : 5; 4,5,2 stellen die Anzahl der Bits unsigned c : 2; dar } 16
Bitfelder (2) Mit den Strukturelementen kann jetzt so gearbeitet werden, als wären es normale unsigned-variable, d.h. es kann z.b. eine Zuweisung erfolgen. beispiel.a=12; Beachte: Werden Werte zugewiesen, die größer sind, als mit der entsprechenden Bitzahl darstellbar ist, werden die werthöchsten Bits einfach abgeschnitten. 17