Frage

Lassen Sie uns wir haben eine einfache Struktur (POD).

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

May Ich gehe davon aus, dass folgender Code in Ordnung ist? Darf ich davon ausgehen, gibt es keine Lücken? Was die Norm sagt? Ist es wahr, für PODs? Ist es wahr, für die Klassen?

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

Lösung

Die Antwort ist ein bisschen schwierig. Der C ++ Standard sagt, dass POD Datentypen Kompatibilität C Layout Garantien haben ( Referenz ). Gemäß Abschnitt 9.2 der C-Spezifikation der Mitglieder einer Struktur in der angegebenen Reihenfolge verlegt werden, wenn

  1. Es gibt keine Zugänglichkeit Modifikator Unterschied
  2. Keine Ausrichtungsprobleme mit dem Datentyp

Also ja diese Lösung funktioniert, solange die Art float eine kompatible Ausrichtung auf der aktuellen Plattform hat (es ist die Plattform Wortgröße). So soll dies für 32-Bit-Prozessoren arbeiten, aber meine Vermutung ist, dass es für 64-Bit-diejenigen ausfallen würde. Im Wesentlichen überall, dass sizeof(void*) ist anders als sizeof(float)

Andere Tipps

Dies ist nicht durch den Standard garantiert, und nicht auf vielen Systemen arbeiten. Die Gründe dafür sind:

  • Der Compiler kann struct Elemente als geeignet für die Zielplattform auszurichten, die 32-Bit-Ausrichtung kann bedeuten, 64-Bit-Ausrichtung oder irgendetwas anderes.
  • Die Größe des Schwimmers kann 32 Bit oder 64 Bit betragen. Es gibt keine Garantie, dass es das gleiche wie die Struktur Mitglied Ausrichtung ist.

Das bedeutet, dass p[1] an der gleichen Stelle wie xyz.y sein könnte, oder es könnte teilweise oder gar nicht überlappen.

Nein, es ist nicht in Ordnung, dies zu tun, außer für das erste Feld.

Von dem C ++ Standard:

  

