Question

Nous allons nous avons une structure simple (POD).

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

Puis-je suppose que le code suivant est OK? Est-ce que je suppose qu'il n'y a pas des lacunes? Ce que dit la norme? Est-il vrai pour PODs? Est-il vrai pour les classes?

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

La solution

La réponse est un peu délicat. Le standard C ++ dit que les types de données POD auront des garanties C mise en page de compatibilité ( Référence ). Selon la section 9.2 de la spécification C les membres d'une struct seront disposés dans un ordre séquentiel si

  1. Il n'y a pas de différence de modification de l'accessibilité
  2. Aucun problème d'alignement avec le type de données

Alors oui cette solution fonctionnera aussi longtemps que le type float a un alignement compatible sur la plate-forme actuelle (c'est la taille de texte de la plate-forme). Donc, cela devrait fonctionner pour les processeurs 32 bits mais je pense que ce serait un échec pour 64 les bits. Essentiellement partout où sizeof(void*) est différent sizeof(float)

Autres conseils

Ce n'est pas garanti par la norme, et ne fonctionnera pas sur de nombreux systèmes. Les raisons sont les suivantes:

  • Le compilateur peut aligner membres de la structure en fonction de la plate-forme cible, ce qui peut signifier un alignement 32 bits, 64 bits d'alignement, ou tout autre chose.
  • La taille du flotteur pourrait être 32 bits ou 64 bits. Il n'y a aucune garantie que c'est la même chose que l'alignement des membres struct.

Cela signifie que p[1] peut-être au même endroit que xyz.y, ou il pourrait se chevaucher partiellement, ou pas du tout.

Non, ce n'est pas OK pour le faire, sauf pour le premier champ.

A partir des normes C ++:

  

9.2 Les membres du groupe   Un pointeur vers un objet struct-POD,   converti de manière appropriée en utilisant un   reinterpret_cast, rappelle son   élément initial (ou si ce membre est un   champ binaire, puis à l'unité dans laquelle   elle se trouve), et vice versa. [Remarque:   Il pourrait donc être sans nom   rembourrage au sein d'un objet struct POD,   mais pas à son début, au besoin   pour parvenir à un alignement approprié.

dépend du matériel. La norme autorise explicitement les classes POD ont un rembourrage non spécifiée et imprévisible. Je l'ai constaté sur la page C ++ Wikipédia et ai saisi la note avec la référence de spécifications pour vous.

^ a b ISO / CEI (2003). ISO / CEI 14882: 2003 (E): Langages de programmation - C ++ §9.2 membres de la classe [class.mem] par. 17

En pratique, cependant, sur le matériel commun et compilateurs, il sera très bien.

En cas de doute, changer la structure de données en fonction de l'application:

struct xyz
{
    float  p[3];
};  

Pour faciliter la lecture, vous pouvez envisager:

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;}
};

Peut-être même ajouter un peu plus d'encapsulation:

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];
}

En général, si une structure de données doit être traitée en deux ou plusieurs façons différentes, peut-être la structure de données doit être repensée; ou le code.

La norme exige que l'ordre d'arrangement en mémoire correspondent à l'ordre de définition, mais permet padding arbitraire entre eux. Si vous avez un spécificateur d'accès (public:, private: ou protected:) entre les membres, même la garantie sur l'ordre est perdu.

Edit: dans le cas particulier des trois membres étant du même type primitif (c.-à-pas eux-mêmes struct ou quelque chose comme ça) vous avez une chance assez juste - pour les types primitifs, la taille de l'objet et les exigences d'alignement sont souvent les même, donc il fonctionne.

OTOH, ce n'est que par accident, et a tendance à être plus d'une faiblesse qu'une force; le code est erroné, donc, idéalement, il ne permettrait pas immédiatement au lieu d'apparaître au travail, jusqu'au jour où vous donnez une démo pour le propriétaire de l'entreprise qui va être votre client le plus important, au cours de laquelle il sera ( bien sûr) ne parviennent pas à la mode possible le plus odieux ...

