2010-01-24 11 views
13

Ho bisogno di mettere in valigia alcuni bit in un byte in questo modo:Campi bitmap C/C++ rispetto agli operatori bit a bit per individuare i bit, che è più veloce, migliore, più portabile?

struct 
{ 
    char bit0: 1; 
    char bit1: 1; 
} a; 

if(a.bit1) /* etc */ 

o:

if(a & 0x2) /* etc */ 

Dal codice sorgente di chiarezza è abbastanza ovvio per me che sono campi di bit più ordinato. Ma quale opzione è più veloce? So che la differenza di velocità non sarà eccessiva se non nessuna, ma dato che posso usarne qualcuna, se è più veloce, migliore.
D'altra parte, ho letto che i bitfield non sono garantiti per organizzare i bit nello stesso ordine attraverso le piattaforme e voglio che il mio codice sia portatile.

Note: Se si prevede di rispondere a 'Profilo' ok, lo farò, ma poiché sono pigro, se qualcuno ha già la risposta, molto meglio.
Il codice potrebbe essere sbagliato, puoi correggermi se vuoi, ma ricorda qual è il punto di questa domanda e per favore prova a rispondere anche a questo.

+0

Potresti essere interessato a http://stackoverflow.com/questions/1044654/bitfield-manipulation-in-c. C'è almeno un'altra domanda molto simile su cui ricordo di aver commentato (non sembra essere questa). –

+0

Bitfield: sei fuori! Grazie a tutti. – Petruza

+0

Wow. Abbiamo insegnato una lezione preziosa qui. –

risposta

6

Preferisco utilizzare il secondo esempio di preferenza per la massima portabilità. Come ha fatto notare Neil Butterworth, l'uso dei bitfield è solo per il processore nativo. Ok, pensaci, cosa succederebbe se Intel x86 fallisse domani, il codice sarà bloccato, il che significa dover ri-implementare i bitfield per un altro processore, diciamo il chip RISC.

Devi guardare l'immagine più grande e chiedere come ha fatto OpenBSD a trasferire i propri sistemi BSD su molte piattaforme utilizzando un codice base? Ok, ammetto che è un po 'esagerato, discutibile e soggettivo, ma realisticamente parlando, se vuoi portare il codice su un'altra piattaforma, è il modo di farlo usando il secondo esempio che hai usato nella tua domanda .

Non solo, i compilatori per piattaforme diverse hanno il proprio modo di eseguire il padding, allineando bitfield per il processore su cui è attivo il compilatore. E inoltre, che dire della funzionalità del processore?

Mai fare affidamento sui bitfield come una bacchetta magica. Se si desidera la velocità per il processore e sarà fissata su di esso, ovvero nessuna intenzione di eseguire il porting, quindi sentirsi liberi di utilizzare bitfield. Non puoi averli entrambi!

+0

+1: Non è solo che non hai intenzione di eseguire il porting, è che non hai intenzione di usare l'intero contenuto della variabile come un numero intero (rispetto a singoli bit) in qualsiasi contesto piuttosto che in memoria su un processore. Se invii il valore intero di una struttura bitfield a un disco, alla rete o ad alcune periferiche di sistema incorporate, faresti meglio a essere sicuro che il compilatore collochi i bitfield nell'ordine corretto. –

+6

Ti interessa spiegare come utilizzare i bitfield incorporati su quelli definiti manualmente è più portabile? Posso crederci se ci si basa sul layout di memoria della struct impostato in un modo molto certo, ma se tutto ciò che si fa è accedere a ciascun campo tramite la sintassi integrata, non riesco a vedere alcun problema di portabilità. –

+0

Il genitore non è corretto, non è un problema di portabilità della CPU, è un problema di portabilità del compilatore. Ogni compilatore è libero di allineare i bitfield come preferisce, il che significa che il codice compilato con due compilatori diversi non può collegarsi correttamente tra loro e condividere i dati contenenti bitfield. Facendo il mascheramento e spostando te stesso elimina questo problema. – naasking

4

Se si desidera la portabilità, evitare i bitfield. E se sei interessato all'esecuzione di un codice specifico, non c'è alternativa alla scrittura dei tuoi test. Ricorda, i bitfield utilizzeranno le istruzioni bit a bit del processore sotto il cofano.

+0

Perché, oltre all'ordinamento dei bitfield, i bitfield potrebbero non essere portabili? Anche alcuni processori supportano l'inserimento/estrazione di bitfield. Il 68020 ha fatto, per esempio. –

+3

Ci sono anche problemi di imballaggio. E proprio perché un processore ha un'istruzione per scopi speciali, non c'è alcuna garanzia che il compilatore lo usi - ecco perché ho detto che un test è essenziale. –

+2

Ah, ma solo perché un bitfield potrebbe utilizzare più o meno spazio a seconda dell'implementazione, non lo rende non portatile. E se un compilatore non sfrutta ciò che il processore può fare, dovrebbe essere migliorato o sostituito. –

1

Penso che un programmatore C tenderebbe alla seconda opzione di utilizzare maschere di bit e operazioni logiche per dedurre il valore di ciascun bit. Piuttosto che avere il codice pieno di valori esadecimali, le enumerazioni verrebbero impostate o, in genere, quando sono coinvolte operazioni più complesse, le macro per ottenere/impostare bit specifici. Ho sentito sulla vite, che la struttura implementata bitfield è più lenta.