9.2 Klassenmitglieder
  Ein Zeiger auf ein POD-struct Objekt,   in geeigneter Weise umgewandelt, um eine Verwendung von   reinterpret_cast, verweist auf seine   Ausgangselement (oder, wenn das Element ein   Bit-Feld, dann an die Einheit, in der   es sich befindet), und vice versa. [Hinweis:   Es könnte daher sein unnamed   Klotzen innerhalb eines POD-struct Objekt,   aber nicht am Anfang, wie nötig   geeignete Ausrichtung zu erreichen.

Abhängig von der Hardware. Der Standard ermöglicht es explizit POD Klassen nicht spezifiziert und unberechenbar Polsterung zu haben. Ich bemerkte dies auf der C ++ Wikipedia-Seite und griff nach der Fußnote mit der Spezifikation Referenz für Sie.

^ a b ISO / IEC (2003). ISO / IEC 14882: 2003 (E): Programmiersprachen - C ++ §9.2 Klasse Mitglieder [class.mem] Abs. 17

In der Praxis jedoch auf gemeinsame Hardware und Compiler wird es in Ordnung sein.

Im Zweifelsfall die Datenstruktur ändern, um die Anwendung zu entsprechen:

struct xyz
{
    float  p[3];
};  

Zur besseren Lesbarkeit Sie können zu prüfen,:

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

Vielleicht sogar etwas mehr Verkapselung hinzufügen:

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 der Regel, wenn eine Datenstruktur muss in zwei oder mehr verschiedenen Arten behandelt werden, vielleicht braucht die Datenstruktur neu gestaltet werden; oder der Code.

Der Standard verlangt, dass die Reihenfolge der Anordnung im Speicher die Reihenfolge der Definition entspricht, erlaubt aber beliebiges padding zwischen ihnen. Wenn Sie einen Zugriffsbezeichner haben (public:, private: oder protected:) zwischen den Mitgliedern, auch die Garantie für Ordnung verloren.

Edit: im konkreten Fall von allen drei Mitgliedern des gleichen primitiven Typs sein (also nicht selbst Structs oder ähnlichem) können Sie eine ziemlich gute Chance - für primitive Typen, die Größe des Objekts und Ausrichtungsanforderungen sind oft die gleiche, es funktioniert so aus.

OTOH, dass dies nur durch Zufall, und neigt dazu, als eine Stärke eher eine Schwäche zu sein; der Code ist falsch, so im Idealfall würde es sofort statt erscheinen nicht bis zum Tag der Arbeit, dass Sie eine Demo für den Eigentümer des Unternehmens sind zu geben, die Ihre wichtigsten Kunden sein werden, zu welcher Zeit es wird ( natürlich) nicht in den abscheulichsten möglich Mode ...

Nein, Sie können nicht davon ausgehen, dass es keine Lücken gibt. Sie können für Sie Architektur überprüfen, und wenn es nicht und Sie kümmern sich nicht um die Portabilität, wird es in Ordnung sein.

Aber stellen Sie eine 64-Bit-Architektur mit 32-Bit-Floats. Der Compiler kann die Struktur der Schwimmer auf 64-Bit-Grenzen ausrichten und Ihre

p[1]

geben Sie Junk, und

p[2]

wird dir geben, was Sie denken, dass Ihr davon ab,

p[1]

& c.

Allerdings Compiler Sie können geben Sie eine Möglichkeit, um die Struktur zu packen. Es wäre immer noch nicht „Standard“ --- die Norm enthält keine solche Sache, und verschiedene Compiler bieten sehr unverträglich Möglichkeiten dies zu tun --- aber es ist wahrscheinlich mehr tragbar sein.

Werfen wir einen Blick auf Doom III Quellcode nehmen:

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

Es funktioniert auf vielen Systemen.

Ihr Code in Ordnung ist (so lange, wie es immer nur Daten in der gleichen Umgebung erzeugt Griffe). Die Struktur wird im Speicher werden wie erklärt angelegt, wenn es POD. Aber im Allgemeinen gibt es ein Gotcha Sie sich bewusst sein müssen. Der Compiler Polsterung in die Struktur einfügen jedes Mitglied Ausrichtungsanforderungen eingehalten werden, um sicherzustellen,

Wäre Ihr Beispiel gewesen

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

dann hätte z begann 8 Bytes in die Struktur und sizeof (xyz) würde 12 wurden als floats sind (in der Regel) 4-Byte-ausgerichtet sind.

Auch im Fall

struct xyz
{
    float x;
    bool y;
};

sizeof (xyz) == 8, um sicherzustellen, ((xyz *) ptr) 1 gibt einen Zeiger, dass gehorcht x die Ausrichtungsanforderungen.

Da Ausrichtungsanforderungen / Schriftgrößen zwischen Compilern / Plattformen, wie Code unterschiedlich sein können, ist im allgemeinen nicht tragbar.

Wie andere haben die Ausrichtung darauf hingewiesen wird, nicht von der Spezifikation gewährleistet. Viele sagen, es ist hardwareabhängig, aber eigentlich ist es auch Compiler abhängig. Hardware kann viele verschiedene Formate unterstützen. Ich erinnere mich, dass die PPC Compiler-Unterstützung pragmas dafür, wie die Daten zu „packen“. Man könnte es auf ‚native‘ Grenzen packen oder es zu 32-Bit-Grenzen erzwingen, etc.

Es wäre schön, zu verstehen, was Sie zu tun versuchen. Wenn Sie auf ‚Parse‘ Eingangsdaten versuchen, sind Sie besser dran mit einem echten Parser. Wenn Sie serialisiert werden, dann einen echten Serializer schreiben. Wenn Sie versuchen, Bits zu drehen, wie für einen Fahrer, dann sollte das Gerät spec geben Sie eine bestimmte Speicherkarte zu schreiben. Dann können Sie Ihre POD Struktur schreiben, geben Sie die korrekte Ausrichtung Pragmas (falls unterstützt) und ziehen weiter.

  1. Struktur Verpackung (zB #pragma pack in MSVC) http://msdn.microsoft.com/en-us/library/aa273913%28v=vs.60%29.aspx
  2. variable Ausrichtung (ZB __declspec(align( in MSVC) http://msdn.microsoft.com/en-us /library/83ythb65.aspx

sind zwei Faktoren, die Ihre Annahmen zunichte machen können. Schwimmer ist in der Regel 4 Bytes breit, so ist es selten so große Variablen misalign. Aber es ist immer noch leicht Ihren Code zu brechen.

Dieses Problem ist am deutlichsten sichtbar, wenn Binär-Lesekopf Struktur mit kurzen Hosen (wie BMP oder TGA) -. Vergessen pack 1 eine Katastrophe verursacht

Ich nehme an, Sie wollen eine Struktur Ihre Koordinaten als Mitglieder (.x, .y und .z) zugegriffen zu halten, aber Sie wollen, dass sie immer noch zugegriffen werden, sagen wir mal, eine OpenGL-Weg (als ob es sich um ein Array war).

Sie können versuchen, den Operator [] der Struktur der Umsetzung so kann es als ein Array zugegriffen werden. So etwas wie:

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;
    }
  }
};
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top