Ein paar Fragen zu memcpy und versch. Datentypen
-
HaJo. schrieb:
Also kein Problem solange es unter Windowssystemen läuft. Wenn es jetzt aber z.B. unter einem PowerPC wo Windows in einer VM läuft, würde es schiefgehen, richtig?
ne VM sollte tunlichst das windows bit-ordering emulieren, sonst wärs keine anständige VM. probleme bekommst dann, wenn dein programm nativ auf anderen system laufen soll.
-
thordk schrieb:
ne VM sollte tunlichst das windows bit-ordering emulieren, sonst wärs keine anständige VM. probleme bekommst dann, wenn dein programm nativ auf anderen system laufen soll.
Stimmt. Im Nachhinein wohl schwachsinn. Danke.
-
HaJo. schrieb:
CStoll schrieb:
@1: memcpy() kopiert byteweise den kompletten Speicherbereich - da du keine "externen" Daten verwendest, ist das hier völlig ausreichend (aber unnötig - du kannst auch read()/write() direkt auf deine FILE_HEADER loslassen).
Müsste dann nur in CharType casten also
std::basic_fstream<CharType> file("Dateiname"); file.write(reinterpret_cast<CharType*>(&Header), sizeof(FILE_HEADER<CharType>));Wäre um einiges performater, da ich eine Speicheranforderung weglassen kann.
Ja, genau so sollte es funktionieren (abgesehen von der Tatsache, daß FILE_HEADER kein Template ist ;)).
CStoll schrieb:
@1b: "#pragma pack()" gibt an, wie die Strukturelemente im Speicher ausgerichtet werden. Je nach Einstellung (und Größe der Member) können noch Füll-Bytes dazwischengeschoben werden, um die Adressen zu korrigieren. Das wird nur dann problematisch, wenn du die Daten zwischen verschiedenen Systemen austauschen willst.
Wie oben erwähnt. Die Software soll nur unter Windowssystemen laufen. Hättest du vielleicht ein Beispiel wo #pragma pack(xyz) genutzt werden müsste?
Auf Anhieb nicht. Solche Anpassungen brauchst du vor allem, wenn du ein bestimmtes Speicher-Layout erzwingen willst. Bei deiner geänderten Struktur könnten z.B. nach den beiden char-Arrays padding-Bytes eingefügt werden, die (a) eventuell unnötigen Speicherplatz verbrauchen und (b) das Layout durcheinanderwürfeln können.
-
CStoll schrieb:
Ja, genau so sollte es funktionieren (abgesehen von der Tatsache, daß FILE_HEADER kein Template ist ;)).
Doch, FILE_HEADER ist doch ein Template.

template<class CharType>struct FILE_HEADER { /* ... */ };CStoll schrieb:
Auf Anhieb nicht. Solche Anpassungen brauchst du vor allem, wenn du ein bestimmtes Speicher-Layout erzwingen willst. Bei deiner geänderten Struktur könnten z.B. nach den beiden char-Arrays padding-Bytes eingefügt werden, die (a) eventuell unnötigen Speicherplatz verbrauchen und (b) das Layout durcheinanderwürfeln können.
Schade.
Falls dir noch mal eines über den Weg läuft, wäre ich dran interessiert. Also sollte es auch noch mit der geänderten Sruktur, wo nicht mehr alle Memeber durch 4 teilbar sind klappen.
-
HaJo. schrieb:
CStoll schrieb:
Ja, genau so sollte es funktionieren (abgesehen von der Tatsache, daß FILE_HEADER kein Template ist ;)).
Doch, FILE_HEADER ist doch ein Template.

