Frage

Bei einer Codeüberprüfung bin ich auf Code gestoßen, der eine einfache Struktur wie folgt definiert:

class foo {
   unsigned char a;
   unsigned char b;
   unsigned char c;
}

An anderer Stelle ist ein Array dieser Objekte definiert:

foo listOfFoos[SOME_NUM];

Später werden die Strukturen roh in einen Puffer kopiert:

memcpy(pBuff,listOfFoos,3*SOME_NUM);

Dieser Code basiert auf den Annahmen, dass:a.) Die Größe von foo beträgt 3 und es wird kein Abstand angewendet, und b.) Ein Array dieser Objekte wird ohne Abstand dazwischen gepackt.

Ich habe es mit GNU auf zwei Plattformen versucht (RedHat 64b, Solaris 9) und es hat auf beiden Plattformen funktioniert.

Sind die oben genannten Annahmen gültig?Wenn nicht, unter welchen Bedingungen (z.B.(Änderung im Betriebssystem/Compiler) könnten sie scheitern?

War es hilfreich?

Lösung

Ein Array von Objekten erforderlich ist angrenzend zu sein, so dass es nicht zwischen den Objekten Klotzen, kann, obwohl padding zum Ende eines Objekts hinzugefügt werden (fast die gleiche Wirkung erzeugt).

Da Sie mit Chars arbeiten, sind die Annahmen wahrscheinlich recht häufiger als nicht, aber die C ++ Standard garantiert sicherlich nicht. Eine andere Compiler, oder auch nur eine Änderung der zu Ihrem aktuellen Compiler übergeben Fahnen in padding führen könnte zwischen den Elementen der Struktur eingeführt werden oder im Anschluss an das letzte Element der Struktur, oder beides.

Andere Tipps

Es wäre auf jeden Fall sicherer zu tun:

sizeof(foo) * SOME_NUM

Wenn Sie Ihr Array wie folgt kopieren sollten Sie verwenden

memcpy(pBuff,listOfFoos,sizeof(listOfFoos));

Das wird immer funktionieren, solange Sie pBuff auf die gleiche Größe zugeordnet. Auf diese Weise Sie machen keine Annahmen über Polsterung und Ausrichtung überhaupt.

Die meisten Compiler richten eine Struktur oder Klasse auf die gewünschte Ausrichtung des größten Typs enthalten. In Ihrem Fall von Zeichen bedeutet, dass keine Ausrichtung und Polsterung, aber wenn Sie einen kurzen hinzufügen zum Beispiel Ihrer Klasse 6 Bytes groß und mit einem Byte padding hinzugefügt würde zwischen dem letzten Zeichen und Ihrem kurz.

Ich denke, der Grund, dass dies funktioniert, weil alle Felder in der Struktur char sind, die man ausrichten. Wenn es mindestens ein Feld ist, das nicht 1 auszurichten ist, wird die Ausrichtung der Struktur / Klasse 1 nicht (die Ausrichtung wird auf dem Feld abhängt Reihenfolge und Ausrichtung).

Lassen Sie einige Beispiele finden Sie unter:

#include <stdio.h>
#include <stddef.h>

typedef struct {
    unsigned char a;
    unsigned char b;
    unsigned char c;
} Foo;
typedef struct {
    unsigned short i;
    unsigned char  a;
    unsigned char  b;
    unsigned char  c;
} Bar;
typedef struct { Foo F[5]; } F_B;
typedef struct { Bar B[5]; } B_F;


#define ALIGNMENT_OF(t) offsetof( struct { char x; t test; }, test )

int main(void) {
    printf("Foo:: Size: %d; Alignment: %d\n", sizeof(Foo), ALIGNMENT_OF(Foo));
    printf("Bar:: Size: %d; Alignment: %d\n", sizeof(Bar), ALIGNMENT_OF(Bar));
    printf("F_B:: Size: %d; Alignment: %d\n", sizeof(F_B), ALIGNMENT_OF(F_B));
    printf("B_F:: Size: %d; Alignment: %d\n", sizeof(B_F), ALIGNMENT_OF(B_F));
}

Wenn er ausgeführt wird, ist das Ergebnis:

Foo:: Size: 3; Alignment: 1
Bar:: Size: 6; Alignment: 2
F_B:: Size: 15; Alignment: 1
B_F:: Size: 30; Alignment: 2

Sie können diese Bar sehen und F_B hat Ausrichtung 2, so dass sein Feld i richtig ausgerichtet werden. Sie können auch die Größe der Bar finden Sie unter 6 und nicht mehr als 5 . In ähnlicher Weise ist die Größe der B_F (5 Bar) 30 und nicht mehr als 25 .

Wenn Sie also ein harter Code statt sizeof(...) ist, werden Sie ein Problem hier.

Hope, das hilft.

Es kommt alles auf Speicherausrichtung. Typische 32-Bit-Maschinen lesen oder zu schreiben 4 Byte Speicher pro Versuch. Diese Struktur ist sicher vor Problemen, weil es unter diesem 4 Bytes fällt leicht ohne verwirrende padding Fragen.

Wenn nun die Struktur war als solche:

class foo {
   unsigned char a;
   unsigned char b;
   unsigned char c;
   unsigned int i;
   unsigned int j;
}

Ihre Mitarbeiter Logik würde wahrscheinlich führen zu

memcpy(pBuff,listOfFoos,11*SOME_NUM);

(3 Char = 3 Bytes, 2 ints = 2 · 4 Bytes, SO 3 + 8)

Leider wegen der Struktur padding tatsächlich 12 Byte in Anspruch nimmt. Dies liegt daran, dass Sie nicht mehr als drei Chars und einen int in das 4-Byte-Wort passen, und so gibt es ein Byte gepolsterten Raum gibt, die den int in seinem eigenen Wort drückt. Dies wird mehr und mehr ein Problem Je vielfältiger die Datentypen werden.

Für Situationen, in denen Sachen wie diese verwendet werden, und ich kann es nicht vermeiden, ich versuche, die Kompilation Pause zu machen, wenn die Vermutungen nicht mehr halten. Ich benutze so etwas wie die folgenden (oder Boost.StaticAssert , wenn es die Situation erlaubt):

static_assert(sizeof(foo) <= 3);

// Macro for "static-assert" (only usefull on compile-time constant expressions)
#define static_assert(exp)           static_assert_II(exp, __LINE__)
// Macro used by static_assert macro (don't use directly)
#define static_assert_II(exp, line)  static_assert_III(exp, line)
// Macro used by static_assert macro (don't use directly)
#define static_assert_III(exp, line) enum static_assertion##line{static_assert_line_##line = 1/(exp)}

Ich hätte schon sicher und ersetzt die magische Zahl 3 mit einem sizeof(foo) ich rechnen.

Meine Vermutung ist, dass Code für zukünftige Prozessorarchitekturen optimiert werden wahrscheinlich irgendeine Form von padding einzuführen.

Und versucht, diese Art von Fehlern aufzuspüren sind ein echten Schmerzen!

Wie bereits gesagt wurde, unter Verwendung von sizeof (foo) ist eine sicherere Wette. Einige Compiler (vor allem esoterisch diejenigen in dem embedded world) fügen ein 4-Byte-Header-Klassen. Andere können flippige Speicher-Ausrichtung Tricks, abhängig von Ihren Compiler-Einstellungen.

Für eine Mainstream-Plattform, sind Sie wahrscheinlich in Ordnung, aber es ist keine Garantie.

Möglicherweise liegt weiterhin ein Problem mit sizeof() vor, wenn Sie Daten zwischen zwei Computern übertragen.Auf einem von ihnen könnte der Code mit Auffüllung kompiliert werden, auf dem anderen ohne, wobei sizeof() in diesem Fall unterschiedliche Ergebnisse liefern würde.Wenn die Array-Daten von einem Computer zum anderen weitergegeben werden, werden sie falsch interpretiert, da die Array-Elemente nicht dort gefunden werden, wo sie erwartet werden.Eine Lösung besteht darin, sicherzustellen, dass wann immer möglich #pragma pack(1) verwendet wird, aber das reicht für die Arrays möglicherweise nicht aus.Am besten ist es, das Problem vorherzusehen und eine Auffüllung auf ein Vielfaches von 8 Bytes pro Array-Element zu verwenden.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top