Struct im Union über array abfüllen

OP #6477517
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?
Angehängte Dateien:
#6477721
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.
OP #6477819
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.
#6477839
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
  }
Angehängte Dateien:
OP #6478230
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?
OP #6478242
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.
Gast #6478253
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.
Moderator Persönliche Seite #6478335
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.
Gast #6478416
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.

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