Frage

Ich schreibe C Cross-Plattform-Bibliothek, aber schließlich habe ich bekomme Fehler in meinen Unittests, aber nur auf Windows-Rechnern. Ich habe das Problem verfolgt und fand es auf die Ausrichtung von Strukturen verwandt ist (ich bin Arrays von Strukturen unter Verwendung von Daten für mehrere ähnliche Gegenstände zu halten). Das Problem ist:. Memset (sizeof (struct)) und Einstellungsstrukturen Mitglieder eines nach dem anderen produzieren verschiedene Byte-zu-Byte-Ergebnis und damit memcmp () gibt "ungleich" Ergebnis

Hier ist der Code für die Darstellung:

#include <stdio.h>
#include <string.h>

typedef struct {
    long long      a;
    int            b;
} S1;

typedef struct {
    long           a;
    int            b;
} S2;

S1 s1, s2;

int main()
{
    printf("%d %d\n", sizeof(S1), sizeof(S2));

    memset(&s1, 0xFF, sizeof(S1));
    memset(&s2, 0x00, sizeof(S1));

    s1.a = 0LL; s1.b = 0;

    if (0 == memcmp(&s1, &s2, sizeof(S1)))
        printf("Equal\n");
    else
        printf("Not equal\n");

    return 0;
}

Dieser Code mit MSVC 2003 @ Windows-Produkten folgende Ausgabe:

16 8
Not equal

Aber der gleiche Code mit GCC 3.3.6 @ Linux funktioniert wie erwartet:

12 8
Equal

Das macht meine Komponententests sehr hart.

Bin ich das richtig verstanden, dass MSVC verwendet Größe der größten nativen Typ (long long) Ausrichtung zu bestimmen, zu strukturieren?

Kann jemand mir Ratschläge geben, wie kann ich den Code ändern, um es robuster gegen dieses seltsame Ausrichtungsproblem? In meinem realen Code arbeite ich mit Arrays von Strukturen über generische Zeiger Memset / memcmp auszuführen, und ich weiß in der Regel nicht genau Typen, ich habe nur sizeof (struct) Wert.

War es hilfreich?

Lösung

Was wir getan haben, ist das Pragma Pack verwendet, um festzulegen, wie groß sollten die Objekte sein:

#pragma pack(push, 2)

typedef struct {
    long long      a;
    int            b;
} S1;

typedef struct {
    long           a;
    int            b;
} S2;

#pragma pack(pop)

Wenn Sie dies tun, werden die Strukturen die gleiche Größe auf beiden Plattformen sein.

Andere Tipps

Ihr Unit-Test-Erwartung ist falsch. Es (oder der Code testet) sollte nicht die Puffer-Byte-für-Byte Struktur scannen. Für Byte-genaue Daten sollte der Code einen Byte-Puffer explizit auf Stapel erstellen oder auf Halde und füllt ihn mit den Extrakten von jedem Element. Die Extrakte können durch Verwendung der Verschiebeoperation nach rechts gegen die ganzzahligen Werte und Gießen des Ergebnisses durch die Byte-Typ, wie (unsigned char).

in CPU-Endian-unabhängige Art und Weise erhalten werden,

BTW, Ihr Snippet schreibt Vergangenheit s2. Sie konnten das Problem beheben, indem diese zu ändern

memset(&s2, 0x00, sizeof(S1));

s1.a = 0LL; s1.b = 0;

if (0 == memcmp(&s1, &s2, sizeof(S1)))

Dazu

memset(&s2, 0x00, sizeof(S2));

s1.a = 0LL; s1.b = 0;

if (0 == memcmp(&s1, &s2, sizeof(S2)))

, aber das Ergebnis ist technisch „undefiniert“, weil die Ausrichtung der Mitglieder in den Strukturen ist Compiler-spezifisch ist.

GCC Handbuch:

  

Beachten Sie, dass die Ausrichtung einer bestimmten Struktur oder Union-Typ durch die ISO-C-Standard erforderlich ist, mindestens ein perfektes Vielfaches des kleinsten gemeinsamen Vielfachen der Ausrichtungen von allen Mitgliedern der Struktur oder Vereinigung in Frage zu sein.

Auch dies führt typischerweise ein Element der Polsterung (d.h. Füllbytes, die Struktur haben, ausgerichtet ist). Sie können die #pragma mit dem Argument packed verwenden. Beachten Sie, #pragmas sind NICHT ein tragbares Arbeitsweise. Leider ist dies auch über die einzige Möglichkeit, in Ihrem Fall zu arbeiten.

Referenzen: Hier GCC auf Strukturausrichtung. MSDN Strukturausrichtung.

Beachten Sie, dass dies nicht ein ‚seltsames‘ Ausrichtungsproblem. MSVC hat, um sicherzustellen, daß die gewählte Struktur auf einer 64-Bit-Grenze ausgerichtet ist, da es ein 64-Bit-Element hat, so dass es am Ende der Struktur eines gewisse Polsterung fügt, um sicherzustellen, dass Arrays dieser Objekte werden jedes Element haben, richtig ausgerichtet ist. Ich bin eigentlich überrascht, dass GCC nicht das gleiche tun.

Ich bin gespannt, was Sie Unit-Tests sind macht das trifft einen Haken mit diesem - die meiste Zeit Ausrichtung der Strukturelemente ist nicht erforderlich, wenn Sie ein binäres Dateiformat oder ein Draht-Protokoll übereinstimmen müssen oder Sie wirklich brauchen der Speicher durch eine Struktur (insbesondere verwendet in eingebetteten Systemen) verwendet, zu reduzieren. Ohne zu wissen, was Sie versuchen, in Ihren Tests zu tun, kann ich glaube nicht, dass ein guter Vorschlag gegeben werden. die Struktur Verpackung könnte eine Lösung sein, aber es kommt zu einem gewissen Preis - Leistungs (vor allem auf nicht-Intel-Plattformen) und Portabilität (wie struct Verpackung aufgebaut ist, kann vom Compiler unterschiedlich sein Compiler). Diese können nicht wichtig für Sie, aber es könnte ein besserer Weg, um das Problem in Ihrem Fall zu lösen.

Sie können entweder so etwas wie

#ifdef _MSC_VER
#pragma pack(push, 16)
#endif

/* your struct defs */

#ifdef _MSC_VER
#pragma pack(pop)
#endif

eine Compiler-Direktive zwingt Ausrichtung geben

oder die Projektoptionen gehen und die Standard-Struktur Ausrichtung ändern [unter Codegenerierung]

Struktur Polsterung für 64-Bit-Werte unterscheidet sich auf verschiedene Compiler. Ich habe Unterschiede gesehen zwischen sogar zwischen gcc Zielen.

Beachten Sie, dass explizit auf Klotzen 64-Bit-Ausrichtung wird das Problem nur verstecken. Es wird wieder kommen, wenn Sie naiv Verschachtelung Strukturen beginnen, weil die Compiler noch auf der natürlichen Ausrichtung der inneren Strukturen nicht zustimmen werden.

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