Alternative zu memcpy?
-
Wutz schrieb:
aha schrieb:
weil es schneller und weniger fehleranfällig ist,
Man sieht an deinem Code, dass dem eben nicht so ist. Je mehr Code du schreibst, desto mehr Fehler sind drin.
aha schrieb:
wenn ich einmal fread mache als 100x fread?
Wieviele Terabyte Daten liest du denn, dass du signifikante Zeitunterschiede erwartest? Ein Aberglauben vieler Anfänger, die meinen, durch eigene Code"optimierungen" das Programm verbessern zu müssen, dabei aber die eigentliche Aufgabenstellung ignorieren und auch noch massenhaft Fehler einbauen.
Schreib doch erstmal ein funktionsfähiges Programm, optimieren kannst du dann immer noch.
Deine Probleme rühren von unkoordiniertem bzw. gänzlich fehlendem Programmdesign her, du frickelst Bottom-Up rum anstatt Top-Down. (auch wieder ein klassischer Anfängerfehler)aha schrieb:
SeppJ schrieb:
Die ist schon klar, dass das Ergebnis dieser Aktion plattformabhängig ist?
ja das hats mit c so ansich. alternavivorschläge?
Deutsch kannst du aber schon, oder? Sieht aber nicht so aus.
C kapselt die Schnittstellen der jeweiligen Plattform und arbeitet somit zunächst mal plattformunabhängig, und geht, im Gegensatz vieler anderer Laiensprachen davon aus, dass der Entwickler weiß was er tut, in deinem Fall musst du also wissen, wer oder was Byteorder bedeutet und wie man mit C-Mitteln diese verarbeitet.ja ich weiß was big und little endian sind.
also deine beiträge werden auch immer herablassender und arroganter, und enthalten meist wenig inhalt.
wollt ich nur mal gesagt haben
also was ist jetzt?
-
struct zahlen zahlen1; ... size_t result = fread(&zahlen1, sizeof(zahlen1), 1, mystream); ...
-
Belli schrieb:
struct zahlen zahlen1; ... size_t result = fread(&zahlen1, sizeof(zahlen1), 1, mystream); ...
ja, aber auch hier ist das problem mit dem struct-padding nicht gelöst.
-
Ich habe oben noch etwas Relevantes in meinen Beitrag reineditiert. Ich hatte nicht erwartet, dass der Thread so schnell wächst.
-
ok, danke für die bisherigen antworten.
aber ich weiß nach wie vor nicht, was der "profi" weg ist.
daher hier nochmal ein praktisches beispiel:struct zahlen{ int zahl1; int zahl2; char zahl3; char zahl4[3]; }; char datei[]={0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x20};
das ist die ausgangslage. ich habe ein struct, und ich habe eine datei (--> byte-array). nun sollen daten aus dem array "datei" in ein struct vom typ "zahlen".
ich hätte jetzt jedes einzelne element mit memcpy eingelesen. aber anscheinend ist dies nicht der richtige weg?
vorschlag mit dem casten:
struct zahlen struct1=(zahlen) data;
das ist zwar ein einfacher und unkomplizierter weg, allerdings könnte es probleme mit struct-padding geben.
also was jetzt?
-
ja ok, aber... schrieb:
Belli schrieb:
struct zahlen zahlen1; ... size_t result = fread(&zahlen1, sizeof(zahlen1), 1, mystream); ...
ja, aber auch hier ist das problem mit dem struct-padding nicht gelöst.
Das ist hier genau so gut gelöst, wie bei memcpy, und dazu wurde eine Alternative gesucht!
-
Belli schrieb:
ja ok, aber... schrieb:
Belli schrieb:
struct zahlen zahlen1; ... size_t result = fread(&zahlen1, sizeof(zahlen1), 1, mystream); ...
ja, aber auch hier ist das problem mit dem struct-padding nicht gelöst.
Das ist hier genau so gut gelöst, wie bei memcpy, und dazu wurde eine Alternative gesucht!
ja das ist richtig, aber ich lese jedes element einzeln ein!
bitte meinen vorherigen post nicht übersehen!
-
Dann lies hier auch jedes Element einzeln ein:
fread(&zahlen1.zahl1, ...
-
Belli schrieb:
Dann lies hier auch jedes Element einzeln ein:
fread(&zahlen1.zahl1, ...
ja, ich suche hier alternativvorschläge.
mein gott, wie machen es denn andere programmierer? ich hab noch nie irgendwo gesehen, dass jedes element einzeln eingelesen wird!
bin ich denn der erste, der probleme mit diesem struct-padding hat?
-
Mach dich mal klar darüber, dass das struct-Padding bei den Methoden...
a) jedes struct direct über fread füllen
b) mit fread gelesenes bytearray via memcpy in struct kopieren
c) struct-Zeiger zeigt auf mit fread gelesenes bytearray... immer genau das gleiche Problem ist. Entweder deine Definition der struct passt oder halt nicht.
-
Weder memcpy, noch Casts, noch ein fread lösen auf magische Weise Portabilitätsprobleme. Das sind alles drei nur unterschiedliche Schreibweisen für die gleiche technische Aktion, die Rohdaten aus der Datei direkt so in den Speicher zu schreiben, wo sie dann als Elemente deines structs aufgefasst werden. Ein sehr cleverer Compiler könnte sogar für alle drei Varianten den gleichen Code erzeugen (ich glaube nicht, dass es so einen cleveren Compiler gibt, die Funktionsaufrufe sind eine ziemlich starke Optimierungshürde) oder zumindest sehr ähnlichen (dies kann ich mir realistisch vorstellen. Ich habe schon gesehen, wie Kopierschleifen zu memcpy-Aufrufen optimiert wurden).
Wenn du diese Probleme grundlegend von der Ursache her lösen willst, dann musst du dich von all diesem Gefummel auf Byteebene verabschieden. Wutz ist zwar nicht immer höflich, hat aber meistens Recht: Geh das Problem andersherum an! Definier genau, wie dein Dateiformat aussieht. Wenn es ein Binärformat sein soll (warum überhaupt? Ich halte dies für einen großen Nachteil wenn es Portabel sein soll), dann leg genau die Byteorder und ähnliches fest. Dann schreibst du eine Parsefunktion, die diese Daten plattformunabhängig in dein Strukturformat einliest (indem du die Werte aus dem Binärformat berechnest, anstatt sie einfach in den Speicher zu schreiben, so wie es ein scanf/atoi/strotl bei einem menschenlesbaren Format tun würde). Falls es dir nicht schnell genug sein sollte, dann kannst du noch optimierte Lesefunktionen für bestimmte Plattformen schreiben, die, unter Kenntnis des Formates und der Eigenschaften der Plattform, die Daten direkt richtig in das struct lesen. Und dies wäre dann auf der einen Plattform, die genau kompatibel zu deinem Binärformat ist, gerade ein fread/memcpy/Cast.
Oder du sparst dir das alles mit einem menschenlesbarem Format und machst es so wie die meisten Leute, denen es egal ist, ob das Programm am Anfang 0.0001 Sekunden zum Lesen oder 0.0002 Sekunden benötigt.
ich hab noch nie irgendwo gesehen, dass jedes element einzeln eingelesen wird!
Was hast du denn überhaupt schon gesehen? Ist das nicht deine lang gesuchte Alternative?
-
also soll ich es jetzt casten?
-
SeppJ schrieb:
Weder memcpy, noch Casts, noch ein fread lösen auf magische Weise Portabilitätsprobleme. Das sind alles drei nur unterschiedliche Schreibweisen für die gleiche technische Aktion, die Rohdaten aus der Datei direkt so in den Speicher zu schreiben, wo sie dann als Elemente deines structs aufgefasst werden. Ein sehr cleverer Compiler könnte sogar für alle drei Varianten den gleichen Code erzeugen (ich glaube nicht, dass es so einen cleveren Compiler gibt, die Funktionsaufrufe sind eine ziemlich starke Optimierungshürde) oder zumindest sehr ähnlichen (dies kann ich mir realistisch vorstellen. Ich habe schon gesehen, wie Kopierschleifen zu memcpy-Aufrufen optimiert wurden).
Wenn du diese Probleme grundlegend von der Ursache her lösen willst, dann musst du dich von all diesem Gefummel auf Byteebene verabschieden. Wutz ist zwar nicht immer höflich, hat aber meistens Recht: Geh das Problem andersherum an! Definier genau, wie dein Dateiformat aussieht. Wenn es ein Binärformat sein soll (warum überhaupt? Ich halte dies für einen großen Nachteil wenn es Portabel sein soll), dann leg genau die Byteorder und ähnliches fest. Dann schreibst du eine Parsefunktion, die diese Daten plattformunabhängig in dein Strukturformat einliest (indem du die Werte aus dem Binärformat berechnest, anstatt sie einfach in den Speicher zu schreiben, so wie es ein scanf/atoi/strotl bei einem menschenlesbaren Format tun würde). Falls es dir nicht schnell genug sein sollte, dann kannst du noch optimierte Lesefunktionen für bestimmte Plattformen schreiben, die, unter Kenntnis des Formates und der Eigenschaften der Plattform, die Daten direkt richtig in das struct lesen. Und dies wäre dann auf der einen Plattform, die genau kompatibel zu deinem Binärformat ist, gerade ein fread/memcpy/Cast.
praktisches beispiel?
also eigentlich gings mir hier hauptsächlich um dieses struct-padding, und nicht um die byte order (warum sollte ich mich um die scheißen?). auf den meisten pcs hat man doch sowieso little-endian.
-
............... schrieb:
praktisches beispiel?
Nur wenn du mir eine gute Begründung gibst, wieso du nicht den einfachen Weg nimmst.
-
SeppJ schrieb:
............... schrieb:
praktisches beispiel?
Nur wenn du mir eine gute Begründung gibst, wieso du nicht den einfachen Weg nimmst.
ja was ist jetzt der einfache weg? casten?
-
also jetzt doch casten? schrieb:
ja was ist jetzt der einfache weg? casten?
Menschenlesbares Format. Oder falls doch Binärformat (warum?) dann genau definiert ohne optimiertes Lesen sondern mit einem Parser im Stil eines menschenlesbaren Formates.*
Wenn du wirklich auf Byteebene rumfrickeln möchtest, dann musst du auch genau verstehen, was du da tust. Ich (und wohl auch die anderen Antwortgeber in diesem Thread) habe hingegen den Eindruck, dass du noch ziemlicher Anfänger bist, der zudem die falschen Prioritäten setzt. Lern erst einmal besser zu programmieren! Tieferes Verständnis kommt dann schon noch. Es ist jedenfalls nicht damit getan, ein Beispiel aus einem Forum abzuschreiben und dieses dann für die eigenen Zwecke zu verändern. Das wird schiefgehen. Dies ist einer der Gründe, wieso ich so zögerlich bin, dir ein konkretes Beispiel für das Bytegefrickel zu geben.
*: Bevor du mit einem Speicherplatzargument kommst: Es gibt komfortable Bibliotheken, die du einfach zwischen Lese-/Schreiblogik und eine komprimierte Datei schalten kannst.
-
SeppJ schrieb:
also jetzt doch casten? schrieb:
ja was ist jetzt der einfache weg? casten?
Menschenlesbares Format. Oder falls doch (warum?) Binärformat dann genau definiert ohne optimiertes Lesen.
Wenn du wirklich auf Byteebene rumfrickeln möchtest, dann musst du auch genau verstehen, was du da tust. Ich (und wohl auch die anderen Antwortgeber in diesem Thread) habe hingegen den Eindruck, dass du noch ziemlicher Anfänger bist, der zudem die falschen Prioritäten setzt. Lern erst einmal besser zu programmieren! Tieferes Verständnis kommt dann schon noch. Es ist jedenfalls nicht damit getan, ein Beispiel aus einem Forum abzuschreiben und dieses dann für die eigenen Zwecke zu verändern. Das wird schiefgehen. Dies ist einer der Gründe, wieso ich so zögerlich bin, dir ein konkretes Beispiel für das Bytegefrickel zu geben.
dein menschenlesbarer weg ist mir zu kompliziert, keine ahnung was genau du meinst.
naja ich glaub ich lass das mal lieber bei dem casten. andere scheinen sich da mit byte-padding auch keine gedanken zu machen, also warum?
-
Üblicherweise macht man das ungefähr so:
#include <stdio.h> #include <stdint.h> #include <assert.h> typedef struct Record { int32_t a, b, c; } Record; /*schreibt den Wert portabel in Big Endian in die Datei*/ void serialize_int32(FILE *out, int32_t value) { fputc((value >> 24) & 0xff, out); fputc((value >> 16) & 0xff, out); fputc((value >> 8) & 0xff, out); fputc((value ) & 0xff, out); } /*das gleiche in lesender Richtung*/ int32_t deserialize_int32(FILE *in) { int32_t result = 0; /*Der Cast ist da, weil int nicht unbedingt 32 Bit haben muss.*/ result |= ((int32_t)fgetc(in) << 24); result |= ((int32_t)fgetc(in) << 16); result |= ((int32_t)fgetc(in) << 8); result |= ((int32_t)fgetc(in) ); return result; } void serialize_record(FILE *out, Record const *record) { serialize_int32(out, record->a); serialize_int32(out, record->b); serialize_int32(out, record->c); } void deserialize_record(FILE *in, Record *record) { record->a = deserialize_int32(in); record->b = deserialize_int32(in); record->c = deserialize_int32(in); } static Record const TestData = {1, 2, 3}; static char const * const FileName = "test.bin"; int main(void) { Record deserialized; FILE *file = fopen(FileName, "wb"); if (!file) { return 1; } serialize_record(file, &TestData); fclose(file); file = fopen(FileName, "rb"); if (!file) { return 2; } deserialize_record(file, &deserialized); if (ferror(file)) { printf("File could not be read\n"); fclose(file); return 3; } assert(TestData.a == deserialized.a); assert(TestData.b == deserialized.b); assert(TestData.c == deserialized.c); fclose(file); return 0; }
Kein einziges
fread
odermemcpy
ist nötig.
Schnell genug ist das und vor allem lesbar und portabel.
-
struct-padding schrieb:
wie machen es denn anwendungen, z.b. der linuxkernel? die dateisysteme dort werden ja auch durch structs implementiert. wird da etwas mit memcpy eingelsen oder direkt gecastet? weiß das jemand?
Weder noch. Das On-Disk-Format eines Linux-Dateisystems spezifiert für jedes Feld die genaue Größe, die zu erwartende Byteorder und einen Offset zum Beginn der Struktur.
Ein JBD2-Journal-Header (u.a. von Ext3 und Ext4 verwendet) ist etwa so definiert:
Offset Typ Name 0 be32 h_magic 4 be32 h_blocktype 8 be32 h_sequence
Das heißt, dass jedes der drei Felder 32 Bit groß ist und in Big-Endian-Byteorder vorliegen muss. Außerdem schließen die Felder direkt aneinander an, es gibt kein Padding.
An diese Spezifikation muss sich jeder halten. Tut er es nicht, ist das was er schreibt einfach kein gültiger JBD2-Blockheader mehr.
Außerhalb des Journals nutzt Ext4 auch Strukturen mit LE-Byteorder. Wie bei Binärformaten üblich, muss man hier einfach genau darauf achten, was vereinbart wurde. Daher kann ich Sepp nur zustimmen: sofern es irgendwie möglich ist, benutze ein textuelles Format. Da ersparst du dir sehr viel Fummelei und bist trotzdem portabel.