template<class CharType>struct FILE_HEADER { /* ... */ };Ups, stimmt. Das hatte ich übersehen.
CStoll schrieb:
Auf Anhieb nicht. Solche Anpassungen brauchst du vor allem, wenn du ein bestimmtes Speicher-Layout erzwingen willst. Bei deiner geänderten Struktur könnten z.B. nach den beiden char-Arrays padding-Bytes eingefügt werden, die (a) eventuell unnötigen Speicherplatz verbrauchen und (b) das Layout durcheinanderwürfeln können.
Schade.
Falls dir noch mal eines über den Weg läuft, wäre ich dran interessiert. Also sollte es auch noch mit der geänderten Sruktur, wo nicht mehr alle Memeber durch 4 teilbar sind klappen.Theoretisch ja, praktisch müsstest du mal sehen, ob dich die Padding-Bytes im Ernstfall stören.
PS: Laut meiner MSDN ist der Defaultwert für's Padding bei 8, also hast du auch in der Originalversion möglicherweise schon Lücken im Objekt.
-
CStoll schrieb:
Theoretisch ja, praktisch müsstest du mal sehen, ob dich die Padding-Bytes im Ernstfall stören.
Also. Ich habe mal Testweise die Struktur mit Zufallswerten gefüllt in einer Schleife in die Datei schreiben lassen und wieder auslesen lassen. Dann mit memcmp die Bytes verglichen und es haut alles hin. Das ist die Struktur
template<class CharType> struct MAIN_HEADER { CharType m_pSignature[7]; short m_MajorVersion; short m_MinorVersion; EncryptionAlgorithm m_EncryptionAlgorithm; CharType m_pSalt[32]; int m_Counter; CharType m_pReserved[54]; };Die "echte" Größe der Struktur ist 105 Bytes. Ohne #pragma pack Angaben - Standardwert 8 - ist die Struktur allerdings 108 Bytes groß. Also wird etwas aufgefüllt. Das mit dem Padding verstehe ich nicht ganz in der MSDN. Wozu dient das?
Es läuft in meinem Test auch alles mit #pragma pack(1). Hier hat die Struktur dann ihre Originalgröße von 105 Bytes.Eine Sache verunsichert mich hier allerdings:
MSDN schrieb:
Note that if you change the alignment of a structure, the structure will not use as much space in memory, but you may see a decrease in performance or even get a hardware-generated exception for unaligned access. It is possible to modify this exception behavior with SetErrorMode.
Ja, habe ich denn jetzt ein Performanzverlust bei #pragma pack(1)?
CStoll schrieb:
PS: Laut meiner MSDN ist der Defaultwert für's Padding bei 8, also hast du auch in der Originalversion möglicherweise schon Lücken im Objekt.
Klingt nett. Ich habe auch eine.

