Forum: Mikrocontroller und Digitale Elektronik Struct im Union über array abfüllen


von Moot S. (mootseeker)


Angehängte Dateien:

Lesenswert?

Hallo Zusammen,

ich versuche eine Struktur im ein array zu kopieren um dies 
anschliessend, versenden oder speichern zu können auf einem externen 
Speicher. Nun habe ich mit folgende Struktur in einem union erstellt:
1
typedef struct
2
{
3
    union
4
    {
5
  struct
6
  {
7
          uint8_t id;
8
    uint8_t flag;
9
    float serial;
10
    uint8_t spare[4];
11
  } as_struct;
12
13
  uint8_t as_array[10];
14
15
    } dat;
16
} sys_data_type;

Wenn ich nun in der Struktur für den float serial einen Wert von 12.25 
setzte, muss ich doch in array as_array an der Stelle 2, 3 die Zahlen 
für den Wert 12.25 sehen? Der Hexwert für 12.25 ist 0x4144 in DEC 65 & 
68.

Wenn ich die Struktur abfülle und es mit dem Debugger kontrolliere sehe 
ich aber etwas komplett anderes (Siehe Bild im Anhang). die ersten 
beiden Zahlen für den uint8_t stimmen. Dort wo ich den Float erwarten 
würde steht 0 und am ende steht dann der Wert für den float. Warum steht 
das nicht an der selben stelle?

von Jim M. (turboj)


Lesenswert?

Vermutlich weil der Compiler ein 4-Byte Alignment für den Float Wert 
haben möchte und daher die Struct passend auffüllt.

Schaur Dir z.B. mal die Adressen von spare und as_array ganz genau an.

von A. S. (Gast)


Lesenswert?

Jim M. schrieb:
> Schaur Dir z.B. mal die Adressen von spare und as_array ganz genau an.

Oder ein compiletime assert mit der Größe (sizeof) der Union.

von Max M. (jens2001)


Lesenswert?

Moot S. schrieb:
> Wenn ich nun in der Struktur für den float serial einen Wert von 12.25
> setzte, muss ich doch in array as_array an der Stelle 2, 3 die Zahlen
> für den Wert 12.25 sehen?

Wie kommst du auf die Idee?

https://de.wikipedia.org/wiki/Gleitkommazahl

von Moot S. (mootseeker)


Lesenswert?

Ja weil ich die Bytes doch an der selben Addresse befinden?

Stimmt das nicht?

von Moot S. (mootseeker)


Lesenswert?

Ich arbeite übrigends mit dem GCC Compiler für STM32. Versteht der das 
anderst?

von Kevin M. (arduinolover)


Lesenswert?

Er wird den Float nur im richtigen alignment speichern könne, also in 
einem 4er inkrement. Demnach ist dein Struct 12 Byte groß und die zwei 
Byte nach id und flag sind ungenutzt.

von Moot S. (mootseeker)


Lesenswert?

Kevin M. schrieb:
> Er wird den Float nur im richtigen alignment speichern könne, also in
> einem 4er inkrement. Demnach ist dein Struct 12 Byte groß und die zwei
> Byte nach id und flag sind ungenutzt.

Aber wenn ich das Array im Debugger ansehe sind 6 Bytes leer und der 
float steht im 7. und 8. Byte

von Kevin M. (arduinolover)


Lesenswert?

Ich habe das gerade mal ausprobiert es scheint wirklich so zu sein. 
Zudem dreht er die byte order um, warum hab ich spontan keine idee. Es 
war aber auch ein langer Tag ;)

Wenn der Speicher nicht allzu knapp ist (wovon ich mal ausgehe, da der 
STM genug davon hat) versuch folgendes:
1
typedef struct
2
{
3
  union {
4
    uint32_t as_array[3];
5
    struct {
6
          uint16_t id;
7
          uint16_t flag;
8
          float serial;
9
          uint8_t spare[4];
10
    };
11
  };
12
}sys_data_type;

Wenn du Byte weise Zugriff brauchst kannst du das Array ja jederzeit auf 
einen (uint8_t*) casten.

Vielleicht hat jemand ja noch eine andere/bessere Idee.

von Einer K. (Gast)


Lesenswert?

