Wie hieß denn noch der Datentyp ?
-
Michael E. schrieb:
Selbst wenn garantiert wäre, dass das Padding gleich ist, wäre es trotzdem nicht legal, über m auf mat zuzugreifen.
Das verstehe ich jetzt nicht. Ist das nicht gerade Sinn und Zweck einer union?
-
Jockelx schrieb:
Michael E. schrieb:
Selbst wenn garantiert wäre, dass das Padding gleich ist, wäre es trotzdem nicht legal, über m auf mat zuzugreifen.
Das verstehe ich jetzt nicht. Ist das nicht gerade Sinn und Zweck einer union?
C++98 Kapitel 9.5 schrieb:
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.
Der einzige Sinn von Unions ist es, Speicher zu sparen.
-
Mmh...
Heisst das jetzt, dass in einer union zwar mehrere Member sein dürfen, aber pro Instanz nur ein Member genutzt werden darf?Ich bin etwas verwundert ,denn als Std-Beispiel für uinon hab ich sowas im Kopf
union blub { int32 bla; char bla1; char bla2; char bla3; char bla4; }zum setzen/lesen einzelner Byteteile in einem int.
Das wäre demnach auch quatsch, richtig?
-
Jockelx schrieb:
Mmh...
Heisst das jetzt, dass in einer union zwar mehrere Member sein dürfen, aber pro Instanz nur ein Member genutzt werden darf?Du darfst natürlich den Member ändern, den du nutzt. Aber der erste Zugriff auf einen anderen Member muss dann ein schreibender Zugriff sein.
Ich bin etwas verwundert ,denn als Std-Beispiel für uinon hab ich sowas im Kopf
union blub { int32 bla; char bla1; char bla2; char bla3; char bla4; }zum setzen/lesen einzelner Byteteile in einem int.
Das wäre demnach auch quatsch, richtig?Klappt zwar meistens (wenn man Endianness beachtet), ist aber nicht definiert.
-
Aha.
Werde ich zwar vermutlich wieder nur unter 'unions sind komisch und brauch ich nicht' abspeichern,
aber Danke für die Erklärung.
-
Skym0sh0 schrieb:
wieso?
wenn man weiss, was man macht, ist das doch ok?!?wie würdest du denn sonst zum beispiel eine 4x4 matrix darstellen?
union Matrix { float mat[4][4]; struct m { float _11, _12, _13, _14; float _21, _22, _23, _24; float _31, _32, _33, _34; float _41, _42, _43, _44; }; };so kann man per array index zugreifen oder halt per variablen ansprache...
U.u. dann wenn du eine Membervariable m definieren würdest und nicht einen Membertyp.
Persönlich bin ich von dem Paddingargument ja nicht überzeugt, selbst wenn der Wortlaut des Standards das erlaubt, scheint es mir doch gegen die Intention der Standardkomittes zu sprechen.
Wenn wir annehmen, dass Padding kein Problem darstellt, sehe ich allerdings keine Einwände hinsichtlich undefinierten Verhaltens.
Jockelx schrieb:
Mmh...
Heisst das jetzt, dass in einer union zwar mehrere Member sein dürfen, aber pro Instanz nur ein Member genutzt werden darf?Ich bin etwas verwundert ,denn als Std-Beispiel für uinon hab ich sowas im Kopf
zum setzen/lesen einzelner Byteteile in einem int.
Das wäre demnach auch quatsch, richtig?Gemeint ist sicher so etwas:
union blub { int32 bla; char blab[sizeof(int32)]; };Das ist unproblematisch.
-
camper schrieb:
Wenn wir annehmen, dass Padding kein Problem darstellt, sehe ich allerdings keine Einwände hinsichtlich undefinierten Verhaltens.
Ich hab zwar immer Angst, mich mit dir bzgl. des Standards anzulegen, aber wo siehst du eine Rechtfertigung, dass es nicht undefiniertes Verhalten sein soll?
Gemeint ist sicher so etwas:
union blub { int32 bla; char blab[sizeof(int32)]; };Das ist unproblematisch.
Nur was rauskommt, weiß man nicht
Ist es Big Endian oder Little Endian? Verhält char sich wie signed char oder wie unsigned char? Warum werden die Bits in einem Char nicht in einer anderen Reihenfolge interpretiert als bei einem int-Byte? Warum ist es überhaupt definiert?
-
camper schrieb:
Gemeint ist sicher so etwas:
union blub { int32 bla; char blab[sizeof(int32)]; };Das ist unproblematisch.
Wirklich? Ich hab ja gestern einiges gelernt über undefiniertes Verhalten (anderer Thread: http://www.c-plusplus.net/forum/286938 ), und wenn ich das hier mal anwende, dann könnte ein Compiler diese union folgendermaßen behandeln. Jeder Zugriff auf einen Member, der beweisbar vorher durch Schreibzugriff auf einen anderen Member invalidiert wurde, kann ignoriert werden, da er undefiniertes Verhalten auslöst. Das heißt z.B.
blub B; B.bla = 42; if (x != 0) B.blab[0] = 42; B.bla += x; // kann wegoptimiert werden, da die einzige legale Ablaufsequenz notwendig x==0 impliziertMir fällt auf die Schnelle kein besseres Beispiel ein, ich hoffe, es wird ungefähr klar, was ich meine.
-
Wozu denn dieses
union-Gefrickel, wenn man doch etwas ganz anderes erreichen möchte?int i = 12; char *p = reinterpret_cast<char *>(&i); if (*p == 12) ..
-
Bashar schrieb:
Wirklich? Ich hab ja gestern einiges gelernt über undefiniertes Verhalten (anderer Thread: http://www.c-plusplus.net/forum/286938 ), und wenn ich das hier mal anwende, dann könnte ein Compiler diese union folgendermaßen behandeln. Jeder Zugriff auf einen Member, der beweisbar vorher durch Schreibzugriff auf einen anderen Member invalidiert wurde, kann ignoriert werden, da er undefiniertes Verhalten auslöst.
Keineswegs, was beweist wie subtil die Sprache des Standards sein kann:
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. ]Nirgendwo steht hier, dass der lesende Zugriff über ein anders Member per se undefiniert wäre. Was hier steht ist, dass die anderen Member keine eigenen Wert haben und folglich, da auf das gleiche Objekt zugegriffen wird, das Ergebnis des Zugriffs vom Wert des zuletzt geschriebenen Members abhängt. Und das ist ausreichend, da zu jeder beliebigen Objektrepräsentation (die ja von allen Membern geteilt wird) eine eindeutige Wertrepräsentation gehört.
Undefiniert wird es erst durch die Einschränkungen, die in 3.10/15 zu finden sind (aliasing). In gewisser Weise dient der erste Satz nur dazu, dafür zu sorgen, dass die Aliasregeln überhaupt auf diesen Fall angewendet werden können, andernfalls wäre nämlich nicht klar, auf den Wert welchen Objektes überhaupt zugegriffen wird.Interesessant ist in diesem Zusammenhang die folgende Bemerkung (nicht normativ, was dort gesagt wird, könnten wir also auch aus anderen Regeln herleiten) - wenn alle POD-structs mit gleicher Anfangssequenz für diese Sequenz das gleiche Speicherlayout haben müssen, spricht das meiner Meinung nach stark dagegen, dass der Compiler (abgesehen vom Ende des structs) nach Belieben Padding einführen darf.
-
Michael E. schrieb:
Nur was rauskommt, weiß man nicht
Ist es Big Endian oder Little Endian? Verhält char sich wie signed char oder wie unsigned char? Warum werden die Bits in einem Char nicht in einer anderen Reihenfolge interpretiert als bei einem int-Byte? Warum ist es überhaupt definiert?Einige dieser Fragen werden durch den Standard geklärt, andere müssen von der jeweiligen Implementation definiert werden.
-
camper schrieb:
Nirgendwo steht hier, dass der lesende Zugriff über ein anders Member per se undefiniert wäre. Was hier steht ist, dass die anderen Member keine eigenen Wert haben und folglich, da auf das gleiche Objekt zugegriffen wird, das Ergebnis des Zugriffs vom Wert des zuletzt geschriebenen Members abhängt. Und das ist ausreichend, da zu jeder beliebigen Objektrepräsentation (die ja von allen Membern geteilt wird) eine eindeutige Wertrepräsentation gehört.
Du greifst aber nicht auf das Objekt, sondern auf einen Member des Objekts zu. Dass an dieser Stelle ein Bitmuster steht, macht den Zugriff nicht legal.
Interesessant ist in diesem Zusammenhang die folgende Bemerkung (nicht normativ, was dort gesagt wird, könnten wir also auch aus anderen Regeln herleiten) - wenn alle POD-structs mit gleicher Anfangssequenz für diese Sequenz das gleiche Speicherlayout haben müssen, spricht das meiner Meinung nach stark dagegen, dass der Compiler (abgesehen vom Ende des structs) nach Belieben Padding einführen darf.
Wieso?
camper schrieb:
Michael E. schrieb:
Nur was rauskommt, weiß man nicht
Ist es Big Endian oder Little Endian? Verhält char sich wie signed char oder wie unsigned char? Warum werden die Bits in einem Char nicht in einer anderen Reihenfolge interpretiert als bei einem int-Byte? Warum ist es überhaupt definiert?Einige dieser Fragen werden durch den Standard geklärt, andere müssen von der jeweiligen Implementation definiert werden.
Welche werden denn vom Standard geklärt?
-
PS: Hab noch ne schöne Anekdote gefunden:
Conversion by union can be a disaster. Been there, done that. The BSD kernel used
to have a union that essentially looked like this:union u {
char* c;
short* s;
int* i;
long* l;
};and used to be loosy goosy vax and store into one field and read back from another.
This was fine until we were porting to a machine that encodes the operand size in
the low order bits of the pointer. That led to some fun hunting.Quelle: http://www.velocityreviews.com/forums/t284331-p2-undefined-behaviour.html
-
Michael E. schrieb:
Du greifst aber nicht auf das Objekt, sondern auf einen Member des Objekts zu.
Subobjekte sind keine Objekte?
Michael E. schrieb:
Wieso?
Dazu schreib ich später noch was.
Michael E. schrieb:
camper schrieb:
Michael E. schrieb:
Nur was rauskommt, weiß man nicht
Ist es Big Endian oder Little Endian? Verhält char sich wie signed char oder wie unsigned char? Warum werden die Bits in einem Char nicht in einer anderen Reihenfolge interpretiert als bei einem int-Byte? Warum ist es überhaupt definiert?Einige dieser Fragen werden durch den Standard geklärt, andere müssen von der jeweiligen Implementation definiert werden.
Welche werden denn vom Standard geklärt?
Warum ist es überhaupt definiert?
3.9, 3.9.1
Warum werden die Bits in einem Char nicht in einer anderen Reihenfolge interpretiert als bei einem int-Byte?
Das ist eine sinnlose Frage: da bytes die kleinste adressierbare Einheit darstellen, existiert soetwas wie eine Reihenfolge von Bits innerhalb eines chars nicht, ansonsten 3.9.1/7
-
camper schrieb:
Warum werden die Bits in einem Char nicht in einer anderen Reihenfolge interpretiert als bei einem int-Byte?
Das ist eine sinnlose Frage: da bytes die kleinste adressierbare Einheit darstellen, existiert soetwas wie eine Reihenfolge von Bits innerhalb eines chars nicht, ansonsten 3.9.1/7
Nur weil du nicht auf die interne Darstellung zugreifen kannst, heißt das nicht, dass sie nicht unterschiedlich sein dürfte. Die zugrundeliegende Hardware hat schließlich nicht die Einschränkungen den Programmierers.
-
camper schrieb:
Michael E. schrieb:
Du greifst aber nicht auf das Objekt, sondern auf einen Member des Objekts zu.
Subobjekte sind keine Objekte?
Du sagst, dass du auf das gleiche Objekt zugreifst.
Warum ist es überhaupt definiert?
3.9, 3.9.1
Kannst du das bitte etwas konkretisieren?
Warum werden die Bits in einem Char nicht in einer anderen Reihenfolge interpretiert als bei einem int-Byte?
Das ist eine sinnlose Frage: da bytes die kleinste adressierbare Einheit darstellen, existiert soetwas wie eine Reihenfolge von Bits innerhalb eines chars nicht, ansonsten 3.9.1/7
Die Reihenfolge existiert sehr wohl, wie SeppJ schon bemerkt hat. Außerdem kann ich in 3.9.1/7 nicht sehen, dass die Reihenfolge festgelegt wird.
-
Michael E. schrieb:
camper schrieb:
Michael E. schrieb:
Du greifst aber nicht auf das Objekt, sondern auf einen Member des Objekts zu.
Subobjekte sind keine Objekte?
Du sagst, dass du auf das gleiche Objekt zugreifst.
Objekte sind bestimmte Speicherbereiche. Greife ich auf den gleichen Speicherbereich zu, ist es das gleiche Objekt.
Warum ist es überhaupt definiert?
3.9, 3.9.1
Kannst du das bitte etwas konkretisieren?
Wenn die Frage konkretisiert wird.
Michael E. schrieb:
Warum werden die Bits in einem Char nicht in einer anderen Reihenfolge interpretiert als bei einem int-Byte?
Das ist eine sinnlose Frage: da bytes die kleinste adressierbare Einheit darstellen, existiert soetwas wie eine Reihenfolge von Bits innerhalb eines chars nicht, ansonsten 3.9.1/7
Die Reihenfolge existiert sehr wohl, wie SeppJ schon bemerkt hat. Außerdem kann ich in 3.9.1/7 nicht sehen, dass die Reihenfolge festgelegt wird.
Bevor ich mich auf Glatteise begebe, bitte ich um eine präzisere Fragestellung. Der Standard kennt keine Reihenfolgen von bits, nur Wertigkeiten.
-
camper schrieb:
Michael E. schrieb:
camper schrieb:
Michael E. schrieb:
Du greifst aber nicht auf das Objekt, sondern auf einen Member des Objekts zu.
Subobjekte sind keine Objekte?
Du sagst, dass du auf das gleiche Objekt zugreifst.
Objekte sind bestimmte Speicherbereiche. Greife ich auf den gleichen Speicherbereich zu, ist es das gleiche Objekt.
struct Foo { int bar; }; Foo foo;Sind foo und foo.bar das gleiche Objekt?
Kannst du das bitte etwas konkretisieren?
Wenn die Frage konkretisiert wird.
Warum ist der Zugriff auf blab[0] nicht undefiniert, wenn der letzte schreibende Zugriff auf bla war?
Michael E. schrieb:
Die Reihenfolge existiert sehr wohl, wie SeppJ schon bemerkt hat. Außerdem kann ich in 3.9.1/7 nicht sehen, dass die Reihenfolge festgelegt wird.
Bevor ich mich auf Glatteise begebe, bitte ich um eine präzisere Fragestellung. Der Standard kennt keine Reihenfolgen von bits, nur Wertigkeiten.
Warum ist Folgendes nicht möglich? Bei chars steht das höchstwertige Bit vorne im Byte, bei ints steht das höchstwertige Bit hinten im Byte. Wenn ich einen int schreibe und einen char an dieser Position lese, wurden die Wertigkeiten verändert.
-
Michael E. schrieb:
camper schrieb:
Michael E. schrieb:
camper schrieb:
Michael E. schrieb:
Du greifst aber nicht auf das Objekt, sondern auf einen Member des Objekts zu.
Subobjekte sind keine Objekte?
Du sagst, dass du auf das gleiche Objekt zugreifst.
Objekte sind bestimmte Speicherbereiche. Greife ich auf den gleichen Speicherbereich zu, ist es das gleiche Objekt.
struct Foo { int bar; }; Foo foo;Sind foo und foo.bar das gleiche Objekt?
Definiere: Zugriff: = entweder
1.[lesen] bestimmen des Zustandes (=Objektrepräsentation) eines Speicherbereiches (Objekt); ermittle daraus Wertrepräsentation und schließlich den Wert selbst
2. [schreiben] ausgehend von einem Wert codiere Wert- und schließlich Objektrepräsentation, festlegen des Zustandes eines Speicherbereiches (Objekt) auf diese ObjektrepräsentationJeder Zugriff auf foo.bar, greift notwendigerweise auch auf foo zu, da foo.bar ein Teil von foo ist. Die Umkehrung gilt nur bedingt.
Warum ist der Zugriff auf blab[0] nicht undefiniert, wenn der letzte schreibende Zugriff auf bla war?
Ihrem Ursprung nach gibt es zwei Arten von undefiniert:
- solchen Dingen, zu denen der Standard sich nicht äußert,
- solchen, die ausdrücklich ausgeschlossen werden; und für die andernfalls eine Interpretation gefunden werden könnte
blab[0] hat auf jeden Fall eine bestimmten Zustand (dazwischenliegende Sequenzpunkte angenommen, etc.), und ein Zugriff auf blab[0] hängt (nur) von dessen Zustand ab. Bis hierhin muss ich hoffentlich nicht den Standard zitieren.
Es könnte sein, dass der Zugriff trotzdem ausdrücklich ausgeschlossen ist. Da ich hier nicht den gesamten Standard untersuchen kann, behaupte ich, dass eine solche Regel für die Fälle, die bisher betrachtet wurden, nicht existiert.Warum ist Folgendes nicht möglich? Bei chars steht das höchstwertige Bit vorne im Byte, bei ints steht das höchstwertige Bit hinten im Byte. Wenn ich einen int schreibe und einen char an dieser Position lese, wurden die Wertigkeiten verändert.
Das ist möglich. Allerdings wird diese Problematik bereits durch die Frage nach Endianess erfasst (oder umgekehrt). Ich nahm natürlich an, dass du auf verschiedene Probleme hinweisen wolltest. Ich meine, Paddingbits und Traprepräsentation wurden ja auch nicht angesprochen. Ultimativ hat das ohnehin wenig mit ursprünglichen Code zu tun.
-
TyRoXx schrieb:
Wozu denn dieses
union-Gefrickel, wenn man doch etwas ganz anderes erreichen möchte?int i = 12; char *p = reinterpret_cast<char *>(&i); if (*p == 12) ..Weil auch das undefiniert ist (strict aliasing)