C Language
Bit-fält
Sök…
Introduktion
De flesta variabler i C har en storlek som är ett integrerat antal byte. Bitfält är en del av en struktur som inte nödvändigtvis upptar ett integrerat antal byte; de kan valfritt antal bitar. Flera bitfält kan paketeras i en enda lagringsenhet. De är en del av standard C, men det finns många aspekter som är implementeringsdefinierade. De är en av de minst bärbara delarna av C.
Syntax
- typspecifikationsidentifierare: storlek;
parametrar
Parameter | Beskrivning |
---|---|
typ-specificerare | signed , unsigned , int eller _Bool |
identifierare | Namnet på detta fält i strukturen |
storlek | Antalet bitar som ska användas för detta fält |
Anmärkningar
De enda bärbara typerna för bitfält är signed
, unsigned
eller _Bool
. Den vanliga int
typen kan användas, men standarden säger (§6.7.2¶5) ... för bitfält är det implementeringsdefinerat om specifieraren int
anger samma typ som signed int
eller samma typ som unsigned int
.
Andra heltalstyper kan tillåtas av en specifik implementering, men att använda dem är inte portabelt.
Bit-fält
Ett enkelt bitfält kan användas för att beskriva saker som kan ha ett specifikt antal bitar involverade.
struct encoderPosition {
unsigned int encoderCounts : 23;
unsigned int encoderTurns : 4;
unsigned int _reserved : 5;
};
I det här exemplet överväger vi en kodare med 23 bitar med enstaka precision och 4 bitar för att beskriva flervarv. Bitfält används ofta vid gränssnitt till hårdvara som matar ut data associerade med specifikt antal bitar. Ett annat exempel kan vara kommunikation med en FPGA, där FPGA skriver data till ditt minne i 32-bitarsavsnitt som möjliggör hårdvaruläsning:
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 det här exemplet har vi visat en vanligt använt konstruktion för att kunna få åtkomst till data i dess enskilda bitar, eller för att skriva datapaketet som helhet (efterliknar vad FPGA kan göra). Vi kunde då komma åt bitarna så här:
FPGAInfo fInfo;
fInfo.data = 0xFF34F;
if (fInfo.bits.bulb1On) {
printf("Bulb 1 is on\n");
}
Detta är giltigt, men enligt C99-standarden 6.7.2.1, punkt 10:
Fördelningen av bitfält inom en enhet (hög ordning till låg ordning eller låg ordning till hög ordning) är implementeringsdefinierad.
Du måste vara medveten om endianness när du definierar bitfält på detta sätt. Som sådan kan det vara nödvändigt att använda ett förbehandlingsdirektiv för att kontrollera om maskinens slutlighet är. Ett exempel på detta följer:
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;
Använda bitfält som små heltal
#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;
}
Bitfältinriktning
Bitfält ger möjlighet att deklarera strukturfält som är mindre än teckenbredden. Bitfält implementeras med byte-nivå eller word-level mask. Följande exempel resulterar i en struktur på 8 byte.
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. */
};
Kommentarerna beskriver en möjlig layout, men eftersom standarden säger att anpassningen av den adresserbara lagringsenheten inte är specificerad är andra layouter också möjliga.
Ett namngivet bitfält kan ha valfri storlek, men de kan inte initialiseras eller refereras till.
Ett bitfält med nollbredd kan inte ges ett namn och anpassar nästa fält till gränsen definierad av datatypen för bitfältet. Detta uppnås genom att fylla bitar mellan bitfälten.
Storleken på strukturen 'A' är 1 byte.
struct A
{
unsigned char c1 : 3;
unsigned char c2 : 4;
unsigned char c3 : 1;
};
I struktur B hoppar det första namngivna bitfältet 2 bitar; bitfältet med nollbredd efter c2
får c3
att börja från kolgränsen (så 3 bitar hoppas över mellan c2
och c3
. Det finns 3 stoppningsbitar efter c4
. Således är strukturens storlek 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;
};
När är bitfält användbara?
Ett bitfält används för att sammanfoga många variabler till ett objekt, liknande en struktur. Detta möjliggör minskad minnesanvändning och är särskilt användbar i en inbäddad miljö.
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
Om vi deklarerar dessa variabler separat måste var och en vara minst ett 8-bitars heltal och det totala utrymmet som krävs är 5 byte. Dessutom kommer variablerna inte att använda hela intervallet för ett 8-bitars osignerat heltal (0-255). Här kan vi använda bitfält.
typedef struct {
unsigned int a:2;
unsigned int b:1;
unsigned int c:3;
unsigned int d:1;
unsigned int e:1;
} bit_a;
Bitfälten i strukturen har åtkomst till samma som alla andra strukturer. Programmeraren måste se till att variablerna skrivs inom räckvidd. Om det är utanför räckvidden är beteendet odefinierat.
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;
}
Ofta vill programmeraren nollställa uppsättningen bitfält. Detta kan göras element för element, men det finns en andra metod. Skapa helt enkelt en sammansättning av strukturen ovan med en osignerad typ som är större än eller lika stor som strukturen. Sedan kan hela uppsättningen av bitfält nollställas genom att nollställa detta osignerade heltal.
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;
Användningen är som följer
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;
}
Sammanfattningsvis används bitfält ofta i minnesbegränsade situationer där du har många variabler som kan ta begränsade intervall.
Gör inte för bitfält
- Matriser av bitfält, pekare på bitfält och funktioner som returnerar bitfält är inte tillåtna.
- Adressoperatören (&) kan inte tillämpas på bitfältmedlemmar.
- Datatypen för ett bitfält måste vara tillräckligt bred för att innehålla fältets storlek.
-
sizeof()
kan inte tillämpas på ett bitfält. - Det finns inget sätt att skapa en
typedef
för etttypedef
isolerat (även om du säkert kan skapa entypedef
för en struktur som innehållertypedef
).
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 */
}