Kevin M. schrieb:
> Vielleicht hat jemand ja noch eine andere/bessere Idee.

 __attribute__((packed))

?

von Kevin M. (arduinolover)


Lesenswert?

Arduino Fanboy D. schrieb:
> __attribute__((packed))

Das sollte das Problem lösen das man 2 byte offset hat, also kommt man 
mit 10 Byte klar aber ob es das byte order Problem löst bin ich nicht 
sicher.

von Einer K. (Gast)


Lesenswert?

Kevin M. schrieb:
> ob es das byte order Problem löst bin ich nicht
> sicher.

Das tut es nicht.
Die Byteorder kann man nur bei ganz wenigen Prozessoren umstellen.

von Moot S. (mootseeker)


Lesenswert?

Arduino Fanboy D. schrieb:
> Kevin M. schrieb:
>> Vielleicht hat jemand ja noch eine andere/bessere Idee.
>
>  __attribute__((packed))
>
> ?

Das habe ich in einem anderen Forum auch gelesen. Aber das hat es nicht 
viel besser gemacht, wie auch unten schon beschrieben von @Arduinolover

von Kevin M. (arduinolover)


Lesenswert?

Ich bin aber auch nicht sicher ob das wirklich flasch ist wie es 
gespeichert wird. Hast du mal versucht mit memcpy o.ä. die Daten einfach 
in eine float Variable zu kopieren. Eigentlich sollte im Speicher die 
Endianness immer richtig sein.

von Moot S. (mootseeker)


Lesenswert?

Kevin M. schrieb:
> Ich bin aber auch nicht sicher ob das wirklich flasch ist wie es
> gespeichert wird. Hast du mal versucht mit memcpy o.ä. die Daten einfach
> in eine float Variable zu kopieren. Eigentlich sollte im Speicher die
> Endianness immer richtig sein.

Ja das war mein erster versuch, das hat nicht funktioniert und deswegen 
bin ich dann von dort zur for schleife und dann zum Union aber hat bis 
jetzt alles nicht funktioniert, wahrscheinlich weil im Hintergrund alles 
das gleiche macht.

von Einer K. (Gast)


Lesenswert?

Kevin M. schrieb:
> Eigentlich sollte im Speicher die
> Endianness immer richtig sein.
Sehe ich auch so.
Zumindest innerhalb der meisten Prozessorfamilien.

von Kevin M. (arduinolover)


Angehängte Dateien:

Lesenswert?

Also bei mir funktioniert das, siehe Anhang.
Die Definitionen und das Byte geschupse sind folgende:
1
//Definitionen
2
typedef struct
3
{
4
  union {
5
    uint8_t as_array[10];
6
    //uint32_t as_array32[3];
7
    struct {
8
          uint8_t id;
9
        uint8_t flag;
10
        float serial __attribute__((packed));
11
          uint8_t spare[4];
12
    };
13
  };
14
}sys_data_type;
15
16
sys_data_type test;
17
float ftest1,ftest2;
18
19
//Zuweisung und kopieren
20
  test.serial = 12.25;
21
  test.id = 1;
22
  test.flag = 2;
23
  test.spare[0] = 3;
24
  test.spare[1] = 4;
25
  test.spare[2] = 5;
26
  test.spare[3] = 6;
27
28
  memcpy(&ftest1,&test.as_array[2],4);
29
30
  uint8_t *ptr = (uint8_t*)&ftest2;
31
32
  for(uint8_t i = 0; i<4; i++){
33
    *(ptr+i) = test.as_array[2+i];
34
  }

von (prx) A. K. (prx)


Lesenswert?

Tipp für Code in Beiträgen: Keine TABs verwenden.

von Kevin M. (arduinolover)


Lesenswert?

(prx) A. K. schrieb:
> Tipp für Code in Beiträgen: Keine TABs verwenden.

Ich hab das aus der IDE kopiert, da sind leider Tabs, ich schreib das ja 
nicht alles neu ;)

Ich denke aber, dass es noch ganz leserlich ist :D

von Moot S. (mootseeker)


Lesenswert?

Kevin M. schrieb:
> Also bei mir funktioniert das, siehe Anhang.
1
 //Definitionen
2
 typedef struct
