Bitfeld

In der Informationstechnik und Programmierung bezeichnet ein Bitfeld ein vorzeichenloses Integer, in dem einzelne Bits oder Gruppen von Bits aneinandergereiht werden. Es stellt eine Art Verbunddatentyp auf Bit-Ebene dar. Im Gegensatz dazu steht der primitive Datentyp, bei dem der Wert aus allen Stellen gemeinsam gebildet wird.

Eine verbreitete Verwendung ist die, bei der jede einzelne Binärstelle ein Flag repräsentiert. Dabei entspricht jede Stelle einer booleschen Variablen. Es können aber auch mehrere Stellen einen Wert bilden, z. B. können in einem Byte auch zwei Nibbles zusammengefasst sein oder, wie bei IPv4-Adressen, ein 32-Bit-Datenwort in (beispielsweise) 24-Bit-Netzwerk- und 8-Bit-Hostteil aufgeteilt sein.

Es findet sich auch die Sprechweise „Bitvektor“, ohne damit immer auszudrücken, dass das einzelne Bit durch Indizierung ansprechbar ist. Indizierung von Bits wird im Artikel Bitkette behandelt.

Verwendung

Bitfelder werden typischerweise bei der hardwarenahen oder der Systemprogrammierung eingesetzt. Hier dienen sie häufig dazu, bei Peripheriegeräten ein bestimmtes Verhalten einzustellen. Das liegt daran, dass bspw. das Aktivieren einer bestimmten Funktionalität in Hardware leicht über das Setzen einer einzigen Datenleitung, eines Bits, signalisiert werden kann. Um einfacher mit der Software oder anderen Geräten zu kommunizieren oder weil bspw. I/O-Ports nur in begrenzter Menge vorhanden sind, werden dann mehrere solche Leitungen zu einem Datenwort zusammengefasst, dem Bitfeld.

Daneben werden Bitfelder aber auch bei der Anwendungsprogrammierung verwendet, wenn eine Vielzahl an Parametern übergeben werden soll. Hier kann die Lesbarkeit des Quellcodes verbessert werden, wenn statt einer langen Parameterliste, bei der jeder Parameter explizit angegeben werden muss, nur ein Bitfeld mit den gewünschten Flags zusammengestellt wird.

Bitfelder zu verwenden, um Speicherplatz einzusparen, ist in der Anwendungsprogrammierung im Allgemeinen heutzutage nicht mehr so sinnvoll. Ausnahmen bestehen z. B. bei eingebetteten Systemen, wenn Speicher eine extrem knappe Ressource ist, oder wenn die Bits in außerordentlich großer Zahl benötigt werden wie beim Sieb des Eratosthenes.[1] Für boolesche Variablen wird meist ein ganzes Byte oder Wort verwendet. Auch ist der Zugriff auf einzelne Bits eines Datenworts oft ineffizienter als der auf das ganze Datenwort.

Beispiel in OpenGL

In OpenGL wird bspw. die Funktion glClear definiert, die einen oder mehrere von vier Grafikpuffern löscht. Die Entwickler hätten nun vier Parameter definieren können, die jeweils angeben, ob der Grafikpuffer gelöscht werden soll oder nicht. Der Funktionsaufruf würde folgendermaßen aussehen:

 void glClear(1, 1, 0, 0);

Dies ist aber weder effizient, da vier Variablen übergeben werden müssen, noch sehr leserlich. Daher wurde in der gl.h für jeden Puffer ein sogenanntes benanntes Flag definiert:

 #define GL_DEPTH_BUFFER_BIT               0x00000100
 #define GL_ACCUM_BUFFER_BIT               0x00000200
 #define GL_STENCIL_BUFFER_BIT             0x00000400
 #define GL_COLOR_BUFFER_BIT               0x00004000

Und für die Funktion wurde nur ein einzelner Parameter definiert:

 void glClear(GLbitfield mask); // GLbitfield ist ein typedef auf unsigned int

Der Funktionsaufruf sieht nun folgendermaßen aus:

 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

Verwendung in C und C#

In der Programmiersprache C ist es möglich, in Datenstrukturen Bitfelder zu definieren und damit kleinere Datentypen kompakt zu speichern. Solange die Daten ausschließlich über die Feldnamen adressiert werden, wird der Quellcode dadurch nicht abhängig vom Compiler oder vom Prozessor. Für Zugriffe auf Teile von Registern oder deserialisierte Daten gilt das nicht.

 struct struct-type-name {
     type [name1] : length;
     type [name2] : length;
     //…
     type [nameN] : length;
 } variable-list;

In der Programmiersprache C# ist es mittels der Flags Attributes möglich, eine Enumeration als Bitfeld zu deklarieren.[2] Außerdem stellt das .NET Framework die Datenstruktur BitArray zur Verfügung, die einzelne Bits kompakt speichert und boolesche Operationen ermöglicht.

