Ich möchte einen Teil der Daten in einigen C-Structs finden, um sie teilweise zu serialisieren / deserialisieren, indem ich die Bytes vom Speicher auf die Festplatte schreibe und umgekehrt.
Die Strukturen sind nicht im Voraus bekannt, sie sind dynamisch mit meinem eigenen C-Code-Generator (sowie dem Code, der es serialisieren wird) gebaut. Die serialisierbaren Felder werden am Anfang der Struktur platziert.
Nehmen wir an, wir haben eine Struktur mit 4 Feldern, und die ersten beiden müssen serialisiert werden:
%Vor% Ich habe vor, den Zeiger auf die Strukturvariable zu nehmen und die n
Bytes zu schreiben / zu lesen, die diese beiden ersten Felder ( x1, x2
) abdecken. Ich denke nicht, dass ich mich um die Ausrichtung / das Packen kümmern muss, weil ich nicht beabsichtige, dass die Serialisierung verschiedene Kompilationen überlebt (nur eine einzigartige ausführbare Datei wird erwartet, dass sie die Daten liest / schreibt). Und da ich mich auf eine breite Palette von Compilerarchitekturen konzentriere, möchte ich keine Annahmen zu Alignment-Packing- oder Compiler-spezifischen Tricks machen.
Dann muss ich Bytes zählen. Und ich kann nicht einfach sizeof(st.x1)+sizeof(st.x2)
wegen Alingment-Padding machen. Also, ich plane Zeiger vom Beginn der Struktur bis zum ersten "nicht persistenten" Feld subtrahieren:
Das scheint zu funktionieren. Und es kann in einem Makro platziert werden.
(Für den Datensatz: Ich habe auch versucht, ein anderes Makro zu schreiben, das keine Instanz der Struktur benötigt, so etwas wie das , aber das scheint hier nicht möglich - aber das macht mir nichts aus.
Meine Frage: Ist das korrekt und sicher? Kannst du eine potenzielle Fallstricke oder einen besseren Ansatz sehen?
Unter anderem: Nehmen C-Standard (und De-facto-Compiler) an, dass die Strukturfelder im Speicher in der Reihenfolge liegen, in der sie in source definiert sind? Das ist wahrscheinlich eine dumme Frage, aber ich möchte sicher sein ...
UPDATE: Einige Schlussfolgerungen aus den Antworten und meinen eigenen Ergebnissen:
Mit meinem Ansatz scheint es kein Problem zu geben. Insbesondere schreibt C vor, dass Strukturfelder niemals die Reihenfolge ändern.
Man könnte auch (wie von einer Antwort vorgeschlagen) vom letzten persistenten Feld zählen und seine Größe hinzufügen: (char*)&myst.x2 + sizeof(&myst.x2) - (char*)(&myst)
. Das wäre äquivalent, außer dass es nicht die Füllbytes (falls vorhanden) für das letzte Feld enthalten würde. Ein sehr kleiner Vorteil - und ein sehr kleiner Nachteil darin, weniger einfach zu sein.
Aber die akzeptierte Antwort mit offsetof
scheint mir vorzuziehen. Es ist klar-expressive und reine Kompilierzeit, es erfordert keine Instanz der Struktur. Es scheint weiterhin Standard zu sein, der in jedem Compiler verfügbar ist.
Wenn ein Kompilierzeitkonstrukt nicht benötigt wird und eine Instanz der Struktur verfügbar ist (wie in meinem Szenario), sind beide Lösungen im Wesentlichen äquivalent.
Haben Sie sich die Einrichtung offsetof
angeschaut? Sie gibt den Offset eines Elements vom Anfang einer Struktur zurück. So gibt offsetof (st, x2)
den Offset von x2
vom Start der Struktur zurück. In Ihrem Beispiel gibt offsetof (st, x2) + sizeof(st.x2)
die Anzahl der Bytes der serialisierten Komponenten an.
Das ist ziemlich ähnlich zu dem, was Sie jetzt tun, Sie müssen nur die Auffüllung nach x2 ignorieren und ein selten verwendetes Stück C verwenden.
Der C-Compiler kann Padding-Bytes für die Ausrichtung einfügen, aber die Strukturvariablen nicht neu anordnen.
Ein saubererer Weg könnte sein, einfach eine zweite Struktur für sizeof () Zwecke zu definieren, die die Anfangsvariablen der gewünschten Struktur enthält. Der Compiler garantiert, dass 2 Struts, die die gleichen Variablen in der gleichen Reihenfolge haben, auf die gleiche Weise angeordnet werden.
Ich stimme für das KISS-Prinzip. Schreiben Sie in die Datei Element für Element und Sie sind garantiert keine Compiler-Abhängigkeiten.