Domanda

Diamo abbiamo una struttura semplice (POD).

struct xyz
{
    float x, y, z;
};

Posso presumo che seguente codice è OK? Posso assume alcuna ci sono delle lacune? Ciò che la norma dice? E 'vero per i POD? E 'vero per le classi?

xyz v;
float* p = &v.x;
p[0] = 1.0f;
p[1] = 2.0f; // Is it ok?
p[2] = 3.0f; // Is it ok?
È stato utile?

Soluzione

La risposta qui è un po 'complicato. Lo standard C ++ dice che i tipi di dati POD avranno garanzie di layout C di compatibilità ( Riferimento ). Secondo la sezione 9.2 della specifica C i membri di una struct saranno disposte in ordine sequenziale, se

  1. Non ci sono differenze di accessibilità modificatore
  2. Non ci sono problemi di allineamento con il tipo di dati

Quindi sì questa soluzione funzionerà fino a quando il tipo float ha un allineamento compatibile sulla piattaforma corrente (è la dimensione della parola della piattaforma). Quindi questo dovrebbe funzionare per i processori a 32 bit, ma la mia ipotesi è che sarebbe fallire per quelli a 64 bit. In sostanza da nessuna parte che sizeof(void*) è diverso da quello sizeof(float)

Altri suggerimenti

Questa non è garantita dallo standard, e non funziona su molti sistemi. I motivi sono i seguenti:

  • Il compilatore può allineare membri della struttura appropriata per la piattaforma di destinazione, il che può significare allineamento 32 bit, allineamento 64 bit, o quant'altro.
  • Le dimensioni del galleggiante potrebbe essere 32 bit o 64 bit. Non c'è alcuna garanzia che è la stessa come l'allineamento membro struct.

Questo significa che p[1] potrebbe essere nella stessa posizione come xyz.y, o potrebbe sovrapporsi parzialmente, o non del tutto.

No, non va bene a farlo, tranne per il primo campo.

Dalle standard C ++:

  

