Pregunta

Vamos, tenemos una estructura simple (POD).

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

¿Debo asumir que el siguiente código está bien?¿Debo asumir que no hay ningún hueco?Lo que la norma dice?Es cierto para las Vainas?Es cierto para las clases?

xyz v;
float* p = &v.x;
p[0] = 1.0f;
p[1] = 2.0f; // Is it ok?
p[2] = 3.0f; // Is it ok?
¿Fue útil?

Solución

La respuesta en este caso es un poco complicado. El estándar de C ++ dice que los tipos de datos POD tendrán garantías de compatibilidad (diseño C Referencia ). Según la sección 9.2 de la especificación C los miembros de una estructura quedan dispuestas en orden secuencial si

  1. No hay diferencia modificador de accesibilidad
  2. No hay problemas de alineación con el tipo de datos

Así que sí esta solución funcionará siempre y cuando el tipo float tiene una alineación compatible en la plataforma actual (es el tamaño de la palabra de la plataforma). Por lo que este debe trabajar para procesadores de 32 bits, pero mi conjetura es que éste deje de los 64 bits. Esencialmente cualquier lugar que sizeof(void*) es diferente que sizeof(float)

Otros consejos

Esto no está garantizado por la norma, y no funciona en muchos sistemas.Las razones son:

  • El compilador puede alinear los miembros de una estructura como apropiado para la plataforma de destino, lo que puede significar de 32 bits de alineación, de 64 bits de alineación, o cualquier otra cosa.
  • El tamaño de la flotación puede ser de 32 bits o de 64 bits.No hay ninguna garantía de que es la misma como la estructura miembro de la alineación.

Esto significa que p[1] podría ser en el mismo lugar xyz.y, o es posible que se superponen parcialmente, o no del todo.

No, no está bien para hacerlo, excepto para el primer campo.

A partir de los estándares de C ++:

  

9.2 Los miembros del grupo
  Un puntero a un objeto POD-estructura,   convenientemente convertidos usando una   reinterpret_cast, apunta a su   miembro inicial (o si ese miembro es una   campo de bits, a continuación, a la unidad en la que   reside) y viceversa. [Nota:   Por lo tanto, puede haber sin nombre   relleno dentro de un objeto POD-struct,   pero no en su comienzo, según sea necesario   para lograr la alineación apropiada.

Depende del hardware. La norma permite explícitamente a las clases POD tienen acolchado indeterminado e impredecible. Noté esto en la página C ++ Wikipedia y agarró el pie de página con la referencia de especificaciones para usted.

^ a b ISO / IEC (2003). ISO / IEC 14882: 2003 (E): Lenguajes de Programación - C ++ §9.2 miembros de la Clase [class.mem] párr. 17

En la práctica, sin embargo, el hardware y los compiladores común que va a estar bien.

En caso de duda, cambiar la estructura de datos para adaptarse a la aplicación:

struct xyz
{
    float  p[3];
};  

Para facilitar la lectura es posible que desee considerar lo siguiente:

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

Tal vez incluso añadir un poco más de encapsulación:

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 general, si una estructura de datos necesita ser tratado en dos o más formas diferentes, tal vez la estructura de datos necesita ser rediseñada; o el código.

La norma requiere que el orden de disposición en la memoria coincide con el orden de definición, pero permite el relleno arbitraria entre ellos. Si usted tiene un modificador de acceso (public:, private: o protected:) entre los miembros, incluso la garantía sobre el orden se pierde.

Edit: en el caso específico de los tres miembros siendo del mismo tipo primitivo (es decir, no a sí mismos estructuras o algo por el estilo) tiene una posibilidad bastante justo - para los tipos primitivos, los requisitos de tamaño y alineación del objeto son a menudo el mismo, así que funciona.

Otoh, esto es sólo por accidente, y tiende a ser más de una debilidad de una fuerza; el código es incorrecto, por lo que lo ideal sería un fracaso inmediato en lugar de aparecer a trabajar, hasta el día en que usted está dando una demostración para el propietario de la empresa que va a ser su cliente más importante, momento en el que lo hará ( por supuesto) fallan en la forma más atroz posible ...

No, no se puede asumir que no hay huecos. Usted puede comprobar por ti arquitectura, y si no son y no se preocupan por la portabilidad, que estará bien.

Pero imaginar una arquitectura de 64 bits con flotadores de 32 bits. El compilador puede alinear los flotadores de la struct en los límites de 64 bits, y su

p[1]

le dará chatarra, y

p[2]

le dará lo que usted piensa de su hacer de

p[1]

& c.

Sin embargo, compilador puede darle una cierta manera de embalar la estructura. Todavía no sería "estándar" --- la norma no tal cosa, y diferentes compiladores proporcionan maneras muy incompatibles de hacer esto --- pero es probable que sea más portátil.

Vamos a echar un vistazo a código fuente de 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;
}

Funciona en muchos sistemas.

Su código está bien (con tal de que solamente siempre maneja los datos generados en el mismo entorno). La estructura será expuesto en la memoria como si es declarado POD. Sin embargo, en general, hay una Gotcha que necesita para tener en cuenta:. El compilador insertará el relleno en la estructura para asegurar la alineación se cumplen los requisitos de cada miembro

¿Había sido su ejemplo

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

entonces z habría comenzó 8 bytes en la estructura y sizeof (xyz) habría sido 12 como floats son (en general) de 4 bytes alineados.

Del mismo modo, en el caso

struct xyz
{
    float x;
    bool y;
};

sizeof (xyz) == 8, para asegurar ((XYZ *) PTR) 1 devuelve un puntero que obedece a los requerimientos de alineación de x.

Desde tamaños requisitos de alineación / tipo pueden variar entre los compiladores / plataformas, tal código no es en portable general.

Como otros han señalado la alineación no está garantizada por la especificación. Muchos dicen que es dependiente del hardware, pero en realidad también es dependiente del compilador. Hardware puede soportar muchos formatos diferentes. Recuerdo que los pragmas apoyo PPC compilador para la forma de "paquete" de los datos. Por ejemplo para empaquetar en las fronteras marcadas 'nativos' o forzarlo a 32 fronteras de bits, etc.

Sería bueno para entender lo que está tratando de hacer. Si usted está tratando de 'analizar' los datos de entrada, que está mejor con un programa de análisis real. Si se va a serializar, a continuación, escribir un serializador real. Si usted está tratando de girar los trozos como para un conductor, entonces el dispositivo de especificación debe darle un mapa de memoria específica a escribir. A continuación, puede escribir su estructura POD, especifique los pragmas alineación correcta (si es compatible) y seguir adelante.

  1. estructura de embalaje (por ejemplo #pragma pack en MSVC) http://msdn.microsoft.com/en-us/library/aa273913%28v=vs.60%29.aspx
  2. alineación variable de (Por ejemplo __declspec(align( en MSVC) http://msdn.microsoft.com/en-us /library/83ythb65.aspx

son dos factores que pueden arruinar sus suposiciones. flotadores son por lo general de 4 bytes de ancho, por lo que es poco frecuente desalinear tales variables grandes. Pero aún así es fácil de romper el código.

Este problema es más visible cuando struct cabecera lectura binaria con cortos (como BMP o TGA) -. Olvidar pack 1 provoca un desastre

Asumo que quiere una estructura para mantener sus coordenadas se accede como miembros (.x, .y y .z), pero aún quiere pueda acceder a ellos, digamos, una forma de OpenGL (como si fuera una matriz).

Puede intentar implementar el operador [] de la estructura por lo que se puede acceder como una matriz. Algo así como:

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;
    }
  }
};
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top