(erledigt)[C++]struct auf zu kleinem Puffer standardkonform?
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
(erledigt)[C++]struct auf zu kleinem Puffer standardkonform?
Sagen wir, ich habe einen Puffer von zwei Bytes Größe:
void * twoBytes;
Ich habe außerdem folgendes struct:
struct S {
int16_t first;
int16_t second;
};
Ich lege S über den Puffer. (Alignment hier außen vor.) Ein Zugriff auf second ist gewiss verboten und führt mit großer Wahrscheinlichkeit zum Absturz. Aber ist es zumindest erlaubt, auf first zuzugreifen?
auto foo = (S*)twoBytes;
printf("%i", foo->first);
Ich frage, weil ich das Speicherabbild einer Datei bekomme, über das ich ein struct legen will. Auf die Felder möchte ich nur zugreifen, sofern die Datei groß genug ist. Laufe ich dabei Gefahr, dass ein Compiler die Prüfung der Länge wegoptimiert, weil er annehmen darf, dass file zumindest sizeof(S) groß ist, bloß, weil ich dazu gecastet habe? Oder weil ich ein beliebiges Feld daraus dereferenziere? Oder erst, wenn ich auf Felder mit entsprechendem Offset zugreife?
void process(void * file, size_t size) {
auto data = (S*)file;
if(size >= 2) printf("%i", file->first);
if(size >= 4) printf("%i", file->second);
}
void * twoBytes;
Ich habe außerdem folgendes struct:
struct S {
int16_t first;
int16_t second;
};
Ich lege S über den Puffer. (Alignment hier außen vor.) Ein Zugriff auf second ist gewiss verboten und führt mit großer Wahrscheinlichkeit zum Absturz. Aber ist es zumindest erlaubt, auf first zuzugreifen?
auto foo = (S*)twoBytes;
printf("%i", foo->first);
Ich frage, weil ich das Speicherabbild einer Datei bekomme, über das ich ein struct legen will. Auf die Felder möchte ich nur zugreifen, sofern die Datei groß genug ist. Laufe ich dabei Gefahr, dass ein Compiler die Prüfung der Länge wegoptimiert, weil er annehmen darf, dass file zumindest sizeof(S) groß ist, bloß, weil ich dazu gecastet habe? Oder weil ich ein beliebiges Feld daraus dereferenziere? Oder erst, wenn ich auf Felder mit entsprechendem Offset zugreife?
void process(void * file, size_t size) {
auto data = (S*)file;
if(size >= 2) printf("%i", file->first);
if(size >= 4) printf("%i", file->second);
}
Zuletzt geändert von Krishty am 09.10.2016, 02:24, insgesamt 3-mal geändert.
- dot
- Establishment
- Beiträge: 1745
- Registriert: 06.03.2004, 18:10
- Echter Name: Michael Kenzel
- Kontaktdaten:
Re: [C++] struct auf zu kleinem Puffer standardkonform?
Ohne da jetzt im Detail nachgeschaut zu haben, würd ich mal sagen, dass du in dem Moment, wo du über einen S* auf twoBytes zuzugreifen versuchst, bereits undefined behavior hast und alles weitere sich in Bezug auf Standard C++ damit erübrigt... ;)
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: [C++] struct auf zu kleinem Puffer standardkonform?
Stimmt; und es vereinfacht die Frage weiter, wenn wir einfach über einen void * sprechen, der zu S gecastet wird. Ich editier mal.
So. Meine Einschätzung: Da muss ich mir keine Gedanken um aggressive Optimierung machen.
Gehen wir vom pessimistischen Fall aus: Dass der C++-Standard irgendwo irgendwie vorschreibt, dass hinter einem S * alle Member gültig zugreifbar sind, so lange der Zeiger nicht nullptr ist.
Um das beobachtbare Verhalten zu erhalten, darf er also die Vergleiche nicht wegoptimieren. So einfach ist das.
So. Meine Einschätzung: Da muss ich mir keine Gedanken um aggressive Optimierung machen.
Gehen wir vom pessimistischen Fall aus: Dass der C++-Standard irgendwo irgendwie vorschreibt, dass hinter einem S * alle Member gültig zugreifbar sind, so lange der Zeiger nicht nullptr ist.
- data wird benutzt, ohne vorher auf nullptr geprüft zu werden
- also ist die Annahme data != nullptr gültig
- also muss auch jedes andere Member von data gültig zugreifbar sein
Um das beobachtbare Verhalten zu erhalten, darf er also die Vergleiche nicht wegoptimieren. So einfach ist das.
- dot
- Establishment
- Beiträge: 1745
- Registriert: 06.03.2004, 18:10
- Echter Name: Michael Kenzel
- Kontaktdaten:
Re: (erledigt)[C++]struct auf zu kleinem Puffer standardkonf
Ich frag mich halt: Wieso muss es unbedingt über dieses S gehen? Ein vermutlich standardkonformer Weg:
Code: Alles auswählen
void process(const char* file, size_t size) {
if(size >= 2) {
std::int16_t first;
std::memcpy(&first, file, sizeof(first));
printf("%i", first);
}
if(size >= 4) {
std::int16_t second;
std::memcpy(&second, file, sizeof(second));
printf("%i", second);
}
}
Re: (erledigt)[C++]struct auf zu kleinem Puffer standardkonf
Ich finde das explizite interpretieren eines Speichers ist eine ganz normale Operation.
Ob man nun Speicher typisiert dereferenziert, oder per memcpy darauf zugreift ist vollkommen Wurst, solange man nur Speicher beackert, der einem selbst gehört. Das problem mit memcpy ist, dass es unnutzer Overhead ist, falls man einfach nur read-only-Zugriff hat. Wenn es legal ist auf den Speicher zuzugreifen, muss ich ihn nicht erst kopieren, um ihn zu lesen, weil beim kopieren eh schon gelesen wird. Beim Schreiben ist es die selbe Geschichte.
Was mich an der Stelle wundert: wenn in deine Struktur Padding reingehauen wird, kannst du dich leider nicht mal mehr aufs speicherlayout verlassen. Und das obige Beispiel sollte eigentlich Padding enthalten (können). Irgendwie sollte da ein packed-attribut an die struct geklatscht werden.
Das einzig Wichtige wo es wirklich lichterloh brennen kann ist ein nicht an typgrenzen ausgerichteter Zugriff. AberIch würde der Flexibilität wegen die magic numbers raushauen und offsetof(..) + sizeof(..) verwenden (da haut dir dann Padding auch nix kaputt, falls du doch mal für eine andere Platform compilierst).
Ob man nun Speicher typisiert dereferenziert, oder per memcpy darauf zugreift ist vollkommen Wurst, solange man nur Speicher beackert, der einem selbst gehört. Das problem mit memcpy ist, dass es unnutzer Overhead ist, falls man einfach nur read-only-Zugriff hat. Wenn es legal ist auf den Speicher zuzugreifen, muss ich ihn nicht erst kopieren, um ihn zu lesen, weil beim kopieren eh schon gelesen wird. Beim Schreiben ist es die selbe Geschichte.
Was mich an der Stelle wundert: wenn in deine Struktur Padding reingehauen wird, kannst du dich leider nicht mal mehr aufs speicherlayout verlassen. Und das obige Beispiel sollte eigentlich Padding enthalten (können). Irgendwie sollte da ein packed-attribut an die struct geklatscht werden.
Das einzig Wichtige wo es wirklich lichterloh brennen kann ist ein nicht an typgrenzen ausgerichteter Zugriff. Aber
Go for it! :DAlignment hier außen vor
Code: Alles auswählen
if(size >= 2) printf("%i", file->first);
if(size >= 4) printf("%i", file->second);
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: (erledigt)[C++]struct auf zu kleinem Puffer standardkonf
@dot: Wie Albi sagt, ist das mit memcpy() furchtbare Quälerei, vor allem mit der Zeigerarithmetik. Ich will nicht die Offsets von 70 Variablen hard-coden müssen bevor ich sie auslese. (Du hast ja z.B. auch den Fehler drin, ein zweites Mal auf first zuzugreifen statt auf second, weil ein + 2 fehlt.)
memcpy() ist zwar nicht mehr soo viel Overhead, weil es fast alle Compiler zu einem einzelnen Load optimieren würden, aber es verschwendet noch immer Speicher auf dem Stack und verhindert die Nutzung kurzer Befehle. (Aus add rax, word ptr [file + 8] würde mov word ptr [rsp+60], word ptr [file + 8] / add rax, word ptr [rsp+60] weil du nun eine Variable hast, deren Adresse beschrieben wird.)
@Albi: Padding kontrolliere ich, nur war das für die Frage total egal, wie Alignment halt auch. offsetoff(x) + sizeof(x) nutze ich auch; danke – aber auch das hätte die Frage einfach nur verkompliziert.
memcpy() ist zwar nicht mehr soo viel Overhead, weil es fast alle Compiler zu einem einzelnen Load optimieren würden, aber es verschwendet noch immer Speicher auf dem Stack und verhindert die Nutzung kurzer Befehle. (Aus add rax, word ptr [file + 8] würde mov word ptr [rsp+60], word ptr [file + 8] / add rax, word ptr [rsp+60] weil du nun eine Variable hast, deren Adresse beschrieben wird.)
@Albi: Padding kontrolliere ich, nur war das für die Frage total egal, wie Alignment halt auch. offsetoff(x) + sizeof(x) nutze ich auch; danke – aber auch das hätte die Frage einfach nur verkompliziert.
-
- Establishment
- Beiträge: 426
- Registriert: 23.01.2013, 15:55
Re: (erledigt)[C++]struct auf zu kleinem Puffer standardkonf
Nun, der Standard garantiert da wohl nix.
Mit Visual C++ sollte diese Operation aber trotzdem funktionieren, weil es kein Strict Aliasing kennt.
Für andere Compiler gibt es __attribute__((may_alias)).
Mit Visual C++ sollte diese Operation aber trotzdem funktionieren, weil es kein Strict Aliasing kennt.
Für andere Compiler gibt es __attribute__((may_alias)).
Code: Alles auswählen
#if defined(__GNUC__) || defined(__llvm__) || defined(__clang__)
#define MAY_ALIAS __attribute__((may_alias))
#elif defined(_MSC_VER)
#define MAY_ALIAS
#else
#error MAY_ALIAS is not implemented for this compiler
#endif
//...
void process(void * file, size_t size) {
auto data = (S MAY_ALIAS*)file;
if(size >= 2) printf("%i", file->first);
if(size >= 4) printf("%i", file->second);
}
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: (erledigt)[C++]struct auf zu kleinem Puffer standardkonf
Hat das wirklich was mit Strict Aliasing zu tun? Bei dem Cast von void * muss der Compiler annehmen, dass an der Stelle wirklich ein S liegt. Sonst wäre ja z.B. malloc() eine ewige Verletzung von Strict Aliasing.
Re: (erledigt)[C++]struct auf zu kleinem Puffer standardkonf
[Ich bin zu blöd überhaupt das Problem zu verstehen.] das hat sich beim schreiben des post wohl langsam aufgeklärt.
Eine Struktur S liegt im speicher. Ein Memerzugriff geschieht indem: (jetzt mal ohne Pointerarithmetik gedacht und einfach nur Adressen addieren)
solang der Speicher mir gehört - was soll da schiefgehen und was konkret soll der Standard dagegen haben auf eine Membervariable zuzugreifen..
Aliasing ist das einzige, was mir in den Sinn kommt. Prinzipiell ist die Operationnicht erwünscht. Es ist UB. Der Compiler kann nun alles totoptimieren, denn alle Operationen auf der Struktur haben offiziell keinen Einfluss auf Speicher, der nicht der Struktur gehört. Mit anderen Worten, jeder Einfluss auf S wird nicht auf einen Einfluss auf WasVoidPtr bezogen.
In dem Fall hilft volatileüberall. Aber das macht mehr tot als man will. Wie wäre es mit einer MemoryBarrier(ist das der richtige begriff?)
In gcc kann man schreiben:
Das bedeutet dann, das sich absolut jeder Speicher geändert hat. [Stimmt das? ]
Das killt die Optimierung nur lokal und ist quasi das, was du willst, wenn man mit Aliasing arbeit.
Edit:
Ich bin mit mit der MemoryBarrier grade nicht mehr sicher, ob die tut, was ich sage.
Ich bin letztlich grade auf der Suche nach so einer Art touch(WasVoidPtr) was sagt, alle annahme über den Speicher hinter der Variable verworden werden sollen.
asm volatile ("" ::: "memory") wird als "compiler barrier" bezeichnet. Das geht schon in die richtige Richtung.
Edit2:
Tadaaah:
Eine Struktur S liegt im speicher. Ein Memerzugriff geschieht indem: (jetzt mal ohne Pointerarithmetik gedacht und einfach nur Adressen addieren)
Code: Alles auswählen
*(&S + offsetof(S.Member))
Aliasing ist das einzige, was mir in den Sinn kommt. Prinzipiell ist die Operation
Code: Alles auswählen
S* s = (S*)WasVoidPtr;
In dem Fall hilft volatileüberall. Aber das macht mehr tot als man will. Wie wäre es mit einer MemoryBarrier(ist das der richtige begriff?)
In gcc kann man schreiben:
Code: Alles auswählen
asm volatile ("" ::: "memory");
Das bedeutet dann, das sich absolut jeder Speicher geändert hat. [Stimmt das? ]
Das killt die Optimierung nur lokal und ist quasi das, was du willst, wenn man mit Aliasing arbeit.
Edit:
Ich bin mit mit der MemoryBarrier grade nicht mehr sicher, ob die tut, was ich sage.
Ich bin letztlich grade auf der Suche nach so einer Art touch(WasVoidPtr) was sagt, alle annahme über den Speicher hinter der Variable verworden werden sollen.
asm volatile ("" ::: "memory") wird als "compiler barrier" bezeichnet. Das geht schon in die richtige Richtung.
Edit2:
Tadaaah:
Dein Job: MSVC äquivlanet finden und gut.Unless *ptr and vobj can be aliased, it is not guaranteed that the write to *ptr occurs by the time the update of vobj happens. If you need this guarantee, you must use a stronger memory barrier such as:Code: Alles auswählen
int *ptr = something; volatile int vobj; *ptr = something; asm volatile ("" : : : "memory"); vobj = 1;
Zuletzt geändert von DerAlbi am 09.10.2016, 13:03, insgesamt 2-mal geändert.
- dot
- Establishment
- Beiträge: 1745
- Registriert: 06.03.2004, 18:10
- Echter Name: Michael Kenzel
- Kontaktdaten:
Re: (erledigt)[C++]struct auf zu kleinem Puffer standardkonf
volatile an sich ändert hier nix dran, dass es UB is und asm volatile ist kein Standard C++.
Und was die Quälerei angeht:
;)
Also clang macht mir aus beiden Varianten den exakt selben Machinencode... ;)Krishty hat geschrieben:memcpy() ist zwar nicht mehr soo viel Overhead, weil es fast alle Compiler zu einem einzelnen Load optimieren würden, aber es verschwendet noch immer Speicher auf dem Stack und verhindert die Nutzung kurzer Befehle. (Aus add rax, word ptr [file + 8] würde mov word ptr [rsp+60], word ptr [file + 8] / add rax, word ptr [rsp+60] weil du nun eine Variable hast, deren Adresse beschrieben wird.)
Und was die Quälerei angeht:
Code: Alles auswählen
template <typename T>
T read(const char*& file)
{
T t;
std::memcpy(&t, file, sizeof(T));
file += sizeof(T);
return t;
}
void process(const char* file, size_t size) {
if(size >= 2) {
auto first = read<std::int16_t>(file);
printf("%i", first);
}
if(size >= 4) {
auto second = read<std::int16_t>(file);
printf("%i", second);
}
}
Re: (erledigt)[C++]struct auf zu kleinem Puffer standardkonf
volatile verhindert die Auswirkungen des UB indem keine fälschlichen Optimierungen angewendet werden, weil jeder Speicherzugriff als unoptimierter Speicherzugriff geschieht, hat das Program keine Chance trotz Aliasing nicht zu funktionieren.volatile an sich ändert hier nix dran, dass es UB is und asm volatile ist kein Standard C++.
Mit anderen Worten: man hebelt den Mechanismus aus, mit dem Aliasing Probleme macht.
Was jetzt an inline-assember nicht konform ist, verstehe ich leider auch nicht. :-(
Code: Alles auswählen
DomeSomethingOnVoidPtr(..)
asm ("":::"memory");
S* s = (S*)WasVoidPtr;
DoWeirdStuffOnS(s);
asm ("":::"memory");
DomeSomethingOnVoidPtr(..)
Selbst wenn die Barriere nur das Reordering der Speicherzugriffe verhindert, wird damit auch die Optimierung gekillt, weil alle Speicherzugriffe abgeschlossen werden müssen.
Der Compiler kann dann
Code: Alles auswählen
Speicher = abc;
Barrier();
xyz = Speicher;
In dem Zusammenhang finde ich Spiele Programmierers Antwort optimal. Ich glaube, dass ist das, was Kirshty eigentlich will. Das zerstört auch weniger Optimierung als meine Compiler-Barriere. Die tötet ja wirklich alles.
Zuletzt geändert von DerAlbi am 09.10.2016, 13:06, insgesamt 1-mal geändert.
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: (erledigt)[C++]struct auf zu kleinem Puffer standardkonf
Nein, das stimmt so nicht:DerAlbi hat geschrieben:Prinzipiell ist die Operationnicht erwünscht. Es ist UB. Der Compiler kann nun alles totoptimieren, denn alle Operationen auf der Struktur haben offiziell keinen Einfluss auf Speicher, der nicht der Struktur gehört.Code: Alles auswählen
S* s = (S*)WasVoidPtr;
- void * → S * und umgekehrt sind, Wirkung des Zugriffs außen vor, erlaubt (Quelle)
- char * → void * und umgekehrt ebenfalls (selbe Quelle wie oben)
- Strict Aliasing erlaubt, jeden Typ mit char * zu aliasen (Quelle)
- es ist egal, wie man an den char * auf ein Objekt kommt, solange man dabei keine Regeln der Sprache verletzt
- genau so rechtfertigt der Standard die Funktion von memcpy(): S * → void * bei Parameterübergabe; memcpy() castet das intern zu char * und arbeitet darauf, was gemäß Strict Aliasing explizit erlaubt ist (wackelige Quelle)
- float * → char * ist erlaubt
- char * → int * ebenfalls
- das Schreiben oder Lesen durch int * ist allerdings verboten, wenn vorher durch float * geschrieben oder gelesen wurde – klassisches UB
- das Schreiben und Lesen durch int * ist nicht verboten, wenn vorher durch char * geschrieben oder gelesen wurde – Strict Aliasing erlaubt char *-Aliasing
These steht, jetzt suche ich die Quellen.
Und letztendlich spielt das eh keine Rolle weil meine Datei nur an einer Stelle gemappt ist und ich nur einen einzigen Zeiger darauf habe.
Zuletzt geändert von Krishty am 09.10.2016, 13:18, insgesamt 4-mal geändert.
-
- Establishment
- Beiträge: 426
- Registriert: 23.01.2013, 15:55
Re: (erledigt)[C++]struct auf zu kleinem Puffer standardkonf
Nun - entweder liegt an der Stelle ein Objekt S oder eben nicht.Hat das wirklich was mit Strict Aliasing zu tun? Bei dem Cast von void * muss der Compiler annehmen, dass an der Stelle wirklich ein S liegt. Sonst wäre ja z.B. malloc() eine ewige Verletzung von Strict Aliasing.
Wenn der erste Fall zutrifft, erübrigt sich das Thema. Im zweiten Fall ist ein Strict Aliasing Problem.
malloc ist in Ordnung, weil der Speicher noch keinen Typ besitzt.
Re: (erledigt)[C++]struct auf zu kleinem Puffer standardkonf
Danke für die Richtigstellung :-) Ein wichtiges Detail.
Jetzt mal rein aus interesse zum Thema:
Impliziert das nun aber nicht, dass du in der struct nur chars haben darfst?
Mir geht es um den angenommenen Schreizugriff auf deine gemappte Struktur. Wenn man z.B. per int scheibt kann man an anderer Stelle das nicht mehr auslesen, außer man setzt sich den int aus den einzelnen chars des Speichers zusammen? (wtf)
a) oder ist struct->int ailiasing mit IntArray[n] erlaubt, weils beides int sind ?)
b) struct->int ailiasing mit floatArray[n] ist nicht erlaubt, aber fürde ich den float als char* aus dem Speicher binär zusammensetzen ist das ok? (immernoch: wtf)
Jetzt mal rein aus interesse zum Thema:
Impliziert das nun aber nicht, dass du in der struct nur chars haben darfst?
Mir geht es um den angenommenen Schreizugriff auf deine gemappte Struktur. Wenn man z.B. per int scheibt kann man an anderer Stelle das nicht mehr auslesen, außer man setzt sich den int aus den einzelnen chars des Speichers zusammen? (wtf)
a) oder ist struct->int ailiasing mit IntArray[n] erlaubt, weils beides int sind ?)
b) struct->int ailiasing mit floatArray[n] ist nicht erlaubt, aber fürde ich den float als char* aus dem Speicher binär zusammensetzen ist das ok? (immernoch: wtf)
Zuletzt geändert von DerAlbi am 09.10.2016, 13:21, insgesamt 1-mal geändert.
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: (erledigt)[C++]struct auf zu kleinem Puffer standardkonf
Wenn du per int * reinschreibst, musst du entweder als int * wieder auslesen, oder als Folge von char (was ja z.B. memcpy() macht, weswegen dots Vorschlag 100 % konform ist).
Ich darf in meinem struct haben, was ich will. Aber wenn irgendwann mal anders auf die Daten zugreife als durch das struct, bleibt mir als einzige Möglichkeit char *. Oooooder ich schalte Strict Aliasing ab oder baue Memory Barriers ein etc, aber das ist dann nicht mehr portabel.
Ich darf in meinem struct haben, was ich will. Aber wenn irgendwann mal anders auf die Daten zugreife als durch das struct, bleibt mir als einzige Möglichkeit char *. Oooooder ich schalte Strict Aliasing ab oder baue Memory Barriers ein etc, aber das ist dann nicht mehr portabel.
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: (erledigt)[C++]struct auf zu kleinem Puffer standardkonf
Cool, danke! Da pennt Visual C++ also mal wieder.dot hat geschrieben:Also clang macht mir aus beiden Varianten den exakt selben Machinencode... ;)Krishty hat geschrieben:memcpy() ist zwar nicht mehr soo viel Overhead, weil es fast alle Compiler zu einem einzelnen Load optimieren würden, aber es verschwendet noch immer Speicher auf dem Stack und verhindert die Nutzung kurzer Befehle. (Aus add rax, word ptr [file + 8] würde mov word ptr [rsp+60], word ptr [file + 8] / add rax, word ptr [rsp+60] weil du nun eine Variable hast, deren Adresse beschrieben wird.)
Neee – ich greife nicht seriell auf die Daten zu. Ich brauche echten wahlfreien Zugriff! Ich müsste alles in lokalen Variablen fetchen, in der richtigen Reihenfolge, aber dann habe ich 30 davon und keinen Mehrwert gegenüber einem struct-Overlay.Und was die Quälerei angeht:
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: (erledigt)[C++]struct auf zu kleinem Puffer standardkonf
Puh, keine Ahnung. VIELLEICHT wäre es mit POD erlaubt, aber … uff.DerAlbi hat geschrieben:a) oder ist struct->int ailiasing mit IntArray[n] erlaubt, weils beides int sind ?)
Ja, ist es. Ist der einzig portable Weg, float als int zu interpretieren (LOL). Bedenk, dass sich das aber einfacher als memcpy() ausdrücken lässt, weil das intern immer auf char * operiert.DerAlbi hat geschrieben:b) struct->int ailiasing mit floatArray[n] ist nicht erlaubt, aber fürde ich den float als char* aus dem Speicher binär zusammensetzen ist das ok? (immernoch: wtf)
Re: (erledigt)[C++]struct auf zu kleinem Puffer standardkonf
*_* Danke <3
:-D
:-D
- dot
- Establishment
- Beiträge: 1745
- Registriert: 06.03.2004, 18:10
- Echter Name: Michael Kenzel
- Kontaktdaten:
Re: (erledigt)[C++]struct auf zu kleinem Puffer standardkonf
Naja, im Gegensatz zu einem struct-Overlay wäre es portabel... ;)Krishty hat geschrieben:Neee – ich greife nicht seriell auf die Daten zu. Ich brauche echten wahlfreien Zugriff! Ich müsste alles in lokalen Variablen fetchen, in der richtigen Reihenfolge, aber dann habe ich 30 davon und keinen Mehrwert gegenüber einem struct-Overlay.
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: (erledigt)[C++]struct auf zu kleinem Puffer standardkonf
Wo ist das struct nicht portabel?
- dot
- Establishment
- Beiträge: 1745
- Registriert: 06.03.2004, 18:10
- Echter Name: Michael Kenzel
- Kontaktdaten:
Re: (erledigt)[C++]struct auf zu kleinem Puffer standardkonf
struct Layout ist im Allgemeinen nicht portabel... ;)
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: (erledigt)[C++]struct auf zu kleinem Puffer standardkonf
Praktisch ist’s portabel genug.
- dot
- Establishment
- Beiträge: 1745
- Registriert: 06.03.2004, 18:10
- Echter Name: Michael Kenzel
- Kontaktdaten:
Re: (erledigt)[C++]struct auf zu kleinem Puffer standardkonf
Das Problem sind ja auch nicht die Pointercasts (sofern man mal davon ausgeht, dass alle involvierten Typen kompatible Alignment Requirements haben), sondern was du mit dem gewonnenen Pointer dann anstellst. Du darfst eben nur über einen aus char* (oder void*, das macht hier eigentlich keinen Unterschied) gewonnenen S* auf ein S an dieser Stelle zugreifen, wenn sich dort tatsächlich ein S befindet. Ein S befindet sich dort nur, wenn zuvor dort ein S konstruiert wurde...Krishty hat geschrieben:Die Casts sind also gültig und jede Operation, die ich auf S * durchführe, spiegelt sich auch mit Strict Aliasing in jedem char * wieder.
Die Lösung per memcpy() funktioniert, weil es sich um trivially-copyable Types handelt und der Standard mir garantiert, dass ich die sizeof(T) Bytes der Object-Representation eines trivially-copyable Type T aus einem existierenden T rauskopieren darf und, sobald ich sie wieder in ein T hineinkopiere, dieses dann das Value des ursprünglichen T hat...
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: (erledigt)[C++]struct auf zu kleinem Puffer standardkonf
Das S wurde in der Tat dort konstruiert, nur eben nicht während der Laufzeit des Prozesses ;)
- dot
- Establishment
- Beiträge: 1745
- Registriert: 06.03.2004, 18:10
- Echter Name: Michael Kenzel
- Kontaktdaten:
Re: (erledigt)[C++]struct auf zu kleinem Puffer standardkonf
Jo, das ist mir schon klar, auch dass das alles so funktionieren wird. Nur nach meiner Leseart des Standard müsste das streng genommen dennoch UB sein und man kann leider nicht wirklich was dran ändern (malloc() + Cast müsste imo btw auch UB sein)... ;)Krishty hat geschrieben:Das S wurde in der Tat dort konstruiert, nur eben nicht während der Laufzeit des Prozesses ;)
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: (erledigt)[C++]struct auf zu kleinem Puffer standardkonf
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 :)
- dot
- Establishment
- Beiträge: 1745
- Registriert: 06.03.2004, 18:10
- Echter Name: Michael Kenzel
- Kontaktdaten:
Re: (erledigt)[C++]struct auf zu kleinem Puffer standardkonf
Das sollte in der Tat eine gültige Lösung sein...xDKrishty hat geschrieben:[...]also benenne ich im Zweifel die Quelldatei der Parsing-Funktion in foo.c um :)
- CodingCat
- Establishment
- Beiträge: 1857
- Registriert: 02.03.2009, 21:25
- Wohnort: Student @ KIT
- Kontaktdaten:
Re: (erledigt)[C++]struct auf zu kleinem Puffer standardkonf
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.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 :)
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.
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
- dot
- Establishment
- Beiträge: 1745
- Registriert: 06.03.2004, 18:10
- Echter Name: Michael Kenzel
- Kontaktdaten:
Re: (erledigt)[C++]struct auf zu kleinem Puffer standardkonf
Ah, hab §3.8 übersehen, das ist natürlich nice, damit ist die Sache wohldefiniert; thx für den Hinweis, jetzt kann ich gleich viel besser schlafen... :D
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: (erledigt)[C++]struct auf zu kleinem Puffer standardkonf
Napf Brekkies für meine Lieblingskatze
dot, erledigt das auch, womit wir im letzten Thread aneinandergeraten sind?
dot, erledigt das auch, womit wir im letzten Thread aneinandergeraten sind?
dot hat geschrieben:Nö, ein Haufen von nicht-konstruierten POD Objekten macht genauso keinen Sinn, denn nicht-konstruierte Objekte existieren per Definition nicht, egal wie P oder O der T auch noch sein mag... :PKrishty hat geschrieben:Und ein Haufen von nicht-konstruierten Objekten macht halt *doch* Sinn, wenn es sich um POD handelt :)