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
typedefstruct
2
{
3
union
4
{
5
struct
6
{
7
uint8_tid;
8
uint8_tflag;
9
floatserial;
10
uint8_tspare[4];
11
}as_struct;
12
13
uint8_tas_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?
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.
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
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.
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
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
typedefstruct
2
{
3
union{
4
uint32_tas_array[3];
5
struct{
6
uint16_tid;
7
uint16_tflag;
8
floatserial;
9
uint8_tspare[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.
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.
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.
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
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.
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.
Kevin M. schrieb:> Eigentlich sollte im Speicher die> Endianness immer richtig sein.
Sehe ich auch so.
Zumindest innerhalb der meisten Prozessorfamilien.
(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
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
typedefstruct
2
{
3
union
4
{
5
uint8_tas_array[14];
6
7
struct
8
{
9
uint8_tid;
10
uint32_tdi;
11
uint8_tflag;
12
floatserial;
13
uint8_tspare[4];
14
}__packedas_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.
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.
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.
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
structtest{
2
floatf;
3
charch1;
4
charch2;
5
charstr[4];
6
};
7
8
printf("size: %zu\n",sizeof(structtest));
9
printf("alignment: %zu\n",_Alignof(structtest));
Und trotzdem braucht diese Struktur 12 Bytes (oder 16, oder 20, ...).
Man kann ja auch ein Array erzeugen:
1
structtestt[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.