-
OK, dann etwas ausführlicher: Das Padding dient dazu, die Position einzelner Member an "gutartigen" Speicheradressen auszurichten. Nehmen wir also eine etwas einfachere Struktur:
struct test { char c; int i; };Die Verbindung zwischen Prozessor und Speicher kann heute meist 4 Byte auf einmal übertragen, also ist es günstig, wenn der int-Wert an einer Adresse steht, die durch 4 teilbar ist. da der char nur 1 Byte benötigt, wird das erreicht, indem noch 3 Füllbytes dazwischengeschoben werden (welchen Wert die haben, ist nicht festgelegt*) - damit hat die gesamte Struktur eine Größe von 8 Byte (c(1)+3+4(i)).
Wenn du das Padding ausschaltest, werden alle Elemente lückenlos hintereinander untergebracht, dadurch reduziert sich die Größe auf 5. Allerdings liegt der int jetzt nicht mehr geeignet im Speicher, um auf einen Schlag ausgelesen zu werden - also muß das Programm für einen Zugriff erst zwei 4-Byte-Blöcke lesen und daraus den int-Wert zusammenbauen (und in der anderen Richtung ihn wieder zerlegen und in Einzelteilen zurückschreiben).
* das bedeutet auch, daß du mitunter falsche Antworten erhältst, wenn du eine Struktur elementweise kopierst und dann per memcmp() vergleichst:
test t1 = {'a',4711},t2; t2.c=t1.c;t2.i=t1.i; cout<<(memcmp(t1,t2)==0)?"gleich":"verschieden";//was hier ausgegeben wird, hängt von den willkürlichen Werten der Padding-Bytes ab
-
CStoll schrieb:
OK, dann etwas ausführlicher: Das Padding dient dazu, die Position einzelner Member an "gutartigen" Speicheradressen auszurichten. Nehmen wir also eine etwas einfachere Struktur:
struct test { char c; int i; };Die Verbindung zwischen Prozessor und Speicher kann heute meist 4 Byte auf einmal übertragen, also ist es günstig, wenn der int-Wert an einer Adresse steht, die durch 4 teilbar ist. da der char nur 1 Byte benötigt, wird das erreicht, indem noch 3 Füllbytes dazwischengeschoben werden (welchen Wert die haben, ist nicht festgelegt*) - damit hat die gesamte Struktur eine Größe von 8 Byte (c(1)+3+4(i)).
Wenn du das Padding ausschaltest, werden alle Elemente lückenlos hintereinander untergebracht, dadurch reduziert sich die Größe auf 5. Allerdings liegt der int jetzt nicht mehr geeignet im Speicher, um auf einen Schlag ausgelesen zu werden - also muß das Programm für einen Zugriff erst zwei 4-Byte-Blöcke lesen und daraus den int-Wert zusammenbauen (und in der anderen Richtung ihn wieder zerlegen und in Einzelteilen zurückschreiben).
Wunderbar. Herzlichen Dank.
CStoll schrieb:
* das bedeutet auch, daß du mitunter falsche Antworten erhältst, wenn du eine Struktur elementweise kopierst und dann per memcmp() vergleichst:
test t1 = {'a',4711},t2; t2.c=t1.c;t2.i=t1.i; cout<<(memcmp(t1,t2)==0)?"gleich":"verschieden";//was hier ausgegeben wird, hängt von den willkürlichen Werten der Padding-Bytes abDas heißt ich könnte mit meinen efrigen memcmp Vergleichen bei allen zig Testdurchläufen immer Glück gehabt haben und sollte lieber jeden Member einzeln vergleichen.
P.S.: Vor jedem Schreiben eines Header wird dieser intialisiert mit
memset(&Header, 0x00, sizeof(MAIN_HEADER)). Das erwischt doch auch die Paddingbytes, sodass memcmp wiederum nicht glücklich verlief sondern "korrekt", oder?
-
Ja, memset() erwischt auch die Padding-Bytes.
(und bei deinem Ansatz oben (Daten schreiben, wieder einlesen und dann vergleichen) kommt auch aus Prinzip das richtige Ergebnis heraus, weil read() und write() ebenfalls alles ohne Rücksicht auf die interne Struktur lesen bzw. schreiben)
PS: Problematisch wird es, wenn du zwischen schreiben und lesen das Layout der Struktur geändert hast, d.h. wenn du z.B. einen Header schreibst, dann im Programm die Padding-Einstellungen änderst und mit der neuen Version diesen Header wieder auszulesen versuchst - dabei stimmen die Daten immer noch auf Byte-Ebene überein, aber die einzelnen Bytes gehören nicht mehr zu den selben Membern. Zum Beispiel bei meiner obigen test-Struktur:
//Lauf 1 mit Standard-Padding: test t = {'a',4711}; file.write((char*)&t,sizeof(test); //Ergebnis: 61 xx xx xx 00 00 12 67 //Lauf 2 compiliert mit #pragma pack(1) test t; file.read((char*)&t,sizeof(test); //Ergebnis: 61 xx xx xx 00 //in t.i kann jetzt alles mögliche drin stehen, aber bestimmt nicht 4711
-
CStoll schrieb:
PS: Problematisch wird es, wenn du zwischen schreiben und lesen das Layout der Struktur geändert hast, d.h. wenn du z.B. einen Header schreibst, dann im Programm die Padding-Einstellungen änderst und mit der neuen Version diesen Header wieder auszulesen versuchst - dabei stimmen die Daten immer noch auf Byte-Ebene überein, aber die einzelnen Bytes gehören nicht mehr zu den selben Membern. Zum Beispiel bei meiner obigen test-Struktur:
//Lauf 1 mit Standard-Padding: test t = {'a',4711}; file.write((char*)&t,sizeof(test); //Ergebnis: 61 xx xx xx 00 00 12 67 //Lauf 2 compiliert mit #pragma pack(1) test t; file.read((char*)&t,sizeof(test); //Ergebnis: 61 xx xx xx 00 //in t.i kann jetzt alles mögliche drin stehen, aber bestimmt nicht 4711Das sollte nicht passieren. Werde es einfach ohne eine #pragma pack Anweisung machen, da sie ja hier nicht nötig ist. Vielen Dank auch.