Domanda

Ho letto che l'ordine dei campi di bit all'interno di una struct è specifica piattaforma. Che dire se uso diverse opzioni di imballaggio specifiche del compilatore, sarà questi dati garanzia viene memorizzato nel giusto ordine così come sono scritte? Ad esempio:

struct Message
{
  unsigned int version : 3;
  unsigned int type : 1;
  unsigned int id : 5;
  unsigned int data : 6;
} __attribute__ ((__packed__));

su un processore Intel con il compilatore GCC, i campi sono stati disposti in memoria che sono mostrati. Message.version era i primi 3 bit nel buffer, e Message.type seguito. Se trovo opzioni dell'imballaggio struct equivalenti per vari compilatori, questo sarà cross-platform?

È stato utile?

Soluzione

No, non sarà completamente portatile. Imballaggio opzioni per le strutture sono le estensioni, e sono essi stessi non completamente portatile. In aggiunta a ciò, C99 §6.7.2.1, punto 10 afferma: "L'ordine di assegnazione dei bit-field all'interno di un'unità (-alto per ordine basso o basso per ordine elevato) dipende dall'implementazione."

Anche un singolo compilatore potrebbe porre il campo di bit differente dipendentemente dalle endianness della piattaforma di destinazione, per esempio.

Altri suggerimenti

campi di bit variano notevolmente da compilatore a compilatore, mi dispiace.

Con GCC, grosse macchine endian lay out di biella bit prima e piccole macchine endian lay out il piccolo fine bit prima.

K & R dice "adiacente [po'-] membri campo delle strutture sono confezionati in unità di memoria di attuazione-dipendente in una direzione dipendente dall'implementazione. Quando un campo seguente altro campo non si adatta ... può essere suddiviso tra unità o la unità può essere imbottito. Un campo senza nome di larghezza 0 forze questo padding ... "

Quindi, se avete bisogno di macchina il layout binario indipendente è necessario farlo da soli.

Questa ultima affermazione vale anche per i non-campi di bit a causa di imbottitura - tuttavia tutti i compilatori sembrano avere qualche modo di forzare imballaggio di byte di una struttura, come ti vedo già scoperto per GCC

.

a bit dovrebbe essere evitato - non sono molto portabili tra compilatori anche per la stessa piattaforma. dallo standard C99 6.7.2.1/10 - "Struttura e sindacali prescrittori" (c'è formulazione simile nello standard C90):

  

L'attuazione può assegnare qualsiasi unità di memorizzazione indirizzabile abbastanza grande da contenere un campo di bit. Se abbastanza spazio rimane, un campo di bit che segue immediatamente un altro campo di bit in una struttura deve essere imballato in bit adiacenti della stessa unità. Se lo spazio insufficiente rimane, se un campo di bit che non si adatta è messo in all'unità o sovrappone unità adiacenti è definito dall'implementazione. L'ordine di assegnazione dei bit-campi all'interno un'unità (-alto per ordine basso o basso per ordine elevato) dipende dall'implementazione. L'allineamento dell'unità di memorizzazione indirizzabile è specificato.

Non si può garantire se un campo di bit sarà un confine int 'arco' o meno e non è possibile specificare se un campo di bit inizia alla fascia bassa del int o la fascia alta del int (questo è indipendente dal fatto che il processore è big-endian o little-endian).

Prefer bitmasks. Usare inline (o anche le macro) per impostare, chiara e testare i bit.

endianness si parla di ordini di byte non ordini bit. Oggi , è sicuro al 99% che gli ordini di bit sono fissi. Tuttavia, quando si utilizza campi di bit, endianness dovrebbe essere presa in conto. Vedere l'esempio di seguito.

#include <stdio.h>

typedef struct tagT{

    int a:4;
    int b:4;
    int c:8;
    int d:16;
}T;


int main()
{
    char data[]={0x12,0x34,0x56,0x78};
    T *t = (T*)data;
    printf("a =0x%x\n" ,t->a);
    printf("b =0x%x\n" ,t->b);
    printf("c =0x%x\n" ,t->c);
    printf("d =0x%x\n" ,t->d);

    return 0;
}

//- big endian :  mips24k-linux-gcc (GCC) 4.2.3 - big endian
a =0x1
b =0x2
c =0x34
d =0x5678
 1   2   3   4   5   6   7   8
\_/ \_/ \_____/ \_____________/
 a   b     c           d

// - little endian : gcc (Ubuntu 4.3.2-1ubuntu11) 4.3.2
a =0x2
b =0x1
c =0x34
d =0x7856
 7   8   5   6   3   4   1   2
\_____________/ \_____/ \_/ \_/
       d           c     b   a

La maggior parte del tempo, probabilmente, ma non scommettere la fattoria su di esso, perché se si sbaglia, si perde grandi.

Se davvero, davvero bisogno di avere identiche informazioni binarie, è necessario creare campi di bit con bitmasks - per esempio si utilizza un unsigned short (16 bit) per messaggio, e poi fare le cose come versionMask = 0xe000 per rappresentare i tre bit più in alto.

C'è un problema simile con l'allineamento all'interno di strutture. Ad esempio, Sparc, PowerPC, e 680x0 CPU sono tutti grandi-endian, e il difetto comune per Sparc e PowerPC compilatori è allineare membri della struttura su 4 byte. Tuttavia, un compilatore che ho usato per 680x0 allineati solo sui confini 2 byte! - e non vi era alcuna possibilità di modificare l'allineamento

Così, per alcuni le strutture, le dimensioni su SPARC e PowerPC sono identici, ma più piccolo sul 680x0, e alcuni dei membri sono in diversi offset di memoria all'interno della struct.

Questo è stato un problema con un progetto ho lavorato su, perché un processo server in esecuzione su Sparc sarebbe interrogare un client e scoprire che era big-endian, e assumere potrebbe essere solo schizzare le strutture binarie sulla rete e il cliente potrebbe far fronte. E che ha lavorato bene su clienti PowerPC, e si è schiantato big-time su 680x0 clienti. Non ho scritto il codice, e ci sono voluti un po 'di tempo per trovare il problema. Ma era facile da risolvere, una volta che ho fatto.

Naturalmente la risposta migliore è quella di utilizzare una classe che legge / scrive i campi di bit come un flusso. Usando la struttura di campo di bit C non è solo garantita. Per non parlare è considerato poco professionale / pigro / stupido utilizzare questo nel mondo reale di codifica.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top