Domanda

Durante una revisione del codice che ho incontrato un po 'di codice che definisce una struttura semplice come segue:

class foo {
   unsigned char a;
   unsigned char b;
   unsigned char c;
}

Altrove, si definisce un array di questi oggetti:

foo listOfFoos[SOME_NUM];

Successivamente, le strutture sono prime copiati in un buffer:

memcpy(pBuff,listOfFoos,3*SOME_NUM);

Questo codice si basa sull'ipotesi che:. A) La dimensione foo è 3, e viene applicato senza imbottitura, e b) Un array di questi oggetti viene compresso senza imbottitura tra loro

.. :

Ho provato con GNU su due piattaforme (RedHat 64b, Solaris 9), ed ha funzionato su entrambi.

Sono le ipotesi di cui sopra sono validi? In caso contrario, a quali condizioni (per esempio cambiamento di OS / compilatore) potrebbero non riescono?

È stato utile?

Soluzione

Un array di oggetti è richiesto di essere contiguo, quindi non è mai imbottitura tra gli oggetti, anche se imbottitura può essere aggiunto alla fine di un oggetto (che produce quasi lo stesso effetto).

:

Dato che si sta lavorando con il char di, le ipotesi sono probabilmente ragione il più delle volte, ma lo standard C ++ non garantisce certamente esso. Un compilatore diverso, o anche solo un cambiamento nelle flag passati al compilatore corrente potrebbe causare imbottitura essere inserito tra gli elementi della struct o dopo l'ultimo elemento della struct, o entrambi.

Altri suggerimenti

Sarebbe sicuramente più sicuro di fare:

sizeof(foo) * SOME_NUM

Se si copia l'array come questo si dovrebbe usare

memcpy(pBuff,listOfFoos,sizeof(listOfFoos));

Questo funziona sempre fino a quando si assegnato pBuff alla stessa dimensione. In questo modo si stanno facendo alcuna ipotesi sulle imbottiture e l'allineamento a tutti.

La maggior parte dei compilatori allineare una struttura o di classe per l'allineamento richiesto del tipo più grande in dotazione. Nel tuo caso di caratteri che significa che non allineamento e imbottitura, ma se si aggiunge un breve, ad esempio la classe sarebbe 6 byte di grandi dimensioni con un byte di imbottitura aggiunto tra l'ultimo carattere e la vostra breve.

Credo che la ragione per cui questo funziona perché tutti i campi della struttura sono char che allineare uno. Se v'è almeno un campo che non allinea 1, l'allineamento della struttura / classe non sarà 1 (l'allineamento sarà dipende dall'ordine e allineamento campo).

Vediamo qualche esempio:

#include <stdio.h>
#include <stddef.h>

typedef struct {
    unsigned char a;
    unsigned char b;
    unsigned char c;
} Foo;
typedef struct {
    unsigned short i;
    unsigned char  a;
    unsigned char  b;
    unsigned char  c;
} Bar;
typedef struct { Foo F[5]; } F_B;
typedef struct { Bar B[5]; } B_F;


#define ALIGNMENT_OF(t) offsetof( struct { char x; t test; }, test )

int main(void) {
    printf("Foo:: Size: %d; Alignment: %d\n", sizeof(Foo), ALIGNMENT_OF(Foo));
    printf("Bar:: Size: %d; Alignment: %d\n", sizeof(Bar), ALIGNMENT_OF(Bar));
    printf("F_B:: Size: %d; Alignment: %d\n", sizeof(F_B), ALIGNMENT_OF(F_B));
    printf("B_F:: Size: %d; Alignment: %d\n", sizeof(B_F), ALIGNMENT_OF(B_F));
}

Quando viene eseguito, il risultato è il seguente:

Foo:: Size: 3; Alignment: 1
Bar:: Size: 6; Alignment: 2
F_B:: Size: 15; Alignment: 1
B_F:: Size: 30; Alignment: 2

Si può vedere che Bar e F_B ha allineamento 2 in modo che il suo campo mi verrà allineato correttamente. Si può anche vedere che la dimensione di Bar è 6 e non 5 . Allo stesso modo, la dimensione del B_F (5 di Bar) è 30 e non 25 .

Quindi, se v'è un codice difficile invece di sizeof(...), si otterrà un problema qui.

Spero che questo aiuti.

Il tutto fino all'allineamento della memoria. macchine a 32 bit tipici leggere o scrivere 4 byte di memoria per tentativo. Questa struttura è sicuro da problemi perché cade sotto che 4 byte facilmente senza problemi imbottitura confusione.

Ora, se la struttura era in quanto tale:

class foo {
   unsigned char a;
   unsigned char b;
   unsigned char c;
   unsigned int i;
   unsigned int j;
}

La logica colleghi provocare probabilmente

memcpy(pBuff,listOfFoos,11*SOME_NUM);

(3 di char = 3 byte, ints 2 = 2 * 4 byte, quindi 3 + 8)

Sfortunatamente, a causa di imbottitura la struttura prende effettivamente fino a 12 byte. Questo perché non si può andare bene tre char e di un int in quella parola 4 byte, e quindi c'è un byte di spazio imbottita lì che spinge l'int in essa la propria parola. Questo diventa sempre più di un problema più diversi tipi di dati diventano.

Per le situazioni in cui si utilizza cose come questa, e non posso evitarlo, cerco di fare la pausa di compilazione quando le presunzioni non reggono più. Io uso qualcosa come il seguente (o Boost.StaticAssert se la situazione lo consente):

static_assert(sizeof(foo) <= 3);

// Macro for "static-assert" (only usefull on compile-time constant expressions)
#define static_assert(exp)           static_assert_II(exp, __LINE__)
// Macro used by static_assert macro (don't use directly)
#define static_assert_II(exp, line)  static_assert_III(exp, line)
// Macro used by static_assert macro (don't use directly)
#define static_assert_III(exp, line) enum static_assertion##line{static_assert_line_##line = 1/(exp)}

sarei stato sicuro e sostituito il numero magico 3 con un sizeof(foo) mi sa.

La mia ipotesi è che il codice ottimizzato per architetture di processori futuri probabilmente introdurrà una qualche forma di imbottitura.

E cercando di rintracciare quel tipo di bug è un vero e proprio dolore!

Come altri hanno detto, usando sizeof (foo) è una scommessa più sicura. Alcuni compilatori (in particolare quelli esoterici nel mondo embedded) saranno aggiungere un'intestazione di 4 byte per le classi. Altri possono fare trucchi funky memoria di allineamento, a seconda delle impostazioni del compilatore.

Per una piattaforma mainstream, probabilmente stai bene, ma non è una garanzia.

Ci potrebbe essere ancora un problema con sizeof () quando si passa i dati tra due computer. Su uno di essi il codice potrebbe essere compilato con imbottitura e l'altro senza, nel qual caso sizeof () darebbe risultati diversi. Se i dati matrice viene passata da un computer all'altro sarà male interpretato perché gli elementi della matrice non verranno trovati dove previsto. Una soluzione è quella di fare in modo che #pragma pack (1) viene utilizzato quando possibile, ma che potrebbe non essere sufficiente per gli array. Migliore è di prevedere il problema e utilizzare imbottitura per un multiplo di 8 byte per elemento dell'array.

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