Zoeken…


Invoering

De meeste variabelen in C hebben een grootte die een geheel aantal bytes is. Bitvelden zijn een onderdeel van een structuur die niet noodzakelijkerwijs een integraal aantal bytes bezetten; ze kunnen elk aantal bits bevatten. Meerdere bitvelden kunnen worden verpakt in een enkele opslageenheid. Ze maken deel uit van standaard C, maar er zijn veel aspecten die door de implementatie worden gedefinieerd. Ze zijn een van de minst draagbare delen van C.

Syntaxis

  • typespecificatie-ID: grootte;

parameters

Parameter Beschrijving
type specifier signed , unsigned , int of _Bool
identifier De naam voor dit veld in de structuur
grootte Het aantal bits dat voor dit veld moet worden gebruikt

Opmerkingen

De enige draagbare typen voor _Bool zijn signed , unsigned of _Bool . Het gewone type int kan worden gebruikt, maar de standaard zegt (§6.7.2¶5) ... voor bitvelden wordt door de implementatie bepaald of de specificator int hetzelfde type aangeeft als signed int of hetzelfde type als unsigned int .

Andere typen gehele getallen kunnen worden toegestaan door een specifieke implementatie, maar het gebruik ervan is niet draagbaar.

Bit-velden

Een eenvoudig bitveld kan worden gebruikt om dingen te beschrijven waarbij een specifiek aantal bits kan zijn betrokken.

struct encoderPosition {
   unsigned int encoderCounts : 23;
   unsigned int encoderTurns  : 4;
   unsigned int _reserved     : 5;
};

In dit voorbeeld beschouwen we een encoder met 23 bits met enkele precisie en 4 bits om multi-turn te beschrijven. Bitvelden worden vaak gebruikt bij de interface met hardware die gegevens uitvoert die zijn gekoppeld aan een specifiek aantal bits. Een ander voorbeeld zou communicatie met een FPGA kunnen zijn, waarbij de FPGA gegevens in uw geheugen in 32-bits secties schrijft, waardoor hardware kan worden gelezen:

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

Voor dit voorbeeld hebben we een veelgebruikte constructie getoond om toegang te krijgen tot de gegevens in zijn individuele bits, of om het gegevenspakket als geheel te schrijven (emuleren wat de FPGA zou kunnen doen). We kunnen dan toegang krijgen tot de bits zoals deze:

FPGAInfo fInfo;
fInfo.data = 0xFF34F;
if (fInfo.bits.bulb1On) {
    printf("Bulb 1 is on\n");
}

Dit is geldig, maar volgens de C99-norm 6.7.2.1, item 10:

De volgorde van toewijzing van bitvelden binnen een eenheid (van hoge orde naar lage orde of van lage orde naar hoge orde) is door de implementatie bepaald.

U moet zich bewust zijn van endianness wanneer u bitvelden op deze manier definieert. Als zodanig kan het nodig zijn om een preprocessorrichtlijn te gebruiken om te controleren op de endianness van de machine. Een voorbeeld hiervan is:

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;

Bitvelden gebruiken als kleine gehele getallen

#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;
}

Bit-velduitlijning

Bitvelden geven de mogelijkheid om structuurvelden aan te geven die kleiner zijn dan de tekenbreedte. Bitvelden worden geïmplementeerd met byte-niveau of woordniveau-masker. Het volgende voorbeeld resulteert in een structuur van 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. */
};

Het commentaar beschrijft een mogelijke lay-out, maar omdat de standaard zegt dat de uitlijning van de adresseerbare opslageenheid niet gespecificeerd is , zijn andere lay-outs ook mogelijk.

Een naamloos bitveld kan van elke grootte zijn, maar ze kunnen niet worden geïnitialiseerd of ernaar worden verwezen.

Aan een bitveld van nul breedte kan geen naam worden gegeven en wordt het volgende veld uitgelijnd met de grens die wordt gedefinieerd door het gegevenstype van het bitveld. Dit wordt bereikt door bits tussen de bitvelden op te vullen.

De grootte van structuur 'A' is 1 byte.

struct A
{
    unsigned char c1 : 3;
    unsigned char c2 : 4;
    unsigned char c3 : 1;
};

In structuur B slaat het eerste naamloze bitveld 2 bits over; het nul-breedte c2 na c2 zorgt ervoor dat c3 begint vanaf de char-grens (dus 3 bits worden overgeslagen tussen c2 en c3 . Er zijn 3 opvulbits na c4 . De structuur is dus 2 bytes.

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

Wanneer zijn bitvelden nuttig?

Een bitveld wordt gebruikt om vele variabelen samen te voegen in één object, vergelijkbaar met een structuur. Dit zorgt voor minder geheugengebruik en is vooral handig in een ingesloten omgeving.

 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

Als we deze variabelen afzonderlijk declareren, moet elk minimaal een 8-bits geheel getal zijn en moet de totale benodigde ruimte 5 bytes zijn. Bovendien zullen de variabelen niet het gehele bereik van een niet-ondertekend geheel getal van 8 bits (0-255) gebruiken. Hier kunnen we bitvelden gebruiken.

typedef struct {
   unsigned int a:2;
   unsigned int b:1;
   unsigned int c:3;
   unsigned int d:1;
   unsigned int e:1;
} bit_a;

De bitvelden in de structuur worden op dezelfde manier benaderd als elke andere structuur. De programmeur moet ervoor zorgen dat de variabelen binnen bereik worden geschreven. Als het buiten bereik is, is het gedrag niet gedefinieerd.

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

Vaak wil de programmeur de set bitvelden op nul zetten. Dit kan element voor element worden gedaan, maar er is een tweede methode. Maak eenvoudig een vereniging van de bovenstaande structuur met een niet-ondertekend type dat groter is dan of gelijk is aan de grootte van de structuur. Vervolgens kan de volledige set bitvelden op nul worden gezet door dit niet-ondertekende geheel getal op nul te zetten.

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;    

Het gebruik is als volgt

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

Concluderend worden bitvelden vaak gebruikt in situaties met een beperkt geheugen, waarin u veel variabelen hebt die een beperkt bereik kunnen hebben.

Don'ts voor bit-velden

  1. Arrays van bitvelden, verwijzingen naar bitvelden en functies die bitvelden retourneren zijn niet toegestaan.
  2. De adresoperator (&) kan niet worden toegepast op bitveldleden.
  3. Het gegevenstype van een bitveld moet breed genoeg zijn om de grootte van het veld te kunnen bevatten.
  4. De operator sizeof() kan niet worden toegepast op een sizeof() .
  5. Er is geen manier om een typedef voor een typedef geïsoleerd te maken (hoewel je zeker een typedef kunt maken voor een structuur die typedef bevat).
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 */
}


Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow