Forum: Mikrocontroller und Digitale Elektronik Einem Strukt Attribut, mehrere andere Strukte anhängen. Geht das?


von C. H. (hedie)


Lesenswert?

Guten Abend zusammen

Folgende Ausgangslage:

Ich habe eine Strukt a, ein Strukt b, und ein Strukt c

Hier der Aufbau von Strukt

a:
1
unsigned char Attribut_1a[20];
2
unsigned char Attribut_2a[1];

b:
1
unsigned char Attribut_1b[5];
2
unsigned char Attribut_2b[5];

c:
1
unsigned char Attribut_1c[5];
2
unsigned char Attribut_2c[5];


Nun die Frage:

Ist es möglich, dem Attribut_1a von Strukt a die Werte des Gesamten 
Stuktes b und c anzuhängen?

Speichermässig sollte es dann so aussehen

--- Attribut_1a ----
Inhalt von Strukt a
Inhalt von Strukt b
--- Attribut_2a ----
Irgend einen zugewiesenen Wert.


Ich hoffe ich versteht was ich fragen möchte :)

Danke

von Fabian O. (xfr)


Lesenswert?

Vielleicht suchst Du ein Union?
1
struct a {
2
  union {
3
    unsigned char Attribut_1a[20];
4
    struct {
5
      struct b Attribut_b;
6
      struct c Attribut_c;
7
    } Attribut_bc;
8
  } Attribut_1a_bc;
9
  unsigned char Attribut_2a[1];
10
};

Oder vielleicht auch einfach nur:
1
struct a {
2
  struct b Attribut_b;
3
  struct c Attribut_b;
4
  unsigned char Attribut_2a[1];
5
};

von C. H. (hedie)


Lesenswert?

Fabian O. schrieb:
> Vielleicht suchst Du ein Union?
> struct a {
>   union a1bc {
>     unsigned char Attribut_1a[20];
>     struct bc {
>       struct b;
>       struct c;
>     };
>   };
>   unsigned char Attribut_2a[1];
> };
> Oder vielleicht auch einfach nur:struct a {
>   struct b;
>   struct c;
>   unsigned char Attribut_2a[1];
> };

Hmmm ich bin mir nicht ganz sicher ob wir uns richtig verstanden haben 
:)

Ich habe nun also im Code diese beiden Strukte initialisiert:
1
struct myStruct_a *StructA;
2
struct myStruct_b *StructB;
3
struct myStruct_c *StructC;


nun möchte ich so etwas machen:
1
myStruct_a->Attribut_1a = myStruct_b+myStruct_c;

Das die so direkt wohl nicht geht ist mir klar.

Es soll quasi der Speicherbereich von myStruct_b und myStruct_c in
das Attribut_1a eingefüllt werden.

von Hmm (Gast)


Lesenswert?

Es scheint das kein Unterschied zwischen Deiner Absicht und der 
Verwendung von memcpy, strcpy, strcat etc. beteht. Wie siehst Du das?

von Fabian O. (xfr)


Lesenswert?

Das geht, wenn Du den Zeiger auf Attribut_1a in einen Zeiger auf eine 
Hilfsstruktur castest:
1
struct bc {
2
  struct myStruct_b Attribut_b;
3
  struct myStruct_c Attribut_c;
4
};
5
6
struct bc* temp = (struct bc*) &(myStruct_a->Attribut_1a);
7
temp->Attribut_b = *StructB;
8
temp->Attribut_c = *StructC;

Schön ist das allerdings nicht und außerdem gefährlich, wenn Attribut_1a 
nicht groß genug ist ...

Wenn Du die Struktur gleich als Union bzw. so definierst, dass sie 
Attribute von Typ myStruct_b und myStruct_c aufnehmen kann, geht es ohne 
gefährlichen Cast:
1
myStruct_a->Attribut_bc.Attribut_b = *StructB;
2
myStruct_a->Attribut_bc.Attribut_c = *StructC;
bzw.
1
myStruct_a->Attribut_b = *StructB;
2
myStruct_a->Attribut_c = *StructC;

Dir ist übrigens hoffentlich klar, dass Du in dem Beispiel nur Zeiger 
auf die Strukturen definiert hast und nicht den Speicher für die 
Strukturen selbst?

von C. H. (hedie)


Lesenswert?

Fabian O. schrieb:
> Das geht, wenn Du den Zeiger auf Attribut_1a in einen Zeiger auf eine
> Hilfsstruktur castest:

Danke für die Lösung. Aber wenn ich ein hilfsstruct brauche, dann kann 
ich auch gleich wie du gesagt hast

