2010-02-12 3 views
6

ho una semplice classe in C++ che ha un intero e un vtable:MSVC oggetto layout Quirk

class Something { 

    virtual void sampleVirtualMethod(); 

    int someInteger; 
}; 

Se si guarda la disposizione oggetto per MSVC (utilizzando il/d1reportSingleClassLayout) si ottiene:

class Something  size(8): 
     +--- 
0  | {vfptr} 
4  | someInteger 
     +--- 

Che ha senso. 4 byte per il puntatore vtable e 4 byte per il numero intero. La cosa strana è quando aggiungo un doppio alla classe:

class Something {  
    virtual void sampleVirtualMethod(); 
    int someInteger; 
    **double someDouble;** 
}; 

ottengo questo layout oggetto:

class Something  size(24): 
     +--- 
0  | {vfptr} 
8  | someInteger 
     | <alignment member> (size=4) 
16  | someDouble 
     +--- 

Perché la differenza tra il 0 di offset e someInteger 8 invece di 4? Il vtable è cresciuto fino a 8 byte in qualche modo? Non importa l'ordine di quando aggiungo un doppio, questo succede.

Grazie.

+0

Non posso fare a meno di chiedermi perché ti interesserebbe di questo. Ciò sta causando un problema diretto a ciò su cui stai lavorando? –

+3

Perché * non * ci importa? È sorprendente per l'OP (e per me). Dov'è il danno nel cercare di imparare cose nuove? ;) – jalf

+0

Sto cercando di unire modelli di oggetti tra binari e binari di Visual Studio. In realtà è un problema :). La cosa strana è che sia GCC che Visual Studio dispongono entrambi di oggetti come questo, mentre llvm no. – Mason

risposta

0

Non posso rispondere direttamente alla tua domanda perché non c'è una buona scusa per il comportamento del compilatore. Invece, mi concederò alla speculazione selvaggia poiché non c'è ancora stata una risposta.

ho il sospetto un bug nell'algoritmo di allineamento che va qualcosa come questo:

  • l'allineamento di una struttura (ad esempio, il posizionamento del primo membro) dovrebbe essere almeno largo quanto l'allineamento della sua più ampia membro
  • oops, dimenticato di contare il puntatore tabella di funzione virtuale come un membro

Se questo bug esiste ho il sospetto che è rimasto da primi giorni del compilatore come un compilatore C 4 byte-allineati. Ora il valore predefinito per il compilatore è /Zp8, il che significa che ogni struttura è allineata almeno a 8 byte, quindi in questo caso non sarebbe necessario correggere l'allineamento del "primo" membro in questo caso.

saluti, Sherm

1

Ho il sospetto che this answer ha qualcosa a che fare con esso. Per citare dalla risposta dirkgently s', citando il manuale di GCC:

nota che l'allineamento di qualsiasi tipo struct o unione è richiesto dallo standard ISO C per essere almeno un multiplo perfetta del minimo comune multiplo di gli allineamenti di tutti i membri della struttura o unione in questione.

Secondo questa regola, una volta aggiunto un doppio di 8 byte, il compilatore deve organizzare tutto su multipli di 8 byte. È possibile sovrascriverlo con #pragma pack(), naturalmente, sebbene risulti meno efficiente se si finisce con un membro di 8 byte su un valore diverso da un limite di 8 byte.

+1

No, non è così. L'unica cosa che dice la citazione è che a causa del 'double' member * l'intera struct * deve essere allineata al limite di 8 byte. Ma non spiega perché l'allineamento del membro intero è cambiato. Questa struttura dovrebbe adattarsi a 16 byte con tutti i requisiti di allineamento soddisfatti perfettamente. Ma il compilatore ha prodotto 24. Perché? – AnT

+0

È interessante notare che questa pagina di esempi di allineamento per il codice x64 in VC++ 8 mostra la stessa cosa con una struct contenente un int e un double, ma sfortunatamente non dice perché. http://msdn.microsoft.com/en-us/library/71kf49f1(VS.80).aspx Chiaramente, il compilatore sta scegliendo di allineare i membri sul minimo comune denominatore, che è 8, quindi la necessità dell'allineamento. Probabilmente spazio per l'interpretazione in quella frase. – aalpern

+0

aaplem: quel collegamento mostra la stessa cosa con un int e doppio. Probabilmente è perché vuoi che i doppi siano allineati a 8 byte. Ho sempre pensato che avere i doppi allineati a 4 byte sia super lento su x86. – Mason

2

This blog post discute lo stesso problema e include una spiegazione nei commenti di Jan Gray, che ha scritto il compilatore del codice di layout MS C++ molto tempo fa.

Per parafrasare, vfptr viene inserito nel layout di classe solo dopo che gli altri membri di dati sono stati disposti.A seconda dei requisiti di allineamento dei membri dei dati, ciò significa che potrebbe essere introdotto un riempimento non necessario. Anche questo succede solo se la classe non ha una classe base con un vfptr.

Di qui la soluzione, che viene presentato nel post del blog:

class EmptyBase 
{ 
protected: 
    virtual ~EmptyBase() {} 
}; 

class Something : public EmptyBase {  
    virtual void sampleVirtualMethod(); 
    int someInteger; 
    **double someDouble;** 
}; 

sizeof(Something) dovrebbe essere 16 in questo caso.