Forum: Mikrocontroller und Digitale Elektronik union mit Struct und Array


von Ingo (Gast)


Lesenswert?

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
typedef struct MyStruct_t{
2
  int Variable1;
3
  unsigned char Variable2;
4
  unsigned char Buffer[10];
5
  .
6
  .
7
  .
8
};
9
10
union{
11
 MyStruct_t MyStruct;
12
 unsigned char Datablock[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

von Karl H. (kbuchegg)


Lesenswert?

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.

von Ingo (Gast)


Lesenswert?

Ach so, ich nutze den GCC unter Atmel Studio 6


Ingo

von Ingo (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Auf einem AVR gibt es diesbezüglich keine Probleme.

Also kann ich das so machen wir ich mir das vorstelle?!



Ingo

von Karl H. (kbuchegg)


Lesenswert?

Ingo schrieb:
> Karl Heinz Buchegger schrieb:
>> Auf einem AVR gibt es diesbezüglich keine Probleme.
>
> Also kann ich das so machen wir ich mir das vorstelle?!


Ja.

von Ingo (Gast)


Lesenswert?

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?

von Flasch Gordon (Gast)


Lesenswert?

>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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Ingo (Gast)


Lesenswert?

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

von Star K. (starkeeper)


Lesenswert?

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
> typedef struct MyStruct_t{
2
>   int Variable1;
3
>   unsigned char Variable2;
4
>   unsigned char Buffer[10];
5
>   .
6
>   .
7
>   .
8
> };
9
> 
10
> union{
11
>  MyStruct_t MyStruct;
12
>  unsigned char Datablock[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.

von Ingo (Gast)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von Ingo (Gast)


Lesenswert?

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.

von Kaj (Gast)


Lesenswert?

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:
1
if( &union.struct.Variable2 == &union.Datablock[2] )
2
{
3
  //tu was
4
}

von Kaj (Gast)


Lesenswert?

Wenn das Array Datablock so groß sein soll wie das struct dann würde ich 
das lieber so machen:
1
unsigned char datablock[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.

von Fabian O. (xfr)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Ingo (Gast)


Lesenswert?

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
typedef struct MyStruct_t{
2
  unsigned char Variable1;
3
  unsigned char Variable2;
4
  unsigned char Variable3;
5
  .
6
  .
7
  .
8
  .
9
};
10
11
union{
12
 MyStruct_t MyStruct;
13
 unsigned char Datablock[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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Fabian O. (xfr)


Lesenswert?

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
struct MyStruct_t {
2
 ...
3
} __attribute__((__packed__));

Oder Du sagst dem Compiler:
1
#pragma pack(1)
Das gilt dann für alle ab dann deklarierten Strukturen.

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.