Speicherinterne Darstellung einer Struktur
-
Hallo.
Ich muss herausfinden, wie eine Struktur im Speicher abgelegt wird. Also welche Folge von Bits entsteht. Ich habe das soweit ausgetüftelt, möchte Euch aber bitten, das mal anhand meines Beispiels zu kontrollieren.
Ich habe folgende einfache Struktur:
typedef struct { unsigned long ganzzahl; float kommazahl; } test;die ganzahl = 2763244241 (dezimal) bzw 0xA4B3C2D1 (hexadezimal) ergibt mit dem Little-Endian-System die Bitfolge:
1010 0100 1011 0011 1100 0010 1101 0001 (binär)
D 1 C 2 B 3 A 4 (hex)die kommazahl 11.25 ergibt
- das positive Vorzeichenbit 0
- der Exponent 3 addiert mit dem Bias-Wert 127 ergibt die Charakteristik 130 (Bitfolge: 10000010)
- die Mantisse 3407872 (Bitfolge: 01101000000000000000000)
damit hat die kommazahl die Bitfolge:
0 10000010 01101000000000000000000Die Struktur wird damit wie folgt im Speicher abgelegt:
1010 0100 1011 0011 1100 0010 1101 0001 0 10000010 01101000000000000000000Ist dies korrekt?
Bitte jetzt nicht jede Umrechnung ins Binärsystem prüfen!
Nur die ganz grundsätzlichen Fragen:
- Elemente einer Struktur werden in der Reihenfolge der Deklaration im Speicher abgelegt? Ansonsten werden keine weiteren Daten gespeichert und es gibt keine Aufteilung der Bitfolge in verschiedene Datenblöcke?
- Die Reihenfolge der Bytes der unsigned long ist korrekt?
- Die einzelnen Bytes der unsigned long sind korrekt? Innerhalb der Bytes wird also praktisch ein Big-Endian-System bei den Bits eingesetzt?
- Bei der float-Variable ist die Reihenfolge Vorzeichen-Exponent-Mantisse korrekt?
- Eine float-Variable verwendet 8 Bit für den Exponenten und damit 23 für die Mantisse?Habe ich sonstige Fehler gemacht?
Vielen Dank!
Gruß,
chris_f
-
Eventuelles padding noch berücksichtigen (Sollte in dieser Struktur allerdings nicht der Fall sein).
Den Rest habe ich nur überflogen, sieht aber ganz gut aus...
-
Vielen Dank schon mal!
Padding bekomme ich meines wissens nach nur bei 2Byte-Integers oder ähnlichem.
Solange ich nur unsigned long und floats habe dürfte das nicht auftreten.Zumindest beim Pentium 4 müsste das so gewesen sein.
-
Du hast weder eine Garantie, wie lang deine Datentypen sind, noch wie diese im Speicher liegen.
Die einzige Garantie ist, dass Du ein struct in sein erstes Element casten kannst.
Alles andere, was Du da z.B. gemacht hast, bezieht sich auf bestimmte Compiler, Platformen, etc..
-
Hmm...
Das klingt jetzt nicht so gut.Jetzt habe ich zwei konträre Meinungen und kann leider keine Entscheidung fällen.
Solange ich mich auf Systemen mit x86-Prozessor bewege, bin ich zumindest mit dem Little-Endian-System fein raus. Die Gleitkommadarstellung ist meines Wissens nach auf allen x86-Systemen ebenfalls korrekt.
Soviel habe ich herausfinden können.
Bleibt nur noch die Frage, wie sich der Rechner dann eine Struktur bastelt.Kann mir noch jemand helfen?
Gruß,
chris_f
-
Wie schon gesagt kann sich das der Compiler so zusammen basteln wie er will (mit der Ausname von dem was 7H3 N4C3R sagte)
-
Das ist sehr Schade. Hatte gehofft, das die Zuordnung eindeutig ist.
Danke euch allen.
Gruß,
chris_f
-
Die Zuordnung wird schon eindeutiger, wenn Du ein unsigned char array verwendest, welches in einem Stück deklariert und initialisiert wird. Und dann die richtigen Werte zuweisen.
Die Richtung ist aber immer unbestimmt.
Blackbird
-
Um Dir die Hoffnung nicht ganz zu nehmen: Irgendwie muss es ja im Speicher dargestellt werden. Die Länge eines unsigned long kannst Du mit dem sizeof Operator abfragen. Auf einer typischen x86 Plattform sollten das 64 Bit sein. Und typischer Weise auch Little Endian. Mit ein Wenig Glück werden floats auch in IEEE-Darstellung abgelegt. Ein Float sollte 32 Bit haben. Und Padding erfolgt normalerweise auch nur auf die nächste 32-Bit oder 64-Bit Adresse (ist meist ein Compiler-Setting).
Die Sache ist halt, dass Dir niemand das garantiert. Ein Wechsel des Compilers kann schon dafür sorgen, dass das nicht mehr funktioniert. Wenn es Dir reicht, dass es für deinen Compiler in der jetzigen Version auf deiner derzeitigen Plattform funktioniert, ist das doch schon gut. Portabel in irgendeiner Art und Weise ist es aber nicht.
Little oder Big Endian kannst Du auch zur Laufzeit bestimmen (vorausgesetzt, uint ist größer als uchar):
unsigned int i = 1; unsigned char* c = reinterpret_cast<unsigned char*>( &i); if( *c != 0) { // Little Endian } else { // Big Endian }Ich hoffe mal, ich habe Little und Big jetzt nicht verdreht
Ansonsten, tausch es einfach.
-
Und um dir noch ein bißchen mehr Hoffnung zu machen: In der Praxis kannst du, zumindest auf den üblichen Plattformen und Compilern, von folgendem ausgehen:
- Die Felder werden in der Reihenfolge der Deklaration abgelegt.
- Der Compiler fügt (wenn man es nicht anders einstellt) Padding-Bytes ein, soweit notwendig.
- Die Elemente eines Arrays werden in aufsteigender Reihenfolge abgelegt. Das ist definitiv garantiert. Zudem ist adressof(Element 1) = addressof(Element 0) + sizeof(Elementtyp), usw.
- Das Alignment einer Struktur richtet sich nach dem Alignment ihres größten Felds; wenn z.B. das größte Feld ein 64bit integer ist, dann hat auch die Struktur ein Alignment von 8 Byte.
- Daher kann es sein, daß am Ende einer Struktur noch Padding-Bytes eingefügt werden, damit direkt danach eine weiter solche Struktur mit richtigem Alignment liegen kann (ist für Arrays wichtig).Wieviel davon vom Standard garantiert ist, weiß ich nicht. Aber in der Praxis wäre C/C++ als systemnahe Sprache unbrauchbar, wenn diese Garantien nicht zumindest De-Facto-Garantien wären.
Gruß
Rolf
-
Wenn du es tatsächlich in einem Programm verwenden willst (fragt sich zwar wieso, aber du wirst deine Gründe haben), dann mach doch folgendes:
Überprüfe, ob deine Annahme stimmt und die zahlen im Speicher so abgelegt werden wie du denkst. Bastle dir einen entsprechenden Struct, bei dem die byteweise Darstellung eindeutig ist. (z.B. der unsigned long als 1001 1000 0010 1101)
Dann lass dir einen Pointer auf den Anfang des structes zurückgeben sowie ein sizeof(mystruct). Wenn das sizeof stimmt musst du nur noch den pointer auf einen unsigned char pointer umcasten und byte für byte durchgehen und nachschauen ob du die bytes vorfindest die du erwartest. Falls ja -> Glückwunsch, falls nicht -> Programm beenden. Die Methode kann dir sogar noch mehr verraten. Du kannst dir ja vorher einige mögliche Verhaltensweisen des Compilers/der Plattform denken und dich darauf einrichten, dass sie al vorkommen können (Big Endian/Little Endian, padding auf 32 bit oder 64 bit etc. Wenn dein sizeof(mystruct) dir jetzt 128 bit liefert weißt du z.B. dass du ein padding auf 64 bit hast. Und wenn die einzelnen bytes deines ulong "falschrum" im Speicher liegen hast du ein big Endian System und kannst dich drauf einrichten...
Ist zwar alles etwas umständlich, aber wer so tief in die Trickkiste greifen muss dass er sich um die Speicherbelegung sorgen macht, der wird sich von solchen banalitäten nicht abschrecken lassen
-
Das hört sich ja alles schon viel besser an!
Ich werde das ganz einfach mal ausprobieren. Die Anwendung um die es hier geht ist sehr spezifisch und muß daher nur in einer Umgebung funktionieren. Wenn es doch nicht klappt, müsste ich mir mit der von pumuckl vorgeschlagenen Methode behelfen können. Auf alle Fälle werde ich das ganze so aufbauen, dass ich das noch nachträglich einbauen kann.
Vielen Dank euch allen!
Gruß,
chris_f