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');
}