C/C++ Problem mit struct - Byte-Verschiebung

Gast #1595963
Lesenswert?

Hallo erst einmal zusammen. Ich habe ein kleines C-Problem mit einem 
struct. Schon mal im Voraus, ich benutze Dev C++ 4.9.9.2.

Jetzt zum Programm. Aus der folgenden Datei soll der markierte Teil in 
ein struct eingelesen werden.

test.dat

00 00 |0A 00 00 00 FF 00 00 00 08 FF 00 00 00 00| 00 00

Wenn ich jetzt im struct unsigned char map_height; verwende wird der 
folgende Teil korrekt eingelesen:

00 00 |0A 00 00 00 FF 00 00 00 08 FF| 00 00 00 00 00 00

Wenn ich jetzt aber unsigned int map_height; verwende erhalte ich als 
Ausgabe für map_height keine 255. Ich habe mehr oder weniger das Gefühl, 
dass beim Marker1 der ein char ist 4 Byte statt einem eingelesen werden 
und darum alles um 3 Byte verrutscht. Wenn ich die test.dat wie folgt 
verändere erhalte ich für map_height eine 255!!

00 00 0A 00 00 00 FF 00 00 00 08 FF 00 00 FF 00 00 00

Können mir der C-Experten evtl. sagen, was ich falsch mache oder ob es 
ein Bug oder Einstellungsfehler ist??

1
#include <stdio.h>
2
#include <stdlib.h>
3

4
unsigned char data[200];
5

6
typedef struct{
7
  unsigned int ID;
8
  unsigned int map_length;
9
  unsigned char Marker_1;
10
  unsigned int map_height;
11
  //unsigned char map_height;
12
} MAP_HEADER;
13

14
int main(int argc, char *argv[])
15
{
16
   FILE *fp;
17
   int filesize;
18
   char filename[81]="test.dat";
19
   MAP_HEADER *map;
20
   int i;
21
  
22
   if ((fp = fopen (filename, "rb")) == NULL)
23
   {
24
      printf("%s nicht vorhanden\n",filename);
25
      return 1;
26
   }
27
   else
28
   {
29
      printf("Datei gefunden\n"); 
30
      fread (data, 1, sizeof (data), fp);
31
      filesize=ftell(fp);
32
      fclose (fp);
33
   }
34
   
35
   map= (MAP_HEADER*)&data[2];
36
   
37
   printf("ID: %x\n",map->ID);
38
   printf("Laenge: %d\n",map->map_length);
39
   printf("Marker: %d\n",map->Marker_1);
40
   printf("Hoehe: %d\n",map->map_height);
41
  
42
  system("PAUSE");  
43
  return 0;
44
}
Angehängte Dateien:
#1595991
Lesenswert?

ich schrieb:
> Können mir der C-Experten evtl. sagen, was ich falsch mache oder ob es
> ein Bug oder Einstellungsfehler ist??

Die Strukturen legt der Compiler 4 Byte aligned an. Du must dem Compiler 
über eine Kommandozeilen-Option oder evtl. als Attribut der Struktur 
mitteilen, dass er die Strukturen packen soll (packed). Dann legt er sie 
exakt so an, wie du möchtest.

Warum er allerdings bei deiner ersten Variante die beiden chars nicht 4 
Byte aligned, weiß ich gerade auch nicht. Evtl. weil sie am Ende stehen.
#1595993
Lesenswert?

ich schrieb:

> Wenn ich jetzt aber unsigned int map_height; verwende erhalte ich als
> Ausgabe für map_height keine 255. Ich habe mehr oder weniger das Gefühl,
> dass beim Marker1 der ein char ist 4 Byte statt einem eingelesen werden
> und darum alles um 3 Byte verrutscht.

Nicht ganz, aber fast.

> Können mir der C-Experten evtl. sagen, was ich falsch mache oder ob es
> ein Bug oder Einstellungsfehler ist??

Du beachtest nicht die Probleme, die entstehen, wenn man Binärdaten auf 
einen Rutsch einfach in eine Struktur liest. In deinem Fall lautet das 
Stichwort "alignment". Je nach Architektur ist ein Zugriff auf eine 
Variable mit falschem Alignment langsam oder gar nicht durchführbar. 
Deshalb werden in Strukturen ggf. ungenutzte Bytes eingefügt, um das 
korrekte Alignment aller Elemente zu gewährleisten. Bei dir wird der 
Compiler daher nach Marker1 drei leer-Bytes eingefügt haben, damit 
map_height wieder bei einem Vielfachen von 4 beginnt (da bei dem von dir 
verwendeten Compiler int 4 Bytes groß ist).

900ss D. schrieb:
> Die Strukturen legt der Compiler 4 Byte aligned an. Du must dem Compiler
> über eine Kommandozeilen-Option oder evtl. als Attribut der Struktur
> mitteilen, dass er die Strukturen packen soll (packed). Dann legt er sie
> exakt so an, wie du möchtest.