Fabian O. schrieb:
> Wenn Du die Struktur gleich als Union bzw. so definierst, dass sie
> Attribute von Typ myStruct_b und myStruct_c aufnehmen kann, geht es ohne
> gefährlichen Cast:

Eine Union definieren.

Fabian O. schrieb:
> Dir ist übrigens hoffentlich klar, dass Du in dem Beispiel nur Zeiger
> auf die Strukturen definiert hast und nicht den Speicher für die
> Strukturen selbst?

Vielen Dank für den Hinweis.

Nein so wirklich bewusst war mir dies nicht.
Ich bin noch ein wenig Lernbedürftig was Zeiger im Zusammenhang mit 
Strukten und Unions angeht.

Eigentlich ist diese methode jedoch vorteilhaft, da diese Speicher 
spart.
Andererseits gefählrich, wenn zb. ein Interrupt auf das Strukt in der 
Selben Art und weise zugreiffen würde.

Wie sähe es denn aus, wenn ich neuen Speicher mit der Struct Struktur 
anlegen würde?

Danke dir.

von Fabian O. (xfr)


Lesenswert?

Claudio Hediger schrieb:
> Eigentlich ist diese methode jedoch vorteilhaft, da diese Speicher
> spart.

Im Gegenteil, die Zeiger brauchen zusätzlichen Speicher (je Zeiger z.B. 
16 Bit = 2 Byte). Um den Speicher, in dem die Struktur selbst liegt, 
kommst Du ja nicht rum.

Mal ein ganz allgemeines Beispiel. Zuerst sagst Du dem Compiler, wie die 
Strukturen aufgebaut sind. Das braucht noch keinen Speicher, sondern ist 
nur eine Information für den Compiler. Ich finde es mit typedef 
leserlicher, ist aber Geschmackssache:
1
typedef struct {
2
  uint8_t b1[5];
3
  uint8_t b2[5];
4
} b_t;
5
6
typedef struct {
7
  uint8_t c1[5];
8
  uint8_t c2[5];
9
} c_t;
10
11
typedef struct {
12
  b_t     b;
13
  c_t     c;
14
  uint8_t a2;
15
} a_t;

Als nächstes legst Du die Variablen an. Dabei wird der Speicher für die 
Strukturen reserviert. Das hängt jetzt von Deiner Anwendung ab, welche 
sie wie oft braucht:
1
a_t mein_a; // belegt 21 Byte
2
b_t mein_b; // belegt 10 Byte
3
c_t mein_c; // belegt 10 Byte

Jetzt kannst Du damit arbeiten:
1
mein_b.b1[0] = 23;
2
mein_c.c2[2] = 42;
3
[...]

Wenn Du nun den Inhalt von mein_b in mein_a.b kopieren willst, geht das 
mit:
1
mein_a.b = mein_b;

Ich vermute mal, dass das schon das ist, was Du eigentlich brauchst. 
Aber der Vollständigkeit halber auch noch die Version mit der Union:

Die Deklaration des Typs:
1
typedef struct {
2
  union {
3
    uint8_t a1[20];
4
    struct {
5
      b_t b;
6
      c_t c;
7
    } bc;
8
  } a1_bc;
9
  uint8_t a2;
10
} a_t;

Die Definition der Variable ist wie gehabt:
1
a_t mein_a; // belegt ebenfalls 21 Byte

Den Inhalt von mein_b und mein_c an die gewünschten Stellen kopieren:
1
mein_a.a1_bc.bc.b = mein_b;
2
mein_a.a1_bc.bc.c = mein_c;

Der Unterschied zu vorher ist, dass Du in die Struktur a_t statt einem 
b_t und einem c_t auch 20 Bytes (uint8_t) am Stück ablegen kannst, auf 
die Du per mein_a.a1_bc.a1[x] zugreifst.

von C. H. (hedie)


Lesenswert?

Vielen Dank für deinen Beitrag.

Ich denke damit sollte es klappen :)

Ansonsten melde ich mich nochmal :)

//////////////// Edit //////////

Ja da hätte ich noch eine Frage.

Ich habe nun alle structs zu Typedefs gemacht und eine entsprechende 
Variable gezogen.

Zuvor hatte ich ein Strukt wie folgt befüllt:
1
myHeader = (struct Header*)&ETH_buffer[UDP_DATA];

doch nun klappt das nicht mehr.
1
error: incompatible types in assignment

Wie würde das befüllen einer typedef Variable aussehen?


Danke

von Fabian O. (xfr)


Lesenswert?

Claudio Hediger schrieb:
> myHeader = (struct Header*)&ETH_buffer[UDP_DATA];

