C Language
Bitfelder
Suche…
Einführung
Die meisten Variablen in C haben eine Größe, die eine ganzzahlige Anzahl von Bytes ist. Bitfelder sind Teil einer Struktur, die nicht unbedingt eine ganzzahlige Anzahl von Bytes belegt. Sie können eine beliebige Anzahl von Bits. Mehrere Bitfelder können in eine einzige Speichereinheit gepackt werden. Sie sind Teil von Standard C, aber es gibt viele Aspekte, die durch die Implementierung definiert werden. Sie sind einer der am wenigsten tragbaren Teile von C.
Syntax
- Typenbezeichnerkennung: Größe;
Parameter
Parameter | Beschreibung |
---|---|
Typbezeichner | signed , unsigned , int oder _Bool |
Kennung | Der Name für dieses Feld in der Struktur |
Größe | Die Anzahl der Bits, die für dieses Feld verwendet werden sollen |
Bemerkungen
Die einzigen portablen Typen für Bitfelder sind signed
, unsigned
oder _Bool
. Die Ebene int
- Typ kann verwendet werden, aber der Standard sagt (§6.7.2¶5) ... für Bitfelder, es ist die Implementierung definiert , ob der Bezeichner int
den gleichen Typ wie bezeichnet signed int
oder vom gleichen Typ wie unsigned int
.
Andere ganzzahlige Typen können von einer bestimmten Implementierung zugelassen werden, deren Verwendung ist jedoch nicht portierbar.
Bitfelder
Ein einfaches Bitfeld kann verwendet werden, um Dinge zu beschreiben, an denen eine bestimmte Anzahl von Bits beteiligt ist.
struct encoderPosition {
unsigned int encoderCounts : 23;
unsigned int encoderTurns : 4;
unsigned int _reserved : 5;
};
In diesem Beispiel betrachten wir einen Encoder mit 23 Bit einfacher Genauigkeit und 4 Bit, um Multiturn zu beschreiben. Bitfelder werden häufig verwendet, wenn eine Schnittstelle zu Hardware hergestellt wird, die Daten ausgibt, die einer bestimmten Anzahl von Bits zugeordnet sind. Ein anderes Beispiel könnte die Kommunikation mit einem FPGA sein, bei dem der FPGA Daten in 32-Bit-Abschnitten in Ihren Speicher schreibt, um Hardware-Lesevorgänge zu ermöglichen:
struct FPGAInfo {
union {
struct bits {
unsigned int bulb1On : 1;
unsigned int bulb2On : 1;
unsigned int bulb1Off : 1;
unsigned int bulb2Off : 1;
unsigned int jetOn : 1;
};
unsigned int data;
};
};
Für dieses Beispiel haben wir ein häufig verwendetes Konstrukt gezeigt, um auf die Daten in ihren einzelnen Bits zugreifen zu können oder das Datenpaket als Ganzes zu schreiben (emulieren, was der FPGA tun könnte). Wir könnten dann auf die Bits wie folgt zugreifen:
FPGAInfo fInfo;
fInfo.data = 0xFF34F;
if (fInfo.bits.bulb1On) {
printf("Bulb 1 is on\n");
}
Dies gilt, aber gemäß C99-Standard 6.7.2.1, Punkt 10:
Die Reihenfolge der Zuordnung von Bitfeldern innerhalb einer Einheit (von hoher bis niedriger oder von niedriger nach hoher Ordnung) ist implementierungsdefiniert.
Wenn Sie Bitfelder auf diese Weise definieren, müssen Sie sich der Endianität bewusst sein. Daher kann es erforderlich sein, eine Präprozessor-Direktive zu verwenden, um die Endianness der Maschine zu überprüfen. Ein Beispiel dafür folgt:
typedef union {
struct bits {
#if defined(WIN32) || defined(LITTLE_ENDIAN)
uint8_t commFailure :1;
uint8_t hardwareFailure :1;
uint8_t _reserved :6;
#else
uint8_t _reserved :6;
uint8_t hardwareFailure :1;
uint8_t commFailure :1;
#endif
};
uint8_t data;
} hardwareStatus;
Verwenden von Bitfeldern als kleine Ganzzahlen
#include <stdio.h>
int main(void)
{
/* define a small bit-field that can hold values from 0 .. 7 */
struct
{
unsigned int uint3: 3;
} small;
/* extract the right 3 bits from a value */
unsigned int value = 255 - 2; /* Binary 11111101 */
small.uint3 = value; /* Binary 101 */
printf("%d", small.uint3);
/* This is in effect an infinite loop */
for (small.uint3 = 0; small.uint3 < 8; small.uint3++)
{
printf("%d\n", small.uint3);
}
return 0;
}
Bitfeldausrichtung
Bitfelder geben die Möglichkeit, Strukturfelder zu deklarieren, die kleiner als die Zeichenbreite sind. Bitfelder werden mit einer Byte- oder Wortebenenmaske implementiert. Das folgende Beispiel ergibt eine Struktur von 8 Bytes.
struct C
{
short s; /* 2 bytes */
char c; /* 1 byte */
int bit1 : 1; /* 1 bit */
int nib : 4; /* 4 bits padded up to boundary of 8 bits. Thus 3 bits are padded */
int sept : 7; /* 7 Bits septet, padded up to boundary of 32 bits. */
};
In den Kommentaren wird ein mögliches Layout beschrieben. Da der Standard besagt, dass die Ausrichtung der adressierbaren Speichereinheit nicht angegeben ist , sind auch andere Layouts möglich.
Ein unbenanntes Bitfeld kann eine beliebige Größe haben, sie kann jedoch nicht initialisiert oder referenziert werden.
Ein Bitfeld mit der Breite Null kann nicht benannt werden und richtet das nächste Feld an der durch den Datentyp des Bitfelds definierten Grenze aus. Dies wird durch Auffüllen von Bits zwischen den Bitfeldern erreicht.
Die Größe der Struktur 'A' beträgt 1 Byte.
struct A
{
unsigned char c1 : 3;
unsigned char c2 : 4;
unsigned char c3 : 1;
};
In der Struktur B überspringt das erste unbenannte Bitfeld 2 Bits; Das Bitfeld mit der Breite Null nach c2
bewirkt, dass c3
an der char-Grenze beginnt (also werden 3 Bits zwischen c2
und c3
übersprungen. Nach c4
gibt es 3 Auffüllbits. Daher beträgt die Größe der Struktur 2 Byte.
struct B
{
unsigned char c1 : 1;
unsigned char : 2; /* Skips 2 bits in the layout */
unsigned char c2 : 2;
unsigned char : 0; /* Causes padding up to next container boundary */
unsigned char c3 : 4;
unsigned char c4 : 1;
};
Wann sind Bitfelder nützlich?
Ein Bitfeld wird verwendet, um viele Variablen in einem Objekt zusammenzufassen, ähnlich einer Struktur. Dies ermöglicht einen geringeren Speicherbedarf und ist besonders in einer eingebetteten Umgebung nützlich.
e.g. consider the following variables having the ranges as given below.
a --> range 0 - 3
b --> range 0 - 1
c --> range 0 - 7
d --> range 0 - 1
e --> range 0 - 1
Wenn wir diese Variablen separat deklarieren, muss jede mindestens eine 8-Bit-Ganzzahl sein und der gesamte erforderliche Speicherplatz beträgt 5 Byte. Darüber hinaus verwenden die Variablen nicht den gesamten Bereich einer vorzeichenlosen 8-Bit-Ganzzahl (0-255). Hier können wir Bitfelder verwenden.
typedef struct {
unsigned int a:2;
unsigned int b:1;
unsigned int c:3;
unsigned int d:1;
unsigned int e:1;
} bit_a;
Auf die Bitfelder in der Struktur wird wie auf jede andere Struktur zugegriffen. Der Programmierer muss darauf achten, dass die Variablen in den Bereich geschrieben werden. Wenn außerhalb des Bereichs ist das Verhalten undefiniert.
int main(void)
{
bit_a bita_var;
bita_var.a = 2; // to write into element a
printf ("%d",bita_var.a); // to read from element a.
return 0;
}
Oft möchte der Programmierer die Menge der Bitfelder auf Null setzen. Dies kann Element für Element erfolgen, aber es gibt eine zweite Methode. Erstellen Sie einfach eine Vereinigung der obigen Struktur mit einem vorzeichenlosen Typ, der größer oder gleich der Größe der Struktur ist. Dann kann der gesamte Satz von Bitfeldern auf Null gesetzt werden, indem diese vorzeichenlose Ganzzahl auf Null gesetzt wird.
typedef union {
struct {
unsigned int a:2;
unsigned int b:1;
unsigned int c:3;
unsigned int d:1;
unsigned int e:1;
};
uint8_t data;
} union_bit;
Die Verwendung ist wie folgt
int main(void)
{
union_bit un_bit;
un_bit.data = 0x00; // clear the whole bit-field
un_bit.a = 2; // write into element a
printf ("%d",un_bit.a); // read from element a.
return 0;
}
Zusammenfassend werden Bitfelder im Allgemeinen in Situationen mit eingeschränktem Speicher verwendet, in denen viele Variablen vorhanden sind, die begrenzte Bereiche annehmen können.
Don'ts für Bitfelder
- Bitfelder, Zeiger auf Bitfelder und Funktionen, die Bitfelder zurückgeben, sind nicht zulässig.
- Der Adressoperator (&) kann nicht auf Bitfeldmitglieder angewendet werden.
- Der Datentyp eines Bitfelds muss breit genug sein, um die Größe des Felds zu enthalten.
- Der Operator
sizeof()
kann nicht auf ein Bitfeld angewendet werden. - Es gibt keine Möglichkeit, eine
typedef
für ein Bitfeld isoliert zu erstellen (obwohl Sie sicherlich einetypedef
für eine Struktur mit Bitfeldern erstellen können).
typedef struct mybitfield
{
unsigned char c1 : 20; /* incorrect, see point 3 */
unsigned char c2 : 4; /* correct */
unsigned char c3 : 1;
unsigned int x[10]: 5; /* incorrect, see point 1 */
} A;
int SomeFunction(void)
{
// Somewhere in the code
A a = { … };
printf("Address of a.c2 is %p\n", &a.c2); /* incorrect, see point 2 */
printf("Size of a.c2 is %zu\n", sizeof(a.c2)); /* incorrect, see point 4 */
}