memcpy / memmove an ein Gewerkschaftsmitglied, setzt dies das 'aktive' Mitglied?

Wichtige Klarstellung: Einige Kommentatoren scheinen zu glauben, dass ich aus einer Gewerkschaft kopiere. Schau dir das @ genau memcpy, kopiert es von der Adresse eines einfachen altenuint32_t, das nicht in einer Union enthalten ist. Außerdem kopiere ich (übermemcpy) an ein bestimmtes Gewerkschaftsmitglied u.a16 oder&u.x_in_a_union, nicht direkt an die gesamte Union selbst &u)

C ++ ist in Bezug auf Gewerkschaften recht streng - Sie sollten nur dann von einem Mitglied lesen, wenn dies das letzte Mitglied war, an das geschrieben wurde:

9.5 Gewerkschaften [class.union] [[c ++ 11]] In einer Union kann zu jedem Zeitpunkt höchstens eines der nicht statischen Datenelemente aktiv sein, dh, der Wert von höchstens einem der nicht statischen Datenelemente kann zu jedem Zeitpunkt in einer Union gespeichert werden.

(Natürlich verfolgt der Compiler nicht, welches Mitglied aktiv ist. Es ist Sache des Entwicklers, sicherzustellen, dass sie dies selbst nachverfolgen.)

Update: Dieser folgende Codeblock ist die Hauptfrage und spiegelt direkt den Text im Fragentitel wider. Wenn dieser Code in Ordnung ist, habe ich eine Nachverfolgung in Bezug auf andere Typen, aber mir ist jetzt klar, dass dieser erste Codeblock selbst interessant ist.

#include <cstdint>
uint32_t x = 0x12345678;
union {
    double whatever;
    uint32_t x_in_a_union; // same type as x
} u;
u.whatever = 3.14;
u.x_in_a_union = x; // surely this is OK, despite involving the inactive member?
std::cout << u.x_in_a_union;
u.whatever = 3.14; // make the double 'active' again
memcpy(&u.x_in_a_union, &x); // same types, so should be OK?
std::cout << u.x_in_a_union; // OK here? What's the active member?

Der Code-Block direkt darüber ist wahrscheinlich das Hauptproblem in den Kommentaren und Antworten. Im Nachhinein musste ich in dieser Frage keine Typen mischen! Grundsätzlich istu.a = b das Gleiche wiememcpy(&u.a,&b, sizeof(b)), unter der Annahme, dass die Typen identisch sind?

Zunächst ein relativ einfachesmemcpy so dass wir ein @ lesen könnuint32_t als Array vonuint16_t:

#include <cstdint> # to ensure we have standard versions of these two types
uint32_t x = 0x12345678;
uint16_t a16[2];
static_assert(sizeof(x) == sizeof(a16), "");
std:: memcpy(a16, &x, sizeof(x));

Das genaue Verhalten hängt von der Endgültigkeit Ihrer Plattform ab, und Sie müssen sich vor Überfüllungsdarstellungen usw. hüten. Aber hier ist man sich einig (ich denke? Feedback erwünscht!), Dass der obige Code unter Vermeidung problematischer Werte im richtigen Kontext auf der richtigen Plattform eine perfekte Standard-Beschwerde sein kann.

(Wenn Sie ein Problem mit dem obigen Code haben, kommentieren oder bearbeiten Sie die Frage entsprechend. Ich möchte sichergehen, dass wir eine unumstrittene Version des obigen Codes haben, bevor Sie mit dem "interessanten" Code weiter unten fortfahren.)

Wenn,und nur wenn, beide obigen Codeblöcke sind nicht-UB, dann möchte ich sie wie folgt kombinieren:

uint32_t x = 0x12345678;
union {
    double whatever;
    uint16_t a16[2];
} u;
u.whatever = 3.14; // sets the 'active' member
static_assert(sizeof(u.a16) == sizeof(x)); //any other checks I should do?
std:: memcpy(u.a16, &x, sizeof(x));

// what is the 'active member' of u now, after the memcpy?
cout << u.a16[0] << ' ' << u.a16[1] << endl; // i.e. is this OK?

Welches Mitglied der Gewerkschaft,u.whatever oderu.a16, ist das 'aktive Mitglied'?

Finally, meine eigene Vermutung ist, dass der Grund, warum uns dies in der Praxis am Herzen liegt, darin besteht, dass ein optimierender Compiler möglicherweise nicht bemerkt, dass dasmemcpy ist passiert und macht daher falsche Annahmen (laut Standard jedoch zulässige Annahmen) darüber, welches Mitglied aktiv ist und welche Datentypen 'aktiv' sind, was zu Fehlern beim Aliasing führt. Der Compiler kann das @ neu anordnmemcpy auf seltsame Weise.Ist dies eine angemessene Zusammenfassung, warum uns das wichtig ist?

Antworten auf die Frage(8)

Ihre Antwort auf die Frage