Zugriff

Der Zugriff auf ein einzelnes Bit, sowohl in testender als auch in setzender Weise, wird von der Hardware (je nach Befehlssatzarchitektur) ähnlich gut unterstützt wie der Zugriff auf ein Byte oder Wort – es genügt bei vielen Maschinen ein einziger Befehl. Die Unterstützung durch die Compiler ist aber (je nachdem, wie sie optimieren) häufig ähnlich wie beim Zugriff auf mehrere Bits, bei dem die Bitgruppe vor dem Vergleich oder der Manipulation durch eine Bitmaske aus dem Speicherwort „herauspräpariert“ werden muss. Als Bitmasken werden Bitfelder bezeichnet, deren Stellen selbst nicht die zu verarbeitende Information repräsentieren, sondern die verwendet werden, um die das Bitfeld umfassenden Bits zu markieren.

Ein in der Netzwerktechnik verbreitetes Beispiel für eine Bitmaske ist die Netzmaske. Sie bestimmt, welche der führenden Bits einer IP-Adresse das Netzpräfix sind, d. h., welcher Adressbereich als internes und welcher als externes Netz beim Routen betrachtet wird.

Bit auslesen

Um ein oder mehrere Bits aus einem Bitfeld auszulesen, wird Letzteres mit einer Bitmaske über die bitweise Und-Operation verknüpft. In der Bitmaske steht an allen Stellen, die nicht betrachtet werden sollen, eine „0“. Diese Stellen werden durch die UND-Verknüpfung ausgeblendet, d. h. auf Null gesetzt. Die Stellen, die betrachtet oder ausgelesen werden sollen, enthalten eine „1“ in der Bitmaske und werden daher unverändert aus dem Bitfeld ins Ergebnis übernommen.

Beispiel

1-Bit:

    01001011 Bitfeld
AND 00001000 Bitmaske
-------------
=   00001000 Ergebnis

0-Bit:

    01001011 Bitfeld
AND 00000100 Bitmaske
-------------
=   00000000 Ergebnis

Das Ergebnis ist Null, wenn die Stellen der Bitmaske im Bitfeld ebenfalls Null sind. Ist mindestens eines davon auf „1“ gesetzt, sind die entsprechenden Bits auch im Ergebnis „1“, d. h., das Ergebnis ist ungleich Null.

Da Werte von Null in den meisten Programmiersprachen zu einem logischen false und Werte ungleich Null zu einem logisch true ausgewertet werden, kann nun das Ergebnis leicht mit einer bedingten Verzweigung daraufhin überprüft werden, ob eines der Bits der Bitmaske im Feld gesetzt war:

if (Ergebnis)
   // Bit gesetzt
else
   // Bit nicht gesetzt

Bits setzen

Sollen Bits im Bitfeld einen bestimmten Wert annehmen, müssen die entsprechenden Stellen der Bitmaske auf „1“ und alle anderen auf „0“ gesetzt werden. Um die Stellen auf „1“ zu setzen, muss das Bitfeld mit der Bitmaske bitweise oder-verknüpft werden. Umgekehrt erhält man an den gewünschten Stellen eine „0“, wenn man Feld und die inverse Maske über eine Und-Operation verknüpft.

Beispiel

Setzen von Bits auf „1“:

    01001011 Bitfeld
OR  00000100 Bitmaske
-------------
=   01001111 Ergebnis

Setzen von Bits auf „0“:

NOT 00001000 Bitmaske
-------------
=   11110111 invertierte Bitmaske
AND 01001011 Bitfeld
-------------
=   01000011 Ergebnis

Bits umschalten

Sollen einzelne Bits umgeschaltet (englisch to toggle) bzw. invertiert werden, setzt man die entsprechenden Stellen in der Bitmaske auf „1“ und alle anderen auf „0“ und verknüpft Bitfeld und Bitmaske über ein exklusives Oder.

    01001011 Information
XOR 00000110 Bitmaske
-------------
=   01001101 Ergebnis

Bitmasken zusammenfassen

Bitmasken können zusammengefasst werden, um z. B. in einer Operation mehrere Bits auf einmal zu überprüfen oder zu setzen. Dazu addiert man lediglich die einzelnen Bitmasken oder verknüpft sie über ein bitweises Oder.

    00000001 Bitmaske1
OR  00000010 Bitmaske2
-------------
    00000011 zusammengefasste Bitmaske

Siehe auch

Einzelnachweise

  1. Die Verwendung von Bitfeldern beliebiger Ausdehnung und mit einer Indexvariablen wird im Artikel Bitkette ausführlicher behandelt.
  2. FlagsAttribute Klasse. In: msdn.microsoft.com. Abgerufen am 27. Januar 2024.
This article is issued from Wikipedia. The text is licensed under Creative Commons - Attribution - Sharealike. Additional terms may apply for the media files.