+0

Più precisamente, non è garantito che i bitfield siano ordinati per tutte le piattaforme. Quindi, se stai usando un bitfield per un registro hardware, avrai un tempo di debug molto difficile perché il tuo hardware non sta facendo quello che dovrebbe essere. –

5

Il primo è esplicito e qualunque sia la velocità, la seconda espressione è soggetta a errori perché qualsiasi modifica alla struttura potrebbe rendere errata la seconda espressione.

Quindi utilizzare il primo.

+0

No. Il primo utilizza una caratteristica insolita che la maggior parte dei codificatori non tocca mai e non capisce, mentre la seconda è semplicemente criptica C. Quindi è la seconda opzione che è sia più chiara riguardo la sua intenzione, sia meno probabilità di essere interrotta da manutentori. – Porculus

+0

L'unica cosa che non va nella seconda espressione è che '0x02' deve essere una costante definita in modo che uno sviluppatore sappia cosa significa. La cosa che non va nel primo è che il compilatore può mettere i due bit nel byte a 8 bit dove vuole. Il kernel Linux definisce un intero set di costanti per dichiarare l'ordine dei bit e il packing, il che porta a blocchi '# ifdef' difficili da gestire. E questo presuppone che la piattaforma definisca 'char' come 8 bit; Ho sviluppato per piattaforme che definivano 'char' come 16 bit perché il DSP non poteva indirizzare byte, solo parole. –

12

I bitfield rendono il codice molto più chiaro se vengono utilizzati in modo appropriato. Userei bitfield solo come dispositivo salvaspazio. Un luogo comune in cui li ho visti è nei compilatori: spesso le informazioni sui tipi o sui simboli sono costituite da un gruppo di flag true/false. I bitfield sono ideali qui poiché un programma tipico avrà molte migliaia di questi nodi creati quando viene compilato.

Non utilizzare i bitfield per eseguire un lavoro di programmazione incorporato comune: lettura e scrittura dei registri del dispositivo. Preferisco usare i turni e le maschere qui perché ottieni esattamente i bit che la documentazione ti dice che ti serve e non devi preoccuparti delle differenze nell'implementazione dei vari bitfield da parte dei compilatori.

Per quanto riguarda la velocità, un buon compilatore darà lo stesso codice per bitfield che maschererà.

+0

Il problema con l'utilizzo di bitfield è che non si ha alcuna garanzia di risparmio di spazio. Il compilatore potrebbe semplicemente allocare 32 bit per ogni bitfield, come se fosse stato un int. Perché no? Sarà più veloce nella maggior parte dei casi. – jalf

+0

Giusto. ma potrebbe risparmiare spazio. Molte persone intelligenti contano sui bitfield per fare proprio questo. –

+0

@jaif: chiamo BS. Hai qualcosa che supporta questa affermazione, sono troppo pigro per guardare le specifiche, ma dubito davvero che sia permesso. – SoapBox

6

I campi bituminosi C sono morti dal momento in cui sono stati inventati - per ragioni sconosciute. Alla gente non piacevano e usavano invece operatori bit a bit. Devi aspettarti che altri sviluppatori non capiscano il codice bitfield C.

Per quanto riguarda più veloce: irrilevante. Qualsiasi compilatore ottimizzante (il che significa praticamente tutto) farà sì che il codice faccia la stessa cosa in qualunque notazione. È un comune fraintendimento dei programmatori C che il compilatore cercherà e sostituirà semplicemente le parole chiave in assembly. I compilatori moderni usano il codice sorgente come un modello per ciò che deve essere realizzato e quindi emettono un codice che spesso sembra molto diverso ma raggiunge il risultato desiderato.

1

Non leggere molto nei "campi di bit non portatili". Ci sono due aspetti di campi di bit che sono definiti dall'implementazione: la firma e il layout e uno non specificato: l'allineamento dell'unità di allocazione in cui sono impacchettati. Se non è necessario altro che l'effetto di imballaggio, il loro utilizzo è altrettanto semplice (purché si specifichi esplicitamente una parola chiave signed dove necessario) come chiamate di funzione che hanno anche proprietà non specificate.

Per quanto riguarda le prestazioni, il profilo è la migliore risposta possibile. In un mondo perfetto, non ci sarebbe differenza tra i due scritti. In pratica, ce ne possono essere alcuni, ma posso pensare a tante ragioni in una direzione come l'altra. E può essere molto sensibile al contesto (differenza logicamente insignificante tra non firmata e firmata per esempio) quindi misurare nel contesto ...

Per riassumere, la differenza è quindi principalmente una differenza di stile nei casi in cui si ha veramente la scelta (cioè non se la disposizione precisa è importante). In tal caso, si tratta di un'ottimizzazione (in termini di dimensioni, non di velocità) e quindi tenderei prima a scrivere il codice senza di esso e aggiungerlo dopo se necessario. Quindi, i campi bit sono la scelta più ovvia (le modifiche da fare sono le più piccole per ottenere il risultato e sono contenute nel luogo unico di definizione invece di essere diffuse in tutti i luoghi di utilizzo).