C-Basic: Bitmanipulation setzen, löschen und wechseln von Bits in C/C++

Dieser Beitrag gibt einen Überblick über einige Tipps und praktische Konzepte zur Bit-Manipulation und zeigt, wie sie in der C / embedded-C Programmierung eingesetzt werden kann. Insbesonderer für Lese- und Schreibzugriffe auf Register bei der Programmierung von Mikrocontrollern ist die Bearbeitung einzelner Bits ein Grundwerkzeug.

Die Grundlage

In einem Binärsystem existieren nur die Werte 0 und 1 und das n-te Bit (einschließlich 0) entspricht einem Wert von 2^n

Definitionen:

  • Ein gelöschtes oder “low, false” Bit entspricht einer 0
  • Ein gesetztes oder “high, true” Bit entspricht eine 1
  • Das ganz linke Bit hat den höchsten Wert und wird daher als das höchstwertigste Bit englisch (Most-Significant Bit, MSB) bezeichnet.
  • Das ganz rechte Bit hat den niedrigsten Wert und wird daher als niederwertigstes Bit englisch  (Least-Significant Bit, LSB) bezeichnet.

Addition

Für die Addition gelten die folgenden Regeln:

  • 0 + 0 ergibt: 0
  • 0 + 1 ergibt: 1
  • 1 + 1 ergibt: 0 mit einer übertragenen 1 auf die nächsthöhere Stelle

Die Addition von 1011_2 (11_{10}) und 1010_2 (10_{10}) ergibt somit 00010111_2 (21_{10})

 1011
     +
 1010
_____
10101

Subtraktion

Für die Subtraktion gelten folgende Regeln:

  • 0 – 0 ergibt: 0
  • 1 – 0 ergibt: 1
  • 1 – 1 ergibt: 0
  • 10 – 1 ergibt: 01

Die Subtraktion von 1011_2 (11_{10}) und 1010_2 (10_{10}) ergibt somit 0001_2 (1_{10})

 1011
     -
 1010
_____
 0001

Multiplikation

Bei der Multiplikation muss wie folgt bitweise vorgegangen werden.  Jedes Bit aus A mit einem Wert wird mit jedem Bit aus  B und dessen Wert “multiplizieren”, indem B um die Position des Wertes von B verschoben wird. Im Anschluss an die Verschiebung werden die beiden addiert. Um beispielsweise A = 3_ {10} (0011_2) und B = 9_{10} (1001_2) zu multiplizieren, wird B um jedes Bit in A verschoben. Für dieses Beispiel ergeben sich somit zwei Verschiebungen von B, welche dann addiert werden:

  1. Verschiebung um 1 nach links für 0010_2 von A ergibt B1 mit 10010_2
  2. Verschiebung um 0 nach links für 0001_2 von A ergibt B2 mit 1001_2
  3. Addition von B1 und B2 ergibt 11011_2 (27_{10})

Die Multiplikation von 1011_2 (11_{10}) und 1010_2 (10_{10}) ergibt somit 01101110_2 (110_{10})

   1011
       x
   1010
  _____
1101110

Komplement

Die Komplement-Bildung auch als Drehen, Negieren oder einfaches Umkehren bezeichnet, ändert alle 1er auf 0er und umgekehrt.  Dieser Vorgang wird in C mit dem Operator ~ durchgeführt. Beispiele für die Komplementbildung:

  • ~1 ergibt: 0
  • ~0 ergibt: 1
  • ~01101011 ergibt: 10010100

Der NICHT-Operator ! entspricht nicht dem Kompliment da dieser nicht bitweise sondern den gesamten Wert manipuliert!

Beispiele:

  • !01101011    ergibt: 00000000
  • !00000000 ergibt: 00000001

ODER-Verknüpfung (OR)

Die ODER-Verknüpfung ist die erste reine Binärverknüpfung (man kann dies nicht mit Dezimalzahlen vollziehen). In C wird sie mit dem Operator | durchgeführt. Um zwei Binärwerte zu “ODERn”, gelten folgende Regeln:

  • 0 | 0 ergibt: 0
  • 0 | 1 ergibt: 1
  • 1 | 1 ergibt: 1
 1011
     |
 1010
_____
 1011

UND-Verknüpfung (AND)

Die UND-Verknüpfung wird in C mit dem Operator & durchgeführt. Um zwei Binärwerte zu “UNDn”, gelten folgende Regeln:

  • 0 | 0 ergibt: 0
  • 0 | 1 ergibt: 0
  • 1 | 1 ergibt: 1
 1011
     &
 1010
_____
 1010

Exklusiv-ODER-Verknüpfung (XOR)

Die Exklusiv-ODER-Verknüpfung wird in C mit dem Operator ^ durchgeführt. Um zwei Binärwerte zu “XORn”, gelten folgende Regeln:

  • 0 | 0 ergibt: 0
  • 0 | 1 ergibt: 1
  • 1 | 1 ergibt: 0
 1011
     ^
 1010
_____
 0001

Shifting (Bits verschieben)

Durch Verschieben, nach links mit << und nach rechts mit >>, wird ein Binärwert um eine bestimmte Anzahl von Bits nach links oder rechts verschoben. Die durch die Verschiebung freiwerdenden Stellen werden mit 0 gefüllt.