9.2 I membri della classe
  Un puntatore a un oggetto POD-struct,   opportunamente convertito utilizzando un   reinterpret_cast, ricorda la sua   membro iniziale (o se quel membro è un   campo di bit, quindi all'unità in cui   risiede) e viceversa. [Nota:   Ci potrebbe quindi senza nome   imbottitura all'interno di un oggetto POD-struct,   ma non al suo inizio, se necessario   per raggiungere l'allineamento appropriato.

dipende dall'hardware. La norma consente in modo esplicito le classi POD di avere imbottitura non specificato e imprevedibile. Ho notato questo sulla pagina di Wikipedia C ++ e afferrato la nota con il riferimento specifiche per voi.

^ a b ISO / IEC (2003). ISO / IEC 14882: 2003 (E): Linguaggi di programmazione - C ++ §9.2 I membri della classe [class.mem] par. 17

In termini pratici, tuttavia, su hardware e compilatori comune andrà bene.

In caso di dubbio, cambiare la struttura dei dati in funzione dell'applicazione:

struct xyz
{
    float  p[3];
};  

Per facilitare la lettura si può prendere in considerazione:

struct xyz
{
    enum { x_index = 0, y_index, z_index, MAX_FLOATS};
    float p[MAX_FLOATS];

    float  X(void) const {return p[x_index];}
    float  X(const float& new_x) {p[x_index] = new_x;}

    float  Y(void) const {return p[y_index];}
    float  Y(const float& new_y) {p[y_index] = new_y;}

    float  Z(void) const {return p[z_index];}
    float  Z(const float& new_z) {p[z_index] = new_z;}
};

Forse anche aggiungere un po 'di incapsulamento:

struct Functor
{
  virtual void operator()(const float& f) = 0;
};

struct xyz
{
  void for_each(Functor& ftor)
  {
     ftor(p[0]);
     ftor(p[1]);
     ftor(p[2]);
     return;
  }
  private:
     float p[3];
}

In generale, se una struttura di dati deve essere trattata in due o più modi differenti, forse la struttura di dati deve essere riprogettata; oppure il codice.

Lo standard richiede che l'ordine di disposizione in memoria corrisponde all'ordine di definizione, ma permette imbottitura arbitraria tra loro. Se si dispone di uno specificatore di accesso (public:, private: o protected:) tra i membri, anche la garanzia su ordine perduto.

Edit: nel caso specifico di tutti e tre i membri di essere dello stesso tipo primitivo (cioè non si struct o qualcosa di simile) vi levate in piedi una bella buona possibilità - per i tipi primitivi, requisiti di dimensione e allineamento dell'oggetto sono spesso il stesso, in modo che funziona fuori.

OTOH, questo è solo per caso, e tende ad essere più di una debolezza che una forza; il codice è sbagliato, così idealmente fallirebbe immediatamente invece di apparire al lavoro, fino al giorno in cui si sta dando una demo per il proprietario della società che sta per essere il vostro cliente più importante, momento in cui lo farà ( ovviamente) fallire nel più atroce possibile moda ...

No, non si può dare per scontato che non ci siano spazi. Si può verificare la presenza di voi l'architettura, e se non ci sono e non si preoccupano di portabilità, sarà OK.

Ma immaginate un'architettura a 64-bit con galleggianti a 32 bit. Il compilatore può allineare i galleggianti della struct sui confini a 64 bit, e la vostra

p[1]

vi darà spazzatura, e

p[2]

vi darà ciò che pensate che il vostro ottenere da

p[1]

& c.

Tuttavia, è compilatore può dare qualche modo di mettere in valigia la struttura. Ancora non sarebbe "standard" --- lo standard prevede nulla di simile, e diversi compilatori forniscono modi molto incompatibili per farlo --- ma è probabile che sia più portabile.

Diamo un'occhiata al codice sorgente di Doom III:

class idVec4 {
public: 
    float           x;
    float           y;
    float           z;
    float           w;
    ...
    const float *   ToFloatPtr( void ) const;
    float *         ToFloatPtr( void );
    ...
}

ID_INLINE const float *idVec4::ToFloatPtr( void ) const {
    return &x;
}

ID_INLINE float *idVec4::ToFloatPtr( void ) {
    return &x;
}

Funziona su molti sistemi.

Il tuo codice è OK (purché sempre e solo gestisce i dati generati nello stesso ambiente). La struttura sarà disposto in memoria come dichiarato se è POD. Tuttavia, in generale, c'è un Gotcha è necessario essere a conoscenza di:. Il compilatore inserirà imbottitura nella struttura per garantire requisiti di allineamento di ciascun membro vengono rispettate

Se fosse stato il tuo esempio

struct xyz
{
    float x;
    bool y;
    float z;
};

z quindi sarebbe iniziare 8 byte nella struttura e sizeof (xyz) sarebbe stato 12 come floats sono (di solito) 4 byte allineati.

Analogamente, nel caso

struct xyz
{
    float x;
    bool y;
};

sizeof (xyz) == 8, al fine di garantire ((xyz *) PTR) +1 restituisce un puntatore che obbedisce requisiti di allineamento di x.

Poiché requisiti di allineamento / tipo dimensioni possono variare tra compilatori / piattaforme, tale codice non è in generale portatile.

Come altri hanno fatto notare l'allineamento non è garantito dalle specifiche. Molti dicono che è dipendente dall'hardware, ma in realtà è anche compilatore dipendente. Hardware può supportare diversi formati. Ricordo che i pragma supporto PPC compilatore su come "confezionare" i dati. Si potrebbe piantarla sui confini 'nativi' o costringerlo a 32 bit confini, ecc.

Sarebbe bello capire che cosa si sta cercando di fare. Se si sta tentando di 'analizzare' dati di input, si sta meglio con un vero e proprio parser. Se avete intenzione di serializzare, quindi scrivere un vero e proprio serializzatore. Se si sta cercando di girarsi bit, come per un pilota, quindi la specifica dispositivo dovrebbe darvi una mappa specifica memoria di scrivere. Poi si può scrivere la vostra struttura POD, specificare i pragma corretto allineamento (se supportato) e andare avanti.

  1. imballaggio struttura (ad es #pragma pack in MSVC) http://msdn.microsoft.com/en-us/library/aa273913%28v=vs.60%29.aspx
  2. allineamento variabile (Es __declspec(align( in MSVC) http://msdn.microsoft.com/en-us /library/83ythb65.aspx

sono due fattori che può rovinare le vostre ipotesi. galleggianti sono generalmente una larghezza di 4 byte, quindi è raro disallineare tali variabili grandi. Ma è ancora facile rompere il vostro codice.

Questo problema è più visibile quando binario lettura intestazione struct con pantaloncini (come BMP o TGA) -. Dimenticare pack 1 provoca un disastro

Presumo che si desidera una struttura per mantenere le vostre coordinate a cui si accede in qualità di membri (.x, .y e .Z), ma si desidera comunque loro di accedere, diciamo, un modo OpenGL (come se fosse un array).

Si può provare l'attuazione l'operatore [] del struct modo che possa essere letta come una matrice. Qualcosa di simile:

struct xyz
{
  float x, y, z;
  float& operator[] (unsigned int i)
  {
    switch (i)
    {
    case 0:
      return x;
      break;
    case 1:
      return y;
      break;
    case 2:
      return z;
      break;
    default:
      throw std::exception
      break;
    }
  }
};
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top