Damit hast Du nicht die Struktur gefüllt, sondern nur einen Zeiger 
verändert! Das muss nicht falsch sein, aber es macht 
höchstwahrscheinlich nicht das, was Du eigentlich dachtest. Poste also 
am besten mal den ganzen (relevanten) Code, sonst ist das Stochern im 
Nebel ...

von Tom M. (Gast)


Lesenswert?

Fabian O. schrieb:
> Mal ein ganz allgemeines Beispiel...

Super ausgeführt, danke dafür. :)

von Claudio hediger (Gast)


Lesenswert?

Fabian O. schrieb:
> Claudio Hediger schrieb:
>> myHeader = (struct Header*)&ETH_buffer[UDP_DATA];
>
> Damit hast Du nicht die Struktur gefüllt, sondern nur einen Zeiger
> verändert! Das muss nicht falsch sein, aber es macht
> höchstwahrscheinlich nicht das, was Du eigentlich dachtest. Poste also
> am besten mal den ganzen (relevanten) Code, sonst ist das Stochern im
> Nebel ...

Ja da hast du recht :) ich poste morgen den gesamten relevanten code.
Ich danke dir für deine bisherige unterstützung

von Carlo (Gast)


Lesenswert?

entschuldigt, eine etwas unorthodoxe Frage:

Luna unterstützt wie C ja auch Strukturen, wäre rein zum Verständnis 
diese Strukturdeklaration hier dasselbe aus obigem Beispiel?:
1
struct b_t
2
  byte b1(5)
3
  byte b2(5)
4
endstruct
5
6
struct c_t
7
  byte c1(5)
8
  byte c2(5)
9
endstruct
10
11
struct a_t
12
  b_t b
13
  c_t c
14
  byte a2
15
endstruct
16
17
dim mein_a as a_t
18
19
mein_a.b.b1(1) = 123
20
...

dasselbe?

Gruß, Carlo

von Fabian O. (xfr)


Lesenswert?

> myHeader = (struct Header*)&ETH_buffer[UDP_DATA];

Da man ja ein bisschen erahnen kann, warum es geht, vielleicht doch noch 
ein paar Worte dazu:

Du möchtest wahrscheinlich auf die einzelnen Elemente des UDP-Headers 
zugreifen, der Teil des empfangenen Ethernet-Frames in ETH_buffer ist. 
Dazu musst Du den Header nicht extra in eine eigene Variable kopieren, 
wie ich es oben beschrieben habe. Es reicht, die bereits im Speicher 
liegenden Daten anders zu interpretieren.

ETH_buffer ist wohl ein Byte-Array, also z.B. so definiert:
1
uint8_t ETH_buffer[1500];

Dort liegen die empfangenen Daten. ETH_buffer{UDP_DATA] dürfte das erste 
Byte des UDP-Headers sein. Mit &ETH_buffer[UDP_DATA] bekommst Du einen 
Zeiger auf dieses Byte, also dessen Speicheradresse.

Man kann diese Speicheradresse aber nicht nur so interpretieren, dass 
sie auf das erste Byte zeigt, sondern auch so, dass sie auf den 
UDP-Header zeigt. Diese "uminterpretieren" geht in C mit einem Cast.

Dazu muss man C zuerst sagen, wie so ein UDP-Header aussieht:
1
typedef struct {
2
  uint16_t port_source;
3
  uint16_t port_destination;
4
  uint16_t length;
5
  uint16_t checksum;
6
} udp_header_t;

Danach erstellt man sich eine Variable, die einen Zeiger auf einen 
UDP-Header enthält:
1
udp_header_t* udp_header_zeiger;

In dieser Variable ist nur Platz für eine Adresse (je nach Plattform 
z.B. 16 Bit = 2 Byte), nicht für die eigentlichen Daten des UDP-Headers!

Jetzt lassen wir diese Variable auf die Stelle zeigen, an der der 
UDP-Header wirklich im Speicher liegt. Dazu müssen wir den Zeiger 
&ETH_buffer[UDP_DATA], der auf das erste Byte zeigt, in einen Zeiger auf 
einen UDP-Header casten:
1
udp_header_zeiger = (udp_header_t*) &ETH_buffer[UDP_DATA];

Und nun kann man über diesen Zeiger auf die einzelnen Elemente des 
UDP-Headers zugreifen:
1
udp_header_zeiger->port_source

Hier muss man den Pfeiloperator statt dem Punkt benutzen, weil 
udp_header_zeiger eben nicht die Daten selbst enthält, sondern die 
Speicheradresse, an der sie stehen.

von Fabian O. (xfr)


Lesenswert?

Und um den Bogen zu vorher noch zu schließen:

Wenn Du nicht die Daten in ETH_buffer uminterpretieren willst, sondern 
tatsächlich eine Kopie des UDP-Headers erstellen willst, geht das 
natürlich auch.