Beispiele:

  • 01101011 << 2 ergibt: 10101100
  • 01101011 >> 4 ergibt: 00000110

Bitmanipulation

In den folgenden Abschnitten wird gezeigt, wie man in einer Bitfolge gezielt einzelne Bits setzt, löscht oder umkehrt.

Setzen eines bestimmten Bits (set)

Das setzen von Bits beruht auf der Kombination aus einem Shift und einer ODER-Verknüpfung oder einer Bitmaske und einer ODER-Verknüpfung.

Um das nte Bit in der Variable A zu setzen (von 0 an gezählt) wird folgende Formel verwendet:

  • A |= (1 << n)

Beispiele:

  • 0100 |= (1 << 0) ergibt: 0100 | 0001 ergibt: 01001
  • 0100 |= (1 << 2) ergibt: 0100 | 0100 ergibt: 0100 (keine Veränderung)

Für das Setzen von Bits bei der Mikrocontroller-programmierung mit C bietet es sich an, das folgende Macro zu verwenden:

#define SET_BIT(byte, bit) ((byte) |= (1UL << (bit)))

Alternativ lässt sich ein Bit auch direkt über eine Maske setzen:

  • A |= Bit-Maske

Beispiel:

  • 1000 |= 0010 ergibt: 1010
  • 1000 |= 1000 ergibt: 1000 (keine Veränderung)

Löschen eines bestimmten Bits (clear)

Das löschen eines Bits kann durch die Kombination aus einem Shift, dem Komplement und der UND-Verknüpfung realisiert werden. Auch hier besteht die Möglichkeit anstelle des Verschiebens eine Bit-Maske zu nutzen.

Um das nte Bit in der Variable A zu löschen (von 0 an gezählt) wird folgende Formel verwendet:

  • A &= ~(1 << n)

Beispiele:

  • 0101 &= ~(1 << 2) ergibt: 0101 & 1011 ergibt: 0001
  • 0010 &= ~(1 << 1) ergibt: 0010 & 1101 ergibt: 0000
  • 0010 &= ~(1 << 0) ergibt: 0010 & 1110 ergibt: 0010(keine Veränderung)

Ebenso ist das Setzen von Bits bei der Mikrocontroller-programmierung mit C über ein Macro möglich:

#define CLEAR_BIT(byte,bit) ((byte) &= ~(1UL << (bit)))

Alternativ lässt sich ein Bit auch direkt über eine Maske löschen:

  • A &= ~(Bit-Maske)

Beispiel:

  • 0101 &= ~0100 ergibt: 0001
  • 1000 &= ~1000 ergibt: 0000

Wechseln eines bestimmten Bits (toggle)

Das Wechseln eines Bits geschieht durch die Kombination der Shift-Funktion und der Exklusiv-ODER-Verknüpfung. Dabei wird das entsprechende Bit von 0 auf 1 oder von 1 auf 0 gesetzt.

Um den Zustand des nte Bits in der Variable A zu wechseln (von 0 an gezählt) wird folgende Formel verwendet:

  • A ^= (1 << n)

Beispiele:

  • 0100 ^= (1 << 2) ergibt: 0100 ^ 0100 ergibt: 0000
  • 0101 ^= (1 << 1) ergibt: 0101 ^ 0010 ergibt: 0111
  • 1101 ^= (1 << 0) ergibt: 1101 ^ 0001 ergibt: 1100

Ebenso ist das Wechseln von Bits bei der Mikrocontroller-programmierung mit C über ein Macro möglich:

#define TOGGLE_BIT(byte,bit) ((byte) ^= (1UL << (bit)))

Alternativ lässt sich ein Bit auch direkt über eine Maske wechseln:

  • A ^= (Bit-Maske)

Beispiel:

  • 0101 ^= 0100 ergibt: 0001
  • 1000 ^= 0100 ergibt: 1100

Aktualisieren eines bestimmten Bits (update)

Um ein spezifisches Bit zu aktualisieren, muss dies zuerst gelöscht werden und dann mit dem zu setzenden Wert ODER-Verknüpft werden.

Um den Zustand des nte Bits in der Variable A zu aktualisieren (von 0 an gezählt) wird folgende Formel verwendet:

  • A &= ~(1 << n)
  • A |= (Wert << n)

Überprüfung eines bestimmten Bits (check)

Die Überprüfung eines speziellen Bits geschieht durch einen Shift und eine UND-Verknüpfung. Wenn das Bit gesetzt ist erhält man eine 1 anderenfalls eine 0.

Um den Zustand des nte Bits in der Variable A zu prüfen (von 0 an gezählt) wird folgende Formel verwendet:

  • A &= (1 << n)

Beispiel:

  • 0101 &= 0100 ergibt: 0100 ergibt: true
  • 0101 &= 1000 ergibt: 0000 ergibt: false

Ebenso ist das Überprüfen von Bits bei der Mikrocontroller-programmierung mit C über ein Macro möglich:

#define IS_SET(byte,bit) (((byte) & (1UL << (bit))) >> (bit))