14.1. Bit-Manipulationen
C verfügt über sechs Operatoren zur Bit-Manipulation. Als Operanden akzepieren sie nur ganzzahlige Werte (darunter auch char). Die folgende Tabelle zeigt die Operatoren zur bitweisen Manipulation:
Operator | Bedeutung |
---|---|
& | UND-Verknüpfung |
| | ODER-Verknüpfung |
^ | XOR-Verknüpfung |
~ | Bit-Komplement (unär) |
<< | Bit-Verschiebung nach links |
>> | Bit-Verschiebung nach rechts |
Die ersten drei dieser Operatoren sind binäre Operatoren, d.h., sie brauchen zwei Operanden. Sie verknüpfen die beiden Operanden Bit für Bit mit einem und, oder bzw. ausschließenden oder. Für die bitweise und- bzw. oder-Verknüpfung gelten die gleichen Regeln und damit auch die gleichen Wahlheitstafeln wie für die entsprechenden logischen Operatoren (siehe 6.4). Bei der xor-Verknüpfung, für die es bei den logischen Operatoren keine Entsprechung gibt, ergeben sich die Wahrheitswerte wahr und falsch unter folgenden Bedingungen:
x | y | x ^ y |
wahr | wahr | falsch |
wahr | falsch | wahr |
falsch | wahr | wahr |
falsch | falsch | falsch |
Folgende Tabelle zeigt eine Reihe von Beispiel-Ausdrücken, ihre binäre und ihre dezimale Repräsentation:
Ausdruck | binäre Darstellung | dezimaler Wert |
---|---|---|
x | 00010011 | 19 |
y | 01111110 | 126 |
x & y | 00010010 | 18 |
x | y | 01111111 | 127 |
x ^ y | 01101101 | 109 |
Wie leicht zu ersehen ist, verknüpfen &, | und ^ die jeweils untereinander befindlichen Bits der Operanden x und y. Wenn bei der &-Verknüpfung beide Bits den Wert 1 haben, dann erhält das Resultat an dieser Stelle ebenfalls den Wert 1, sonst 0. Diese Tatsache kann man dazu ausnutzen, einzelne Bits eines Werts gezielt auf 0 zu setzen. Dazu bedarf es nur eines zweiten Wertes (einer sogenannten bit mask) , dessen binäre Repräsentation aus lauter 1 besteht, ausgenommen an den Stellen, die beim ersten Wert auf 0 gesetzt werden sollen.
Da die ASCII-Werte von Kleinbuchstaben um den Wert 32 größer sind als die der entsprechenden Großbuchstaben, könnten Sie beispielsweise unter Verwendung von & eine sehr schnelle (aber leider schlecht portierbare) Version von tolower() erstellen. Sie bräuchten dafür nur das 5. Bit des Kleinbuchstabens auf 0 setzen. Folgendes Makro zeigt dieses Verfahren:
#include <stdio.h>
/* Der Wert 223 ergibt sich, wenn alle Bits
* eines char-Wertes auf 1 gesetzt werden,
* ausgenommen das 5., also aus 255 - 32.
*/
#define TO_UPPER(c)\
( (c) >= 'a' && (c) <= 'z' ?\
((c) & 223) : (c) )
int main(void){
/* Im Fall von 'y' ergibt sich folgende
* Verknüpfung:
* 11111001 binärer Wert von 'y'
* &
* 11011111 binärer Wert von 223
* --------
* 11011001 binärer Wert von 'Y'
*/
putchar( TO_UPPER('y'));
return 0;
}
Der unäre Operator ~ setzt die Werte aller Bit auf ihren komplementären Wert, also 0 auf 1 und 1 auf 0. Hat z.B. die Variable cChar den Wert 12, binär
00001100
dann ergibt ~cChar
11110011
was dem dezimalen Wert von 243 entspricht.
Wie ihr Name schon vermuten läßt, verschieben die beiden Operatoren zur Bit-Verschiebung (Shift-Operatoren) die Bit eines Wertes um die angebene Zahl nach links oder rechts und füllen die freiwerdenden Stellen mit 0 auf. Bei cChar mit dem Wert 12, binär
00001100
hätte das Verschieben um 2 Stellen nach links, also cChar << 2, folgendes Resultat:
00110000
Dies entspricht dezimal dem Wert 48 (Verschieben von zwei Stellen nach links entspricht mithin einer Multiplikation mit 4).
Folgendes Beispiel benutzt den Operator zur Bitverschiebung nach links, um für eine Dezimalzahl die Binärdarstellung zu errechnen:
/* dec2bin
* errechnet Binärdarstellung für
* beliebige Dezimalzahl
*/
#include <stdio.h>
/* Funktionsprototyp */
void dec2bin(long int lConv);
int main( void ){
long lDezZahl;
puts("Bitte Dezimal-Zahl eingeben: ");
scanf("%ld", &lDezZahl);
dec2bin(lDezZahl);
return 0;
}
/*****************************************
*
* dec2bin()
* Umwandlungsroutine von dezimal auf binär
*
*****************************************/
void dec2bin(long int lConv){
int iCount;
/* (sizeof(lConv) * 8) errechnet, aus
* wie vielen Bit lConv besteht
*/
for( iCount=1;
iCount <= (sizeof(lConv) * 8);
iCount++ ){
/* Bei der Darstellung von Zahlen durch das
* sogenannte Zweier-Komplement (3.3.4.) ist
* das höchstwertige Bit das Vorzeichen-Bit.
* Ist es 1, so ist die Zahl nagativ, sonst
* positiv. Diese Routine prüft nach jedem
* Verschiebevorgang nach links, ob das
* äußerst linke Bit 1 oder 0 ist und gibt
* den jeweiligen Wert mit putchar() aus
*/
lConv < 0 ? putchar('1') : putchar('0');
/* auch für die Shift-Operatoren existieren
* zusammengesetzte Zuweisungsoperatoren
* lConv <<= 1 kann anstelle von
* lConv = lConv << 1 verwendet werden.
* Bei jedem Schleifendurchlauf wird um eine
* Stelle nach links verschoben
*/
lConv <<= 1;
/* zur besseren Lesbarkeit der Ausgabe soll
* nach 8 Stellen jeweils eine Leerstelle
* eingefügt werden
*/
if(iCount % 8 == 0)
putchar(' ');
}
putchar('\n');
}