2013-11-26 8 views
6

È il seguente C++ valido? È un modo alternativo per implementare una coda a lunghezza variabile a una struttura piatta. In C questo viene comunemente fatto con il struct hackSi tratta di un'alternativa C++ alla struct hack?

struct Str 
{ 
    Str(int c) : count(c) {} 
    size_t count; 
    Elem* data() { return (Elem*)(this + 1); } 
}; 

Str* str = (Str*)new char[sizeof(Str) + sizeof(Elem) * count]; 
new (str) Str(count); 
for (int i = 0; i < count; ++i) 
    new (str->data() + i) Elem(); 
str->data()[0] = elem0; 
str->data()[1] = elem1; 
// etc... 

Lo chiedo in risposta al seguente related question

+1

credo che questo non funzionerà se 'Elem' ha allineamento diverso da' Str'. – Henrik

+1

Questo è facilmente risolto: 'union {size_t count, Elem dummy; } ' – MSalters

+0

Se vuoi essere davvero fantasioso, usa la metaprogrammazione del modello per rendere' dummy' il più piccolo di 'Elem' e' std :: max_align_t'. – MSalters

risposta

2

No, non è valido:

Elem potrebbe avere l'allineamento diverso rispetto Str, così (reinterpretare_) la trasmissione di Str+1 a Elem* potrebbe o non potrebbe fornire un puntatore valido e l'acccessing potrebbe dare un comportamento indefinito.

Ma dopo tutto, perché vorresti fare qualcosa del genere?

+0

Perché? Hackery di basso livello. Ma sembra che l'allineamento sia un problema, grazie. – john

+0

Il solito motivo è la località di riferimento. Vedi anche 'std :: make_shared'. Inoltre, IIRC 'new char []' è opportunamente allineato per tutti i tipi (per poter funzionare come sostituto di 'malloc') – MSalters

+0

@MSalters:' new char [] 'è opportunamente allineato, ma dopo un offset di' Str 'tutte le scommesse sono spente. –

0

valido in che senso? È C++ che usa tecniche simili a C che imho va bene finché i requisiti del progetto non lasciano altra scelta. Se stai chiedendo se funzionerà, funzionerà finché i problemi di allineamento dei dati non causeranno il crash del codice (ad esempio non x86 come SPARC, ecc.). C++ si comporta molto come C quando si indirizza la memoria.

ho testato utilizzando le seguenti modifiche sotto gcc e VS e funziona:

struct Elem 
{ 
Elem() : x(0), t(0) { memset(c, 0, sizeof(c));} 
Elem(int v) : x(v), t(0) { memset(c, 0, sizeof(c));} 
Elem(const Elem &e) { *this = e; } 

Elem &operator=(const Elem &e) 
{ 
    if (this != &e) 
    { 
     memcpy(c, e.c, sizeof(c)); 
     x = e.x; 
     t = e.t; 
    } 
    return *this; 
} 

char c[21]; 
int x; 
char t; 
}; 

struct Str 
{ 
    Str(int c) : count(c) {} 
    size_t count; 
    Elem* data() { return (Elem*)(this + 1); } 
}; 

int count = 11; 

Str *str = (Str*)new char[sizeof(Str) + sizeof(Elem) * count]; 
new (str) Str(count); 
for (int i = 0; i < count; ++i) 
{ 
    new (str->data() + i) Elem(); 
    str->data()[i] = Elem(i+1); 
} 

for (int i=0; i<str->count; i++) 
    cout << "[" << i << "]: " << str->data()[i].x << endl; 

Inoltre, ho aggiunto vari membri di dimensioni diverse per Str e Elem forzare imbottitura diversa e suonato con allineamenti (VS/some GCC: #pragma pack (...), GCC: __ attribute__ ((align (...))) e, __ attribute __ (packed)).

Si prega di notare che giocare con allineamenti non è sicuro su tutte le architetture - Relevant question