3
 {
4
    union 
5
    {
6
        uint8_t as_array[10];
7
     
8
        struct 
9
        {
10
            uint8_t id;
11
            uint8_t flag;
12
            float serial __attribute__((packed));
13
            uint8_t spare[4];
14
        };
15
    };
16
 }sys_data_type;

So hat es für mich auch funktioniert, hatte das
1
 __attribute__((packed))
 am falschen Ort. Dachte das kann man für das ganze Struct einsetzten. 
Muss ich das nun bei jeder Variable einsetzten die grösser als uint8_t 
sind?

von Moot S. (mootseeker)


Lesenswert?

Moot S. schrieb:
> Dachte das kann man für das ganze Struct einsetzten.
> Muss ich das nun bei jeder Variable einsetzten die grösser als uint8_t
> sind?

Ich habe noch eine Lösung dafür gefunden. War zwar sehr versteckt aber 
in folgendem Forum habe ich eine Antwort gefunden: 
https://forum.atollic.com/viewtopic.php?t=1796

Meine Struktur sieht nun wie folgt aus:
1
typedef struct
2
{
3
    union
4
    {
5
        uint8_t as_array[14];
6
7
        struct
8
        {
9
            uint8_t id;
10
            uint32_t di;
11
            uint8_t flag;
12
            float serial;
13
            uint8_t spare[4];
14
        } __packed as_struct;
15
    };
16
} sys_data_type;

Das
1
  __packed
 mach das gleiche wie das
1
 __attribute__((packed))
 übernimmt es aber für die ganze Struktur.

Das Problem verstehe ich nun auch :) Das selbe Problem wurde bei z.B. 
Microchip XC Compilern schon vor X Jahren eingebaut.

von CK (Gast)


Lesenswert?

Mit packed, sind die Elemente nicht mehr align und der Zugriff auf diese 
Elemente dauert länger.

Wenn man die Möglichkeit hat, sollte man in einem Struct zuerst mit den 
4 Byte großen Variablen, beginnen, gefolgt von den 2 Byte und Anschluss 
mit den 1 Byte Variablen, dann hat man das Problem nie.

Wenn's nicht anders geht, dazwischen, einmal Padding-Variablen anlegen, 
um die Compiler-Warnung weg zu bekommen, wenn man -Wpadded kompiliert.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

CK schrieb:
> Wenn man die Möglichkeit hat, sollte man in einem Struct zuerst mit den
> 4 Byte großen Variablen, beginnen, gefolgt von den 2 Byte und Anschluss
> mit den 1 Byte Variablen, dann hat man das Problem nie.

Eben. Man kann durchaus selbst das Padding durch geschicktes Anordnen 
der Struct-Members durchführen.

Am einfachsten: Die float-Variable als erstes in die Struct packen. Dann 
ist dieses Problem schon mal gelöst.

von Frage (Gast)


Lesenswert?

CK schrieb:
> Wenn man die Möglichkeit hat, sollte man in einem Struct zuerst mit den
> 4 Byte großen Variablen, beginnen, gefolgt von den 2 Byte und Anschluss
> mit den 1 Byte Variablen, dann hat man das Problem nie.

Ist es wirklich so?

Da will jemand 10 Bytes für seine Struct haben und diese dann auch mal 
byteweise durchlaufen.

Dann packt er alles geschickt und geordnet:
1
   struct test {
2
      float f;
3
      char ch1;
4
      char ch2;
5
      char str[4];
6
   };
7
8
   printf("size: %zu\n", sizeof(struct test));
9
   printf("alignment: %zu\n", _Alignof(struct test));

Und trotzdem braucht diese Struktur 12 Bytes (oder 16, oder 20, ...).
Man kann ja  auch ein Array erzeugen:
1
struct test t[10];

Darum müssen die Adressen ein Vielfache von 4 sein. Was ja bei einer 
Größe von 10 Bytes nicht der Fall wäre.

Man kann das mit __packed umgehen.

CK schrieb:
> Mit packed, sind die Elemente nicht mehr align und der Zugriff auf diese
> Elemente dauert länger.

Würde er für die Structur 4*x Bytes (ein Vielfaches von 4) benötigen, 
dann würde es wohl auch ohne __packed gehen.

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.