Krishty hat geschrieben:malloc() & Cast dürfte sogar recht sicher UB sein, so lange man kein Placement new drauf durchführt. In C hingegen ist das (natürlich) völlig legal, also benenne ich im Zweifel die Quelldatei der Parsing-Funktion in foo.c um :)
Nah, (§3.8) Object Lifetime beginnt wenn Speicherplatz mit korrektem Alignment für Typ
T da ist, und [Initialisierung vollständig durchgeführt
ODER trivial] ist.
malloc kümmert sich für alle fundamentalen Typen
T und Aggretates davon um korrektes Alignment, ergo funktioniert das mit dem Cast schon und der dynamische Typ
T des Objekts steht fest, sobald Speicher durch den Cast mit diesem in Verbindung gebracht wird. Für nicht-triviale Typen muss der Cast logischerweise durch ein Placement-New zur vollständigen Initialisierung/Konstruktion ersetzt werden.
Das beantwortet auch die Eingangsfrage, sofern der Speicher aus deiner Datei das richtige Alignment UND die richtige Größe hat, ist alles in Ordnung. Letzteres ist offensichtlich manchmal verletzt. Der Compiler könnte z.B. beide Loads zusammenfassen und aus dem
if ziehen, sobald nach dem ersten Zugriff klar ist, dass ein gültiges Objekt vorliegen muss; was er vermutlich nie tun wird, aber darum geht es bei der Frage nach undefiniertem Verhalten ja nicht. Das mit dem
memcpy ist übrigens auch unnötig, §3.10.10 erlaubt explizit auch Aliasing durch Elemente von glvalues (Zeiger/Referenzen) von Aggregates oder Unions. §9.2.19 garantiert außerdem, dass ein Zeiger auf ein
struct-Objekt immer auch auf das erste Element zeigt. Insofern wäre es z.B. möglich, die verschiedenen Varianten mit geschachtelten
structs umzusetzen, wobei die innerste Struktur den kleinstmöglichen Header darstellt, und dann von optional ausführlicheren Header-Strukturen als geschachteltes Element an erster Stelle eingebunden wird, SOFERN dem Compiler z.B. durch non-standard Extensions das richtige Padding der enthaltenen Strukturen aufgezwungen wird (soll heißen, das Padding mit dem die Datei erstellt wurde). Das Padding-Problem gilt natürlich generell für
structs - nicht nur für die Endbytes von geschachtelten; auch zwischen den Elementen.
Fazit: Sobald Größe, Alignment, Padding und damit die Byte-Repräsentation stimmt, existieren nach §3.8 erstmal alle potentiellen trivialen Objekte, die dort hinein passen, insbesondere auch das größtmögliche aggregierte.