2011-01-03 10 views
7

Il seguente C++ legale presenta un comportamento ben definito?array di caratteri come spazio di archiviazione per il posizionamento nuovo

class my_class { ... }; 

int main() 
{ 
    char storage[sizeof(my_class)]; 
    new ((void *)storage) my_class(); 
} 

Oppure questo è problematico a causa di considerazioni di fusione puntatore/allineamento?

+0

Per me, va bene. –

+2

No, i puntini di sospensione non sono legali in quel contesto ... (Suggerimento: se vuoi chiedere se il codice è ben definito, deve prima compilare.) – GManNickG

+0

Qualcuno per favore dimmi qual è l'uso di fare sopra la cosa nel mondo dei pogrammi reali. – vrbilgi

risposta

12

Sì, è problematico. Semplicemente non hai alcuna garanzia che la memoria sia allineata correttamente.

Mentre esistono vari trucchi per ottenere l'archiviazione con l'allineamento corretto, è meglio usare Boost o C++ 0x aligned_storage, che nascondono questi trucchi da te.

Poi basta:

// C++0x 
typedef std::aligned_storage<sizeof(my_class), 
           alignof(my_class)>::type storage_type; 

// Boost 
typedef boost::aligned_storage<sizeof(my_class), 
         boost::alignment_of<my_class>::value>::type storage_type; 

storage_type storage; // properly aligned 
new (&storage) my_class(); // okay 

Si noti che in C++ 0x, utilizzando gli attributi, si può solo fare questo:

char storage [[align(my_class)]] [sizeof(my_class)]; 
+0

Grazie, questo è molto utile sapere. – bluescarni

+0

Scusa, non ancora del tutto :) Per quanto ho capito, il tipo di archiviazione è una sorta di tipo intero incorporato. Come lo uso? Posso semplicemente trasmettere liberamente il tipo my_class tramite void *? – bluescarni

+0

Scusa, volevo dire: "lanciare liberamente avanti e indietro da/verso i puntatori alle istanze my_class" ... O qualcosa del genere :) – bluescarni

2

È almeno problematico a causa dell'allineamento.

Nella maggior parte delle architetture non Intel, il codice genera un "errore bus" a causa di un allineamento errato o estremamente lento a causa di trap del processore necessari per correggere l'accesso di memoria non allineato.

Su architettura Intel questo sarà normalmente un po 'più lento del solito. Tranne se alcune operazioni SSE sono coinvolte, allora potrebbe anche bloccarsi.

0

L'array char potrebbe non essere allineato correttamente per la dimensione di myclass. Su alcune architetture, questo significa accessi più lenti, e su altri, significa un crash. Invece di char, dovresti usare un tipo il cui allineamento è uguale o maggiore di quello del struct, che è dato dal più grande requisito di allineamento di uno qualsiasi dei suoi membri.

#include <stdint.h> 

class my_class { int x; }; 

int main() { 
    uint32_t storage[size]; 
    new(storage) my_class(); 
} 

di allocare memoria sufficiente per una my_class esempio, penso che dovrebbe essere sizesizeof(my_class)/sizeof(T), dove T è il tipo che si usa per ottenere il corretto allineamento.

+0

"... il cui allineamento è uguale o maggiore di quello della struttura, che è dato dal più grande requisito di allineamento di uno qualsiasi dei suoi membri" Non penso che questo sia garantito, l'allineamento è totalmente definito dall'implementazione. (Ovvero, non è garantito che l'allineamento sia necessariamente l'allineamento del membro più rigido). Inoltre, 'int' potrebbe essere maggiore di 32-bit. – GManNickG

+0

@GMan: hai ragione. Funziona nella maggior parte dei casi, però. Qui, hai un link: http://stackoverflow.com/questions/364483/determining-the-alignment-of-c-c-structures-in-relation-to-its-members –

3

Come hanno già detto qui, questo non lo farà funziona necessariamente a causa di restrizioni di allineamento. Ci sono diversi modi per ottenere l'allineamento giusto. Innanzitutto, se si dispone di un compilatore C++ 0x compatibile, è possibile utilizzare l'operatore alignof per provare a forzare l'allineamento affinché sia ​​corretto. In secondo luogo, è possibile allocare dinamicamente l'array di caratteri, poiché la memoria dell'operatore new è garantita per essere allineata in modo che tutto possa essere utilizzata correttamente. Terzo, si può provare a memorizzare l'array di caratteri in un'unione con un tipo che ha il massimo allineamento possibile sul proprio sistema; Credo che this article abbia alcune informazioni su di esso (sebbene sia progettato per C++ 03 e non è certamente buono come l'operatore alignof che uscirà presto).

Spero che questo aiuti!

2

Nel caso in cui qualcuno volesse evitare Boost o C++ 1x, questo codice completo funziona sia in GCC che in MSVC. Il codice specifico MSVC è basato su Chromaum aligned_memory.h. È un po 'più complesso rispetto alla versione GCC, perché l'MSVC __declspec(align(.)) accetta solo valori di allineamento letterali e questo viene risolto utilizzando la specializzazione del modello per tutti i possibili allineamenti.

#ifdef _MSC_VER 

template <size_t Size, size_t Align> 
struct AlignedMemory; 

#define DECLARE_ONE_ALIGNED_MEMORY(alignment) \ 
template <size_t Size> \ 
struct __declspec(align(alignment)) AlignedMemory<Size, alignment> { \ 
    char mem[Size]; \ 
}; 

DECLARE_ONE_ALIGNED_MEMORY(1) 
DECLARE_ONE_ALIGNED_MEMORY(2) 
DECLARE_ONE_ALIGNED_MEMORY(4) 
DECLARE_ONE_ALIGNED_MEMORY(8) 
DECLARE_ONE_ALIGNED_MEMORY(16) 
DECLARE_ONE_ALIGNED_MEMORY(32) 
DECLARE_ONE_ALIGNED_MEMORY(64) 
DECLARE_ONE_ALIGNED_MEMORY(128) 
DECLARE_ONE_ALIGNED_MEMORY(256) 
DECLARE_ONE_ALIGNED_MEMORY(512) 
DECLARE_ONE_ALIGNED_MEMORY(1024) 
DECLARE_ONE_ALIGNED_MEMORY(2048) 
DECLARE_ONE_ALIGNED_MEMORY(4096) 

#else 

template <size_t Size, size_t Align> 
struct AlignedMemory { 
    char mem[Size]; 
} __attribute__((aligned(Align))); 

#endif 

template <class T> 
struct AlignedMemoryFor : public AlignedMemory<sizeof(T), __alignof(T)> {}; 
Problemi correlati