Non, vous ne pouvez pas supposer qu'il n'y a pas de lacunes. Vous pouvez vérifier pour vous l'architecture, et s'il n'y a pas et vous ne se soucient pas de la portabilité, il sera OK.

Mais imaginez une architecture 64 bits avec des flotteurs 32 bits. Le compilateur peut aligner les flotteurs du struct sur les limites 64 bits, et votre

p[1]

vous donnera indésirable et

p[2]

vous donnera ce que vous pensez que votre obtenir de

p[1]

& c.

Cependant, vous compilateur peut vous donner une certaine façon d'emballer la structure. Il ne serait toujours pas « standard » --- la norme prévoit pas une telle chose, et différents compilateurs fournissent des moyens très incompatibles de le faire --- mais il est susceptible d'être plus portable.

Permet de jeter un oeil à code source 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;
}

Il fonctionne sur de nombreux systèmes.

Votre code est OK (tant qu'il ne gère que jamais les données générées dans le même environnement). La structure sera mis en mémoire telle que déclarée si elle est POD. Cependant, en général, il y a un Gotcha vous devez être au courant:. Le compilateur insère un rembourrage dans la structure pour assurer que les exigences en matière d'alignement de chaque membre sont obéis

avait été votre exemple

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

z aurait alors commencé à 8 octets dans la structure et sizeof (xyz) aurait été 12 comme floats sont (en général) 4 octets alignés.

De même, dans le cas

struct xyz
{
    float x;
    bool y;
};

sizeof (xyz) == 8, pour assurer ((xyz *) ptr) 1 renvoie un pointeur qui obéit aux exigences d'alignement de x.

Étant donné que la taille des exigences d'alignement / type peuvent varier entre les compilateurs / plates-formes, ce code n'est pas en général portable.

Comme d'autres l'ont souligné l'alignement est pas garanti par la spécification. Beaucoup disent qu'il est dépendant du matériel, mais en réalité il est également dépendant compilateur. Matériel peut prendre en charge de nombreux formats différents. Je me souviens que le support compilateur PPC pragma pour savoir comment « emballer » les données. Vous pouvez emballer sur les limites « indigènes » ou le forcer à les limites de 32 bits, etc.

Il serait bien de comprendre ce que vous essayez de faire. Si vous essayez de données d'entrée « analyser », vous êtes mieux avec un vrai analyseur. Si vous allez sérialisation, puis d'écrire un vrai sérialiseur. Si vous essayez de tourner les bits tels que pour un conducteur, la spécification de l'appareil devrait vous donner une carte mémoire spécifique pour écrire. Ensuite, vous pouvez écrire votre structure POD, spécifiez les pragmas d'alignement correct (si pris en charge) et passer à autre chose.

  1. garnissage de structure (par exemple #pragma pack dans MSVC) http://msdn.microsoft.com/en-us/library/aa273913%28v=vs.60%29.aspx
  2. alignement variables (Par exemple __declspec(align( dans MSVC) http://msdn.microsoft.com/en-us /library/83ythb65.aspx

sont deux facteurs qui peuvent ruiner vos hypothèses. flotteurs sont généralement 4 octets de large, il est donc rare de désalignement variables telles grandes. Mais il est toujours facile de casser votre code.

Ce problème est plus visible lorsque struct d'en-tête de lecture binaire avec un short (comme BMP ou TGA) -. Oublier pack 1 provoque une catastrophe

Je suppose que vous voulez un struct pour garder vos coordonnées accessibles en tant que membres (.x, .y et .z) mais vous voulez toujours qu'ils soient accessibles, disons, d'une manière OpenGL (comme si elle était un tableau).

Vous pouvez mettre en œuvre le [] opérateur du struct il est accessible comme un tableau. Quelque chose comme:

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;
    }
  }
};
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top