Variable für den UDP-Header anlegen:
1
udp_header_t mein_udp_header;

Die belegt 8 Byte im Speicher für die ganzen Felder des UDP-Headers. In 
diese Variable kann man jetzt die 8 Bytes ab der Stelle 
ETH_buffer[UDP_DATA] kopieren. Dazu gibt es zwei Möglichkeiten.

Die erste ist:
1
memcpy(&mein_udp_header, &ETH_buffer[UDP_DATA], sizeof(mein_udp_header));
Sprich: Kopiere 8 Bytes (die Größe des UDP-Headers) von der Adresse 
&ETH_buffer[UDP_DATA] an die Adresse &mein_udp_header.

Die andere Möglichkeit geht wieder über einen Cast:
1
mein_udp_header = *((udp_header_t*) &ETH_buffer[UDP_DATA]);
Sprich: Interpretiere die Adresse &ETH_buffer[UDP_DATA] als Zeiger auf 
einen UDP-Header. Lese die Daten, auf die der Zeiger zeigt 
(Dereferenzieren, mit dem *-Operator), und speichere sie in der Variable 
mein_udp_header.

Auf die einzelnen Elemente greift man dann ganz normal mit dem Punkt zu:
1
mein_udp_header.port_source

von C. H. (hedie)


Lesenswert?

@Fabian.

Du hast es echt drauf!

Danke vielmals. Auch deine Erklärungen sind sehr verständlich.
Du hast wohl alle meine Fragen beantwortet, das was genau dass, was ich 
wissen wollte :)

Tausend Dank!

von Harald (Gast)


Lesenswert?

Carlo schrieb:
> Luna unterstützt wie C ja auch Strukturen, wäre rein zum Verständnis
> diese Strukturdeklaration hier dasselbe aus obigem Beispiel?:struct b_t
>   byte b1(5)
>   byte b2(5)
> endstruct
>
> dim mein_a as a_t
>
> mein_a.b.b1(1) = 123
> ...
>
> dasselbe?
>
> Gruß, Carlo

ja, ist dasselbe, mit dem Unterschied, dass bei C mit [..] die 
Elementanzahl und bei Luna mit (..) das maximale, nullbasierte 
Arrayelement angegeben wird. Du musst also z.Bsp. c1(4) schreiben.

H.

von Kutte (Gast)


Lesenswert?

Claudio Hediger schrieb:
> @Fabian.
>
> Du hast es echt drauf!

dem möchte ich mich als Mitleser unbedingt anschließen !
danke, Kutte

von Claudio H. (Gast)


Lesenswert?

Ich hätte da doch noch eine Frage.

Ich habe folgenden Code:
1
    if(MRFD_NEW_PACKET_RECEIVED == 1)
2
    {
3
      MRFD_NEW_PACKET_RECEIVED = 0;
4
      myHeader = (DataHeader*)    &rxPACKET_Buffer[0];
5
      myRGBData = (RGBPaket*)   &xPACKET_Buffer[HeaderOffset];
6
7
      //uart_send_var(myHeader->DestinationAddr);
8
9
      if((myHeader->DestinationAddr == 255))
10
      {
11
        //uart_send_var(myRGBData->RedChannel);
12
        ucPWMRed   =   myRGBData->RedChannel;
13
        ucPWMGreen   =   myRGBData->GreenChannel;
14
        ucPWMBlue   =   myRGBData->BlueChannel;
15
      }
16
17
    }
18
19
  }

Wenn ich die Auskommentierten Zeilen für die UART Funktion weg mache, 
(also die Uart funktion ausführe) funktioniert alles so wie es soll.

Wenn ich hingegen die funktion auskommentiere, werden die Werte den 
Variablen nicht zugewiesen!

Der Inhalt im rxPACKET_Buffer ist korrekt.

Woran kann dies liegen?

hier noch die uart funktion:
1
void uart_send_var(unsigned int ucNumber)
2
{
3
  char cBuffer[5];
4
  ItoA(ucNumber,cBuffer);
5
  uart_puts(cBuffer);
6
}

von Claudio H. (Gast)


Lesenswert?

Achjaa, natürlich habe ich auch noch eine Variable mit dem Zeiger...
1
Header* myHeader;
2
RGBPaket* myRGBData;

von Claudio H. (Gast)


Lesenswert?

Claudio H. schrieb:
> Achjaa, natürlich habe ich auch noch eine Variable mit dem Zeiger...
> Header* myHeader;
> RGBPaket* myRGBData;

Problem gelöst.

Es muss so heissen:
1
volatile Header* myHeader;
2
volatile RGBPaket* myRGBData;

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.