Ich habe gelesen, dass die Reihenfolge der Bitfelder innerhalb einer Struktur plattformspezifisch ist. Was passiert, wenn ich verschiedene compilerspezifische Verpackungsoptionen verwende? Werden diese Garantiedaten in der richtigen Reihenfolge gespeichert, in der sie geschrieben wurden? Beispielsweise:
struct Message
{
unsigned int version : 3;
unsigned int type : 1;
unsigned int id : 5;
unsigned int data : 6;
} __attribute__ ((__packed__));
Auf einem Intel-Prozessor mit dem GCC-Compiler wurden die Felder wie gezeigt im Speicher angeordnet. Message.version
war die ersten 3 Bits im Puffer und Message.type
folgte. Wenn ich für verschiedene Compiler äquivalente Strukturpackoptionen finde, ist dies plattformübergreifend?
c++
c
bit-manipulation
endianness
bit
Dewald
quelle
quelle
Antworten:
Nein, es wird nicht vollständig tragbar sein. Packoptionen für Strukturen sind Erweiterungen und selbst nicht vollständig portierbar. Darüber hinaus heißt es in C99 §6.7.2.1, Absatz 10: "Die Reihenfolge der Zuordnung von Bitfeldern innerhalb einer Einheit (von hoher zu niedriger Ordnung oder von niedriger zu hoher Ordnung) ist implementierungsdefiniert."
Selbst ein einzelner Compiler kann das Bitfeld beispielsweise je nach Endianness der Zielplattform unterschiedlich anordnen.
quelle
packed
erzwingen Bestellung: stackoverflow.com/questions/1756811/... wie Bitreihenfolge erzwingen: stackoverflow.com/questions/6728218/gcc-compiler-bit-orderBitfelder variieren stark von Compiler zu Compiler, sorry.
Mit GCC legen Big-Endian-Maschinen zuerst das große Ende und die kleinen Endian-Maschinen zuerst das kleine Ende an.
K & R sagt: "Benachbarte [Bit-] Feldmitglieder von Strukturen werden in implementierungsabhängiger Richtung in implementierungsabhängige Speichereinheiten gepackt. Wenn ein Feld, das einem anderen Feld folgt, nicht passt ... kann es zwischen Einheiten aufgeteilt werden oder die Einheit kann es sein." gepolstert. Ein unbenanntes Feld mit der Breite 0 erzwingt diese Polsterung ... "
Wenn Sie ein maschinenunabhängiges Binärlayout benötigen, müssen Sie dies daher selbst tun.
Diese letzte Aussage gilt auch für Nicht-Bitfelder aufgrund des Auffüllens - jedoch scheinen alle Compiler eine Möglichkeit zu haben, das Byte-Packen einer Struktur zu erzwingen, wie Sie bereits für GCC entdeckt haben.
quelle
Bitfelder sollten vermieden werden - sie sind selbst für dieselbe Plattform nicht sehr portabel zwischen Compilern. aus der C99-Norm 6.7.2.1/10 - "Struktur- und Vereinigungsspezifizierer" (die C90-Norm enthält ähnliche Formulierungen):
Sie können nicht garantieren, ob ein Bitfeld eine int-Grenze überspannt oder nicht, und Sie können nicht angeben, ob ein Bitfeld am unteren Ende des int oder am oberen Ende des int beginnt (dies ist unabhängig davon, ob der Prozessor dies ist Big-Endian oder Little-Endian).
Bevorzugen Sie Bitmasken. Verwenden Sie Inlines (oder sogar Makros), um die Bits zu setzen, zu löschen und zu testen.
quelle
_BIT_FIELDS_LTOH
und_BIT_FIELDS_HTOL
Endianness spricht von Bytereihenfolgen, nicht von Bitreihenfolgen. Heutzutage ist es zu 99% sicher, dass Bitreihenfolgen festgelegt sind. Bei der Verwendung von Bitfeldern sollte jedoch die Endianness berücksichtigt werden. Siehe das folgende Beispiel.
quelle
Wahrscheinlich die meiste Zeit, aber setzen Sie die Farm nicht darauf, denn wenn Sie sich irren, verlieren Sie viel.
Wenn Sie wirklich, wirklich identische Binärinformationen benötigen, müssen Sie Bitfelder mit Bitmasken erstellen - z. B. verwenden Sie einen vorzeichenlosen Kurzschluss (16 Bit) für die Nachricht und erstellen dann versionMask = 0xE000, um die drei obersten Bits darzustellen.
Es gibt ein ähnliches Problem bei der Ausrichtung innerhalb von Strukturen. Zum Beispiel sind Sparc-, PowerPC- und 680x0-CPUs alle Big-Endian-CPUs, und die übliche Standardeinstellung für Sparc- und PowerPC-Compiler besteht darin, Strukturelemente an 4-Byte-Grenzen auszurichten. Ein Compiler, den ich für 680x0 verwendet habe, ist jedoch nur an 2-Byte-Grenzen ausgerichtet - und es gab keine Option, die Ausrichtung zu ändern!
Bei einigen Strukturen sind die Größen bei Sparc und PowerPC identisch, bei 680 x 0 jedoch kleiner, und einige der Mitglieder befinden sich in unterschiedlichen Speicherversätzen innerhalb der Struktur.
Dies war ein Problem bei einem Projekt, an dem ich gearbeitet habe, da ein auf Sparc ausgeführter Serverprozess einen Client abfragte und herausfand, dass es sich um Big-Endian handelte, und davon ausging, dass er nur binäre Strukturen im Netzwerk herausspritzen und der Client damit umgehen konnte. Und das funktionierte gut auf PowerPC-Clients und stürzte auf 680x0-Clients stark ab. Ich habe den Code nicht geschrieben und es hat eine ganze Weile gedauert, bis ich das Problem gefunden habe. Aber es war einfach zu reparieren, sobald ich es tat.
quelle
Vielen Dank an @BenVoigt für Ihren sehr nützlichen Kommentar
Linux Quelle tut ein bisschen Feld verwenden , um eine externe Struktur anzupassen: /usr/include/linux/ip.h hat diesen Code für das erste Byte einer IP - Datagramms
Jedoch in Anbetracht Ihres Kommentars Ich gebe es auf, das für den Multi-Byte - Bit - Feld , um Arbeit zu bekommen frag_off .
quelle
Die beste Antwort ist natürlich, eine Klasse zu verwenden, die Bitfelder als Stream liest / schreibt. Die Verwendung der C-Bit-Feldstruktur ist einfach nicht garantiert. Ganz zu schweigen davon, dass es als unprofessionell / faul / dumm angesehen wird, dies in der realen Codierung zu verwenden.
quelle