Ricerca…


introduzione

La maggior parte delle variabili in C ha una dimensione che è un numero intero di byte. I campi di bit sono una parte di una struttura che non occupa necessariamente un numero intero di byte; possono un numero qualsiasi di bit. Più campi di bit possono essere raggruppati in un'unica unità di memoria. Fanno parte dello standard C, ma ci sono molti aspetti che sono definiti dall'implementazione. Sono una delle parti meno trasportabili di C.

Sintassi

  • identificatore del tipo identificativo: dimensione;

Parametri

Parametro Descrizione
tipo-specificatore signed , unsigned , int o _Bool
identificatore Il nome per questo campo nella struttura
taglia Il numero di bit da utilizzare per questo campo

Osservazioni

Gli unici tipi portatili per campi bit sono signed , unsigned o _Bool . Il tipo plain int può essere usato, ma lo standard dice (§6.7.2¶5) ... per i campi bit, è definito dall'implementazione se lo specificatore int designa lo stesso tipo di signed int o lo stesso tipo di unsigned int .

Altri tipi di interi possono essere consentiti da un'implementazione specifica, ma il loro utilizzo non è portabile.

I bit-field

Un semplice campo di bit può essere usato per descrivere cose che possono avere un numero specifico di bit coinvolti.

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

In questo esempio consideriamo un codificatore con 23 bit di precisione singola e 4 bit per descrivere il multigiro. I bit-field vengono spesso utilizzati quando si interfaccia con l'hardware che emette dati associati a un numero specifico di bit. Un altro esempio potrebbe essere la comunicazione con un FPGA, in cui l'FPGA scrive i dati nella memoria in sezioni a 32 bit consentendo letture hardware:

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

Per questo esempio abbiamo mostrato un costrutto comunemente usato per poter accedere ai dati nei suoi singoli bit, o per scrivere il pacchetto di dati nel suo complesso (emulare quello che potrebbe fare l'FPGA). Potremmo quindi accedere ai bit in questo modo:

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

Questo è valido, ma secondo lo standard C99 6.7.2.1, elemento 10:

L'ordine di assegnazione dei campi di bit all'interno di un'unità (ordine alto a basso ordine o basso ordine a alto ordine) è definito dall'implementazione.

È necessario essere consapevoli di endianness quando si definiscono campi di bit in questo modo. Come tale può essere necessario utilizzare una direttiva preprocessore per verificare la endianità della macchina. Un esempio di questo segue:

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;

Utilizzo di campi bit come piccoli numeri interi

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

Allineamento del campo di bit

I campi di bit danno la possibilità di dichiarare campi di struttura che sono più piccoli della larghezza del carattere. I campi di bit vengono implementati con maschera a livello di byte o parola. L'esempio seguente produce una struttura di 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. */
};

I commenti descrivono un possibile layout, ma poiché lo standard dice che l'allineamento dell'unità di memoria indirizzabile non è specificato , sono possibili anche altri layout.

Un campo di bit senza nome può essere di qualsiasi dimensione, ma non può essere inizializzato o referenziato.

A un campo di bit a larghezza zero non può essere assegnato un nome e allinea il campo successivo al limite definito dal tipo di dati del campo di bit. Questo si ottiene tamponando i bit tra i campi di bit.

La dimensione della struttura 'A' è 1 byte.

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

Nella struttura B, il primo campo di bit senza nome salta 2 bit; il campo di bit a larghezza zero dopo c2 fa sì che c3 inizi dal limite del char (quindi vengono saltati 3 bit tra c2 e c3 . Dopo il c4 ci sono 3 bit di padding, quindi la dimensione della struttura è 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;
};

Quando sono utili i campi bit?

Un campo di bit viene utilizzato per raggruppare più variabili in un unico oggetto, simile a una struttura. Ciò consente un utilizzo ridotto della memoria ed è particolarmente utile in un ambiente embedded.

 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

Se dichiariamo queste variabili separatamente, allora ognuno deve essere almeno un numero intero a 8 bit e lo spazio totale richiesto sarà 5 byte. Inoltre le variabili non utilizzeranno l'intero intervallo di un intero senza segno a 8 bit (0-255). Qui possiamo usare campi di bit.

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

I campi di bit nella struttura sono accessibili come qualsiasi altra struttura. Il programmatore deve fare attenzione che le variabili siano scritte nell'intervallo. Se fuori dal range il comportamento non è definito.

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

Spesso il programmatore vuole azzerare il set di campi di bit. Questo può essere fatto elemento per elemento, ma esiste un secondo metodo. Crea semplicemente un'unione della struttura sopra con un tipo senza segno che è maggiore o uguale alla dimensione della struttura. Quindi l'intero set di campi di bit può essere azzerato azzerando questo intero senza segno.

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;    

L'utilizzo è il seguente

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

In conclusione, i campi di bit sono comunemente usati in situazioni con vincoli di memoria in cui si hanno molte variabili che possono assumere intervalli limitati.

Non fare per i campi di bit

  1. Matrici di campi di bit, puntatori a campi di bit e funzioni che restituiscono campi di bit non sono consentiti.
  2. L'operatore dell'indirizzo (&) non può essere applicato ai membri del campo di bit.
  3. Il tipo di dati di un campo di bit deve essere sufficientemente ampio da contenere la dimensione del campo.
  4. L'operatore sizeof() non può essere applicato a un campo di bit.
  5. Non è possibile creare un typedef per un campo di bit in isolamento (sebbene sia possibile creare un typedef per una struttura contenente campi di bit).
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
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow