Hallo, ich möchte gern eine Union aus einer Struct und einem Array
machen. Hintergrund ist, ich lesen Daten aus einem Speicher (256 Bytes)
und möchte die meiner Struct zuordnen. Ich bin mir nur nicht sicher ob
das so funktioniert, da ich gelesen habe das es mit dem allaign Probleme
geben könnte.
1
typedefstructMyStruct_t{
2
intVariable1;
3
unsignedcharVariable2;
4
unsignedcharBuffer[10];
5
.
6
.
7
.
8
};
9
10
union{
11
MyStruct_tMyStruct;
12
unsignedcharDatablock[256];
13
}
ist nun durch die Union Datablock[2] automatisch Variable2 zugeordnet?
Durch Datablock erzwinge ich doch ein Dataalaign auf 8 Bit, oder?
Mein K&R schweigt sich diesbezüglich etwas aus.
Ingo
Ingo schrieb:> ist nun durch die Union Datablock[2] automatisch Variable2 zugeordnet?
Wenn wir streng noch den C-Regeln gehen, dann ist da überhaupt nichts
'zugeordnet'.
Du darfst aus einer Union nur wieder über den Member Daten auslesen,
über den sie zuletzt auch hineingeschrieben wurden. Punkt. Mehr Garantie
über das Verhalten einer Union kriegst du vom C-Standard nicht.
In der Praxis ist es so, dass du dir eine Union vorstellen kannst, wie
wenn du einfach die Startadresse des Speichers auf jeweils den Datentyp
eines deiner Union-Members casten würdest.
Was immer dann passiert, bestimmt deine Plattform. Ob da also ein
Alignment Problem auftaucht oder nicht, entscheidet deine
Plattform/Compiler. Auf einem AVR gibt es diesbezüglich keine Probleme,
ausser dass du dich um die Endianess kümmern musst, je nachdem wo die
Daten herkommen.
Karl Heinz Buchegger schrieb:> Auf einem AVR gibt es diesbezüglich keine Probleme,> ausser dass du dich um die Endianess kümmern musst, je nachdem wo die> Daten herkommen.
Was heisst das genau?
>ist nun durch die Union Datablock[2] automatisch Variable2 zugeordnet?
Ja ist so. Denn der Int ist 2 bytes. , der wird Datablock[0] und
Datablock[1] zugeordnet.
Ingo schrieb:> Karl Heinz Buchegger schrieb:>> Auf einem AVR gibt es diesbezüglich keine Probleme,>> ausser dass du dich um die Endianess kümmern musst, je nachdem wo die>> Daten herkommen.>> Was heisst das genau?
Ein 16 Bit int besteht aus 2 Bytes.
Ist nun die Reihenfolge im Speicher:
zuerst das Low Byte und dann das High Byte?
oder ist es genau anders rum?
Du musst also wissen, wie es derjenige macht, der die Daten erzeugt hat
und dich darauf anpassen.
Karl Heinz Buchegger schrieb:> Ein 16 Bit int, besteht aus 2 Bytes.> Ist nun die Reihenfolge im Speicher:> zuerst das Low Byte und dann das High Byte?> oder ist es genau anders rum?>> Du musst also wissen, wie es derjenige macht, der die Daten erzeugt hat> und dich darauf anpassen.
ja, das macht Sinn. Sehr schön Karl Heinz, besten Dank!
Ingo
Ingo schrieb:> Hallo, ich möchte gern eine Union aus einer Struct und einem Array> machen. Hintergrund ist, ich lesen Daten aus einem Speicher (256 Bytes)> und möchte die meiner Struct zuordnen. Ich bin mir nur nicht sicher ob> das so funktioniert, da ich gelesen habe das es mit dem allaign Probleme> geben könnte.>
1
>typedefstructMyStruct_t{
2
>intVariable1;
3
>unsignedcharVariable2;
4
>unsignedcharBuffer[10];
5
>.
6
>.
7
>.
8
>};
9
>
10
>union{
11
>MyStruct_tMyStruct;
12
>unsignedcharDatablock[256];
13
>}
14
>
> ist nun durch die Union Datablock[2] automatisch Variable2 zugeordnet?> Durch Datablock erzwinge ich doch ein Dataalaign auf 8 Bit, oder?> Mein K&R schweigt sich diesbezüglich etwas aus.>>> Ingo
Also die Elemente einer union liegen übereinander im Speicher. Die Größe
einer union wird vom größten enthaltenen element bestimmt. In deinem
Fall ist das Array Datablock am größten, also ist die union 256 byte
groß.
Die elemente deiner Stuktur könntest du nun beschreiben in dem du die
Daten des Array Datablock beschreibst. Welches Element deiner Struktur
nun an welcher Stelle im Array abgebildet wird liegt am Compiler und der
eingesetzten Architektur.
Wie groß ist z.B. dein int? 8/16/32 Bit? Dann darf der Compiler
Stuffbytes einfügen um das Alignment der Daten in der Struktur zu
verbessern. Ein 8Bi Wert kann z.B. trotzdem 32Bit einnehmen damit die
nachfolgenden Elemente immer an einer 4Byte Adresse beginnen. Dies
könnte man aber durch ein "pragma pack.." abschalten.
Wenn man wüsste, ob die Daten genauso im Speicher liegen, wie sie in der
Struct stehen, könnte man auch einen Poiter direkt auf die Struct gehe,
dann spart man die Union bzw. braucht nicht diese Hilfskrüke mit dem
Array. Kann man das irgenwie sicherstellen?
Mein K&R schweigt sich über #pragma leider aus, zum GCC habe ich auch
noch nichts dazu gefunden...
Ingo
Ingo schrieb:> Wenn man wüsste, ob die Daten genauso im Speicher liegen, wie sie in der> Struct stehen, könnte man auch einen Poiter direkt auf die Struct gehe,> dann spart man die Union bzw. braucht nicht diese Hilfskrüke mit dem> Array.
Ähm.
Da gibt es prinzipiell keinen Unterschied. Die eine Operation ist
genauso gut definiert wie die andere. Die eine Operation leidet unter
genau den gleichen Problemen wie die andere. Nur weil du da jetzt eine
andere Syntax benutzt, vertauschen sich ja jetzt nicht irgendwie magisch
irgendwelche Bytes oder werden Alignment-Padding Bytes plötzlich
weggelassen.
> Kann man das irgenwie sicherstellen?
Auf der Ebene kommst du nicht drumm herum, wissen zu müssen, was genau
dir der Sender in welchem Byte mitteilen will. Egal wie du es
programmierst. Leb damit.
Karl Heinz Buchegger schrieb:> Auf der Ebene kommst du nicht drumm herum, wissen zu müssen, was genau> dir der Sender in welchem Byte mitteilen will. Egal wie du es> programmierst. Leb damit.
Ok, das stellt ja nicht das Problem da, zu gucken ob erst das Highbyte
oder das Lowbyte aus einem 16-Bit Wort zu erst kommt.
Ich glaube hier wird an einander vorbei geredet ;)
So wie ich Ingo verstehe, ist seine Gedanke:
Stehen die Variablen genau in der reihenfolge im Speicher wie sie im
Quellcode stehen?
Denn wenn dem nicht so ist, dann kann auch als erstes Variable2 im
Speicher liegen, und dann erst Variable1, was bedeutet:
Datablock[0] = Variable2
datablock[1]/[2] = Variable1
Somit müsste man erst eine Adressüberprüfung machen, in etwa so:
Wenn das Array Datablock so groß sein soll wie das struct dann würde ich
das lieber so machen:
1
unsignedchardatablock[sizeof(struct)];
sonst könnte es passieren das zwar sachen im struct stehen aber nicht im
Array bzw. umgekehrt (sofern das wichtig ist), ausgelöst durch
Füllbytes/Compiler optimierung.
Für die Datenübertragung zwischen Geräten nimmt man in der Regel die
"Network Byte Order", die Big Endian (höchstwertiges Byte zuerst) ist.
Der AVR ist dagegen eine Little-Endian-Maschine (niederwertigstes Byte
zuerst). Man muss deshalb normalerweise 16- oder 32-Bit-Werte, die man
von außen empfängt, byteweise "umdrehen", bevor man damit rechnen kann.
Dazu kannst Du z.B. diese Makros nutzen:
Beitrag "Re: Little Endian zu Big Endian bei 8Bit MCU"
Kommt aber natürlich drauf an, woher die Werte aus dem Speicher kommen.
Wenn Du bzw. ein anderer Programmteil sie selber berechnet hat, dann
sind sie natürlich schon im richtigen Format.
Kaj schrieb:> Ich glaube hier wird an einander vorbei geredet ;)>> So wie ich Ingo verstehe, ist seine Gedanke:> Stehen die Variablen genau in der reihenfolge im Speicher wie sie im> Quellcode stehen?
OK. Wäre möglich, dass das seine Befürchtung ist.
> Denn wenn dem nicht so ist, dann kann auch als erstes Variable2 im> Speicher liegen, und dann erst Variable1,
Das kann aber nicht sein, da der Compiler nicht eigenmächtig die
Variablen in einer struct umsortieren darf.
Er darf, zum Erreichen eines möglicherweise notwendigen Alignments sog.
Padding Bytes einschieben. Aber das wars dann auch schon. An die vom
Programmierer vorgegebene Reihenfolge innerhalb einer struct ist er
gebunden.
Karl Heinz Buchegger schrieb:> OK. Wäre möglich, dass das seine Befürchtung ist.
So ist es, das war die Befürchtung
Um es nochmal aufzugreifen:
1
typedefstructMyStruct_t{
2
unsignedcharVariable1;
3
unsignedcharVariable2;
4
unsignedcharVariable3;
5
.
6
.
7
.
8
.
9
};
10
11
union{
12
MyStruct_tMyStruct;
13
unsignedcharDatablock[256];
14
}
Die Reihenfolge, in der die Variablen im Speicher stehen, ist die selbe
wie in der Struct angegeben? Das heisst, wenn ich einen Pointer auf die
Struct setze, diesen um jeweils um eins erhöhe (8Bit Pointer) werden die
Variablen auch in dieser Reihenfolge, wie es im Code steht abgearbeitet?
Padding Bytes werden doch nur am Ende meiner Struct ergänzt, um auf die
Größe meines Datenblockes (hier 256) zu kommen, oder? Sprich wenn die
Struct nur 200 Bytes groß ist, werden noch 56 Padding Bytes angehängt am
Ende. Die Daten kommen von einer Chipkarte, deren Datenblöcke immer 256
Byte groß ist.
Ingo schrieb:> Die Reihenfolge, in der die Variablen im Speicher stehen, ist die selbe> wie in der Struct angegeben?
Ja.
> Das heisst, wenn ich einen Pointer auf die> Struct setze, diesen um jeweils um eins erhöhe (8Bit Pointer)
Das ist missverständlich. In deinem Fall muss es ein (unsigned char*)
sein.
> werden die> Variablen auch in dieser Reihenfolge, wie es im Code steht abgearbeitet?
Ja
> Padding Bytes werden doch nur am Ende meiner Struct ergänzt,
Das sind dann aber keine Padding Bytes im eigentlichen Sinn.
In der union gibt es dann halt ganz einfach Bytes, für die es keine
Entsprechung im struct Member gibt.
Gibst du Socken in eine Schublade, und bleibt da Platz frei, dann bleibt
einfach nur Platz frei. Das bedeutet nicht, dass da irgendwas spezielles
im Gange wäre.
> Struct nur 200 Bytes groß ist, werden noch 56 Padding Bytes angehängt am> Ende.
Die union hat die Größe ihres größten Members.
Wenn andere Member diesen Speicher nicht mitbenutzen, dann benutzen sie
ihn eben nicht mit.
Ingo schrieb:> Die Reihenfolge, in der die Variablen im Speicher stehen, ist die selbe> wie in der Struct angegeben? Das heisst, wenn ich einen Pointer auf die> Struct setze, diesen um jeweils um eins erhöhe (8Bit Pointer) werden die> Variablen auch in dieser Reihenfolge, wie es im Code steht abgearbeitet?>> Padding Bytes werden doch nur am Ende meiner Struct ergänzt, um auf die> Größe meines Datenblockes (hier 256) zu kommen, oder? Sprich wenn die> Struct nur 200 Bytes groß ist, werden noch 56 Padding Bytes angehängt am> Ende. Die Daten kommen von einer Chipkarte, deren Datenblöcke immer 256> Byte groß ist.
Die Reihenfolge ist fest, ja. Aber zwischen den Variablen können
prinzipiell Padding Bytes stehen. Auf dem AVR nicht, auf anderen
Prozessoren schon.
Um sicherzugehen, dass der Compiler keine Padding Bytes eingefügt,
kannst Du beim GCC angeben:
1
structMyStruct_t{
2
...
3
}__attribute__((__packed__));
Oder Du sagst dem Compiler:
1
#pragma pack(1)
Das gilt dann für alle ab dann deklarierten Strukturen.