www.mikrocontroller.net

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


Autor: ich (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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??

#include <stdio.h>
#include <stdlib.h>

unsigned char data[200];

typedef struct{
  unsigned int ID;
  unsigned int map_length;
  unsigned char Marker_1;
  unsigned int map_height;
  //unsigned char map_height;
} MAP_HEADER;

int main(int argc, char *argv[])
{
   FILE *fp;
   int filesize;
   char filename[81]="test.dat";
   MAP_HEADER *map;
   int i;
  
   if ((fp = fopen (filename, "rb")) == NULL)
   {
      printf("%s nicht vorhanden\n",filename);
      return 1;
   }
   else
   {
      printf("Datei gefunden\n"); 
      fread (data, 1, sizeof (data), fp);
      filesize=ftell(fp);
      fclose (fp);
   }
   
   map= (MAP_HEADER*)&data[2];
   
   printf("ID: %x\n",map->ID);
   printf("Laenge: %d\n",map->map_length);
   printf("Marker: %d\n",map->Marker_1);
   printf("Hoehe: %d\n",map->map_height);
  
  system("PAUSE");  
  return 0;
}

Autor: 900ss D. (900ss)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Rolf Magnus (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Sven P. (haku) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: 900ss D. (900ss)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Rolf Magnus (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Am besten informierst du dich, was "endianness" ist und antwortest 
danach nochmal.

Autor: Sven P. (haku) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: ich (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kennt sogar das deutsche Wiki: http://de.wikipedia.org/wiki/Endianness

Autor: ich (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe es mal so versucht. Passt jetzt. Hoffe ich zu mindestens.

typedef struct{
  unsigned int ID;
  unsigned int map_length;
  unsigned char Marker_1;
  unsigned int map_height;
  //unsigned char map_height;
}__attribute__ ((packed))MAP_HEADER;


Autor: 900ss D. (900ss)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: 900ss D. (900ss)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Sven P. (haku) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: 900ss D. (900ss)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Rolf Magnus (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht 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".

Autor: 900ss D. (900ss)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Rolf Magnus (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: 900ss D. (900ss)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja, du hast Recht und ich und andere ihre Ruhe.

Autor: daniel (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?!).
// data
typedef struct st_tgm_tp_diag_tag
{
  uint8_t  ubID1;              //! reserved
  uint8_t  ubID2;              //! nummer
  uint8_t  ubFlags;            //! data flags
  ST_TP_ZWEIG   stZweig[EN_MAX_TP_ZWEIG]; //! zweig informationen
}__attribute__((pack(1))) ST_DATA_DIAG;
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

Autor: daniel (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dein Tonfall ist ja wirklich klasse.

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

Autor: daniel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sry, na wenn sogar moderatoren kommentieren, dann lass ich auch mal 
einen:
brne genuegend
ldi note,4
setzen

_packed_ und weg

gruss daniel

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.