Ist allerdings recht unportabel. Aber das ist das Einlesen von 
Binärdaten ohne weitere Vorkehrungen ja sowieso.

> Warum er allerdings bei deiner ersten Variante die beiden chars nicht 4
> Byte aligned, weiß ich gerade auch nicht.

Warum sollte er das tun?
Gast #1596003
Lesenswert?

Weiters wirst du genau dann wieder stolpern, wenn die Endianness nicht 
mehr stimmt. Dann nämlich bekommst du Probleme, wenn du Multibytes 
(16-Bit-Worte, 32-Bit-Integer usw.) direkt und Binär aus der Datei 
holst.
#1596010
Lesenswert?

Sven P. schrieb:
> wenn du Multibytes
> (16-Bit-Worte, 32-Bit-Integer usw.) direkt und Binär aus der Datei
> holst.

Und was macht er gerade? Er öffnet die Datei mit "rb" (read binary). 
Dann bekommst du Daten direkt als binary. Und ob du 1 oder 3 "int" 
hintereinander holst ist dann egal.
Gast #1596032
Lesenswert?

Ja, das ist egal. Aber wie die einzelnen ints kommen, das ist nicht 
egal. Wenn in der Datei zuerst das höchstwertige Byte steht, du aber auf 
einer Little-Endian-Maschine arbeitest, steht nachher ein falscher 
Zahlenwert im int. In dem Fall würde fread zuerst ein Byte lesen 
(höchstwertiges), dann an die erste Speicherstelle schreiben 
(niederwertigstes Byte der vier Bytes des Integers) usw.
Gast #1596051
Lesenswert?

Kann mir noch jemand einen Tipp geben, nach was ich genau suchen muss?
Stichwort:
- endianness - noch nie gehört
- packed - schon mal gesehen, da muss ich mal suchen...

Oder mit anderen Worten, wie schwer ist eine Anpassung, so dass das 
passiert was ich möchte?
Gast #1596059
Lesenswert?

Ich habe es mal so versucht. Passt jetzt. Hoffe ich zu mindestens.
1
typedef struct{
2
  unsigned int ID;
3
  unsigned int map_length;
4
  unsigned char Marker_1;
5
  unsigned int map_height;
6
  //unsigned char map_height;
7
}__attribute__ ((packed))MAP_HEADER;
#1596070
Lesenswert?

Rolf Magnus schrieb:
> Am besten informierst du dich, was "endianness" ist und antwortest
> danach nochmal.

Warum?

Der TO macht das schon, also multibyte (int) einlesen im binary Format. 
Und es funktioniert. Seine Daten liegen für eine x86 Architektur (nehme 
ich jetzt mal an) richtig in der Datei. Ob durch Zufall oder nicht... 
keine Ahnung. Er wird nicht mehr darüber stolpern in seinem jetzigen 
Projekt.

Deshalb schrieb ich: er macht genau das (richtig).

Einfach anzunehmen, dass die Endianness nicht mehr stimmt... warum? 
Sollte sie sich in seinem Projekt einfach ändern?
#1596074
Lesenswert?

"ich" schrieb:
> Ich habe es mal so versucht. Passt jetzt. Hoffe ich zu mindestens.

Prima. Jetzt sollte es funktionieren.
Da du nicht weißt, was endianness ist, informiere dich wie A.K. schrieb 
im Wikipedia. Ist schon wichtig zu wissen. Aber für deinen jetzigen Fall 
ist es richtig.
Gast #1596098
Lesenswert?

900ss D. schrieb:
> Der TO macht das schon, also multibyte (int) einlesen im binary Format.
> Und es funktioniert. Seine Daten liegen für eine x86 Architektur (nehme
> ich jetzt mal an) richtig in der Datei. Ob durch Zufall oder nicht...
> keine Ahnung. Er wird nicht mehr darüber stolpern in seinem jetzigen
> Projekt.
>
> Deshalb schrieb ich: er macht genau das (richtig).
>
> Einfach anzunehmen, dass die Endianness nicht mehr stimmt... warum?
> Sollte sie sich in seinem Projekt einfach ändern?
Vielleicht soll sein Programm ja portabel sein, oder die Daten stammen 
von einem anderen Rechner?
#1596144
Lesenswert?

Sven P. schrieb:
> Vielleicht....
Von "Vielleicht" und anderen fiktiven Fällen stand nirgends etwas. In 
seinem jetzigen Fall ist alles gut so. Und wenn du alle möglichen 
anderen Fälle hier aufführen willst, dann wird dein Posting ziemlich 
lang. ;-) Was muß er z.B. machen wenn er eine CPU nutzt, die nicht 
einzelne Bytes adressieren kann geschweige denn Wort-Zugriffe auf nicht 
geraden Adressen u.s.w.
#1596234
Lesenswert?

900ss D. schrieb:

> Rolf Magnus schrieb:
>> Am besten informierst du dich, was "endianness" ist und antwortest
>> danach nochmal.
>
> Warum?

Weil Sven P. schrieb:
> Weiters wirst du genau dann wieder stolpern, wenn die Endianness
> nicht mehr stimmt.

und deine Antwort darauf war:
> Er öffnet die Datei mit "rb" (read binary).Dann bekommst du Daten direkt
> als binary. Und ob du 1 oder 3 "int"  hintereinander holst ist dann
> egal.

Da deine Antwort überhaupt nichts mit Endianness zu tun hat, war ich 
davon ausgegangen, daß du das Posting, auf das du geantwortet hast, 
nicht verstanden hast.

> Der TO macht das schon, also multibyte (int) einlesen im binary Format.
> Und es funktioniert. Seine Daten liegen für eine x86 Architektur (nehme
> ich jetzt mal an) richtig in der Datei. Ob durch Zufall oder nicht...
> keine Ahnung. Er wird nicht mehr darüber stolpern in seinem jetzigen
> Projekt.

Solange die Endiannes des System, das die Datei mal geschrieben hat, 
immer gleich der des Systems, das sie liest, bleibt.

> Deshalb schrieb ich: er macht genau das (richtig).
>
> Einfach anzunehmen, dass die Endianness nicht mehr stimmt... warum?
> Sollte sie sich in seinem Projekt einfach ändern?

Es ging nicht darum, daß die Endianness sich auf jeden Fall ändert, 
sondern daß, falls sich die Endianness ändert, der Code damit ein 
Problem hat.
Wenn man sich nicht mal bewußt ist, welche potenziellen 
Portabilitäts-Probleme man sich mit dem Einlesen von Binärdaten 
einhandeln kann, hat man keine Grundlage, um zu entscheiden, ob man 
damit leben kann oder nicht. Deshalb finde ich es besser, das zu 
erwähnen statt zu verschweigen.

Nicht alle Projekte sind auf ein einziges System beschränkt, und nicht 
jeder kann es sich leisten, unter der Annahme zu programmieren, daß der 
Code niemals auf irgendein anderes System portiert werden könnte.

> Von "Vielleicht" und anderen fiktiven Fällen stand nirgends etwas. In
> seinem jetzigen Fall ist alles gut so.

Du kennst seinen "jetzigen Fall" doch überhaupt nicht.  Also ist auch 
nur "vielleicht" alles gut.

> Was muß er z.B. machen wenn er eine CPU nutzt, die nicht einzelne Bytes
> adressieren kann geschweige denn Wort-Zugriffe auf nicht geraden
> Adressen u.s.w.

Ersteres ist heutzutage recht unwahrscheinlich, und wenn er so eine CPU 
hätte, wüßte er es auch. Und zweiteres habe ich ja bereits erwähnt. 
Stichwort "alignment".
#1596585
Lesenswert?

@ Rolf Markus: Da diese Diskussion dem TO nicht mehr hilft, lass ich es 
mal gut sein bis auf die Ausnahme unten, die zeugt von echtem Unwissen 
deinerseits.

Rolf Magnus schrieb:
>> Was muß er z.B. machen wenn er eine CPU nutzt, die nicht einzelne Bytes
>> adressieren kann geschweige denn Wort-Zugriffe auf nicht geraden
>> Adressen u.s.w.
>
> Ersteres ist heutzutage recht unwahrscheinlich,

Ach ja?
Gast #1957671
Lesenswert?

Hallo,

habe auch Dev-C++ und ebenfalls das selbe Problem mit dem Allignment. 
Ich benutze ebenso das Attribute packed, jedoch wird es vom Compiler 
ignoriert und er macht es wieder so wie er will (mit 2 byte, warum auch 
immer?!).
1
// data
2
typedef struct st_tgm_tp_diag_tag
3
{
4
  uint8_t  ubID1;              //! reserved
5
  uint8_t  ubID2;              //! nummer
6
  uint8_t  ubFlags;            //! data flags
7
  ST_TP_ZWEIG   stZweig[EN_MAX_TP_ZWEIG]; //! zweig informationen
8
}__attribute__((pack(1))) ST_DATA_DIAG;
1
70 F:\projects\SDUD\branch\1002\glob_interface.h [Warning] `pack' attribute directive ignored

Weiss jemand wie man den Compiler dazu zwingen kann das alignment zu 
akzeptieren? Vielen Dank!
Sitz schon eine Woche an diesem Problem und komme kein Stueck voran. 
Vielleicht muss ich den compiler wechseln?!

Gruss daniel
Gast #1957686
Lesenswert?

Hallo,

tja man muss halt erst ein Forum zumuellen mit sinnlosen Posts.
Das Problem liegt daran das ST_TP_ZWEIG ebenso ein packed enthalten 
muss.

ausserdem ist "pack(1)" einfach falsch an der Stelle, sowas existiert 
nur als #pragma pack(1). Es muss also __attribute__((_packed_)) sein.

Vielleichts nuetzts mal jemanden und er vergeudet nicht eine woche mit 
augenaufreibenden Stunden.

Gruss
daniel

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren