Perché i bitfield non sono consentiti con variabili normali?

Mi chiedo perché i bitfield funzionano con le unioni / structs ma non con una variabile normale come int o short .
Questo funziona:

 struct foo { int bar : 10; }; 

Ma questo fallisce:

 int bar : 10; // "Expected ';' at end of declaration" 

Perché questa funzione è disponibile solo in sindacati / strutture e non con variabili? Non è tecnico lo stesso?


Modificare:

Se fosse permesso, potresti creare una variabile con 3 byte, ad esempio, senza usare il membro struct / union ogni volta. Questo è come lo farei con una struttura:

 struct int24_t { int x : 24 __attribute__((packed)); }; struct int24_t var; // sizeof(var) is now 3 // access the value would be easier: var.x = 123; 

Questa è una domanda soggettiva, “Perché le specifiche dicono questo?” Ma darò il mio colpo.

Le variabili in una funzione normalmente hanno una memorizzazione “automatica”, al contrario di una delle altre durate (durata statica, durata del thread e durata allocata).

In una struttura, si definisce esplicitamente il layout di memoria di alcuni oggetti. Ma in una funzione, il compilatore assegna automaticamente l’archiviazione in qualche modo non specificato alle tue variabili. Ecco una domanda: quanti byte occupa x nello stack?

 // sizeof(unsigned) == 4 unsigned x; 

Potrebbe richiedere fino a 4 byte, oppure potrebbe richiedere fino a 8, o 12, o 0, oppure potrebbe essere inserito in tre registri diversi contemporaneamente, oppure nello stack e in un registro, oppure potrebbe ottenere quattro posizioni nello stack .

Il punto è che il compilatore sta facendo l’allocazione per te. Dato che non stai facendo il layout dello stack, non dovresti specificare le larghezze di bit.

Discussione estesa: i bitfield sono in realtà un po ‘speciali. La specifica afferma che i bitfield adiacenti vengono impacchettati nella stessa unità di archiviazione. I bitfield non sono in realtà oggetti.

  1. Non puoi sizeof() un campo di bit.

  2. Non puoi malloc() un po ‘di campo.

  3. Non puoi &addressof un campo di bit.

Tutte queste cose che puoi fare con gli oggetti in C, ma non con bitfield. I bitfield sono una cosa speciale creata solo per le strutture e da nessun’altra parte.

Informazioni su int24_t (aggiornato): Funziona su alcune architetture, ma non su altre. Non è nemmeno leggermente portatile.

 typedef struct { int x : 24 __attribute__((packed)); } int24_t; 

Su Linux ELF / x64, OS X / x86, OS X / x64, sizeof(int24_t) == 3 . Ma su OS X / PowerPC, sizeof(int24_t) == 4 .

Nota che il codice che GCC genera per caricare int24_t è fondamentalmente equivalente a questo:

 int result = (((char *) ptr)[0] << 16) | (((unsigned char *) ptr)[1] << 8) | ((unsigned char *)ptr)[2]; 

Sono 9 istruzioni su x64, solo per caricare un singolo valore.

I membri di una struttura o unione hanno relazioni tra la loro posizione di archiviazione. Un compilatore non può riordinarlo o impacchettarlo in modi intelligenti per risparmiare spazio a causa di rigidi vincoli sul layout; fondamentalmente l’unica libertà che un compilatore ha nella disposizione delle strutture è la libertà di aggiungere padding extra oltre la quantità necessaria per l’allineamento. I bitfield consentono di dare manualmente al compilatore una maggiore libertà di impacchettare le informazioni promettendo che (1) non è necessario l’indirizzo di questi membri e (2) non è necessario memorizzare valori al di fuori di un certo intervallo limitato.

Se parli di variabili individuali anziché di membri della struttura, nella macchina astratta non hanno alcuna relazione tra le loro posizioni di archiviazione. Se sono variabili automatiche locali in una funzione e i loro indirizzi non vengono mai presi, il compilatore è libero di tenerli nei registri o di metterli in memoria come preferisce. Ci sarebbe poco o nessun vantaggio nel fornire manualmente tali suggerimenti al compilatore.

Perché non è significativo. Le dichiarazioni bitfield vengono utilizzate per condividere e riorganizzare i bit tra i campi di una struct . Se non hai membri, solo una singola variabile, cioè di dimensione costante (che è definita dall’implementazione), Ad esempio, è una contraddizione dichiarare un char , che è quasi certamente largo 8 bit, come variabile a uno o twelwe bit .

Se uno ha una struct QBLOB che contiene combina quattro QBLOB 2 bit in un singolo byte, ogni volta che quella struct viene utilizzata rappresenterà un risparmio di tre byte rispetto a una struct che contiene semplicemente quattro campi di tipo unsigned char . Se si dichiara un array QBLOB myArray[1000000] , tale array richiederà solo 1.000.000 byte; se QBLOB fosse stato una struttura con quattro campi unsigned char , avrebbe avuto bisogno di oltre 3.000.000 di byte. Pertanto, la possibilità di utilizzare bitfield può rappresentare un notevole risparmio di memoria.

Al contrario, sulla maggior parte delle architetture, dichiarare che una variabile semplice sia di un tipo bitfield di dimensioni ottimali potrebbe risparmiare al massimo 15 bit rispetto a dichiarare che è il tipo integrale standard più piccolo adatto. Dal momento che l’accesso ai bitfield richiede generalmente più codice rispetto all’accesso alle variabili dei tipi integrali standard, ci sono pochi casi in cui la dichiarazione di singole variabili come campi di bit offre qualsiasi vantaggio.

C’è una notevole eccezione a questo principio, tuttavia: alcune architetture includono funzionalità che possono impostare, cancellare e testare i singoli bit in modo ancora più efficiente di quanto possano leggere e scrivere byte. I compilatori per alcune di queste architetture includono un tipo di bit e impaccheranno otto variabili di quel tipo in ogni byte di memoria. Tali variabili sono spesso limitate all’ambito statico o globale, dal momento che le istruzioni specializzate che le gestiscono possono essere limitate all’uso di determinate aree di memoria (il linker può garantire che tali variabili vengano posizionate dove devono andare).

Tutti gli oggetti devono occupare uno o più byte contigui o parole, ma un campo bit non è un object ; è semplicemente un modo user-friendly di mascherare i bit in una parola. La struct contenente il bitfield deve occupare un numero intero di byte o parole; il compilatore aggiunge solo il padding necessario nel caso in cui le dimensioni del bitfield non si sumno a una parola completa.

Non c’è una ragione tecnica per cui non si possa estendere la syntax C per definire campi di bit al di fuori di una struttura (AFAIK), ma sarebbero di utilità discutibile per la quantità di lavoro richiesto.