Forum: PC-Programmierung C/C++ Problem mit struct - Byte-Verschiebung


von ich (Gast)


Angehängte Dateien:

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
}

von 900ss (900ss)


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.

von Rolf M. (rmagnus)


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?

von Sven P. (Gast)


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.

von 900ss (900ss)


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.

von Rolf M. (rmagnus)


Lesenswert?

Am besten informierst du dich, was "endianness" ist und antwortest 
danach nochmal.

von Sven P. (Gast)


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.

von ich (Gast)


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?

von (prx) A. K. (prx)


Lesenswert?

Kennt sogar das deutsche Wiki: http://de.wikipedia.org/wiki/Endianness

von ich (Gast)


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;

von 900ss (900ss)


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?

von 900ss (900ss)


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.

von Sven P. (Gast)


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?

von 900ss (900ss)


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.

von Rolf M. (rmagnus)


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".

von 900ss (900ss)


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?

von Rolf M. (rmagnus)


Lesenswert?

900ss D. schrieb:
>> Ersteres ist heutzutage recht unwahrscheinlich,
>
> Ach ja?

Ja, ich halte es für unwahrscheinlich, daß er so eine CPU hat, es nicht 
weiß und damit Dateien lesen und/oder schreiben will. Du nicht?

von 900ss (900ss)


Lesenswert?

Ja, du hast Recht und ich und andere ihre Ruhe.

von daniel (Gast)


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

von daniel (Gast)


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

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Dein Tonfall ist ja wirklich klasse.

Wenigstens hast Du dann noch die Kurve gekriegt und was sinnvolles 
beigetragen.

von daniel (Gast)


Lesenswert?

Sry, na wenn sogar moderatoren kommentieren, dann lass ich auch mal 
einen:
1
brne genuegend
2
ldi note,4
3
setzen

_packed_ und weg

gruss daniel

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.