C
otze schrieb:
meines wissens nach sind nur zugriffe auf unions wohldefiniert, die einen Wert in der Form auslesen, wie er eingelesen wurde. Sprich, wenn ein word eingelesen wurde, muss ein word ausgelesen werden, nicht ein char oder was auch immer. Das ist ja das große Problem bei unions, dass all diese schönen sachen komplett undefiniert sind.
Das steht aber so nirgendwo im Standard und ich denke, man kann es auch nicht durch Auslegung herausbekommen.
ISO 14882:2003 schrieb:
9.5 Unions [class.union]
1 In a union, at most one of the data members can be active at any time, that is, the value of at most one of the data members can be stored in a union at any time. [Note: one special guarantee is made in order to simplify the use of unions: If a POD-union contains several POD-structs that share a common initial sequence (9.2), and if an object of this POD-union type contains one of the POD-structs, it is permitted to inspect the common initial sequence of any of POD-struct members; see 9.2. ] The size of a union is sufficient to contain the largest of its data members. Each data member is allocated as if it were the sole member of a struct.
Dort steht nichts dazu, wie man auf den gespeicherten Wert zugreifen kann (da steht zwar in dem Verweis etwas von "inspect", nicht aber wie dies zu geschehen hat). Um das herauszufinden, müssen wir an einer ganz anderen Stelle suchen:
ISO 14882:2003 schrieb:
3.10/15
If a program attempts to access the stored value of an object through an lvalue of other than one of the following types the behavior is undefined48):
— the dynamic type of the object,
— a cv-qualified version of the dynamic type of the object,
— a type that is the signed or unsigned type corresponding to the dynamic type of the object,
— a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type of the object,
— an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union),
— a type that is a (possibly cv-qualified) base class type of the dynamic type of the object,
— a char or unsigned char type.
Das ist zunächst einmal eine Regel, die uns sagt, wann etwas nicht definiert ist. Dafür müssen wir wiederum woanders schauen - uns interessieren hier nur char&co., dafür muss man 3.9/4 heranziehen. Betrachten wir das Ganze jetzt aus dem Blickwinkel von union-Membern wird plötzlich klar, warum dort diese merkwürdige Regel des nur-eine-Wert-gleichzeitig-aktiv steht; ohne diese Regel wüssten wir nicht, auf welches Objekt wir 3.10/15 anzuwenden haben. 9.5/1 ist also keine Einschränkung sondern macht das sinnvolle Arbeiten mit Unions überhaupt erst möglich.
Ein bisschen schwieriger ist noch der exotische Fall, falls unser Ausdruck ein rvalue ist. Etwa
union foo
{
char i;
char a[1];
double d;
};
foo f() { foo x; x.d = 1.0; return x; }
int main()
{
cout << f().d; // was nun?
cout << f().i; // und hier? im Vergleich zu
cout << f().a[0]; // hier? Dies wäre in C übrigens ill-formed
}
Uns ist natürlich klar, was bei f().d passiert - es ist aber nicht ganz einfach, dies zu begründen; glücklicherweise sind diese Fälle relativ selten.