Kombination von Structs und Unions in C

Es folgt eine kurze Abhandlung zur Verwendung von Structs und Unions zur Datenkonvertierung, z.B. im CAN-Netzwerk

Structs und Bitfelder

Structs können in C verwendet werden, um komplexe Datenobjekte zu definieren.

Bsp Ringpuffer:

  • typedef struct{
  •     unsigned int count; /* Menge der Elemente */
  •     unsigned int head;
  •     unsigned int tail;
  •     char content[BUFFERSIZE];
  • } ringbuffer_t;

Hier wird ein Char Array als Ringpufferinhalt verwendet. Ein Element des Datentyps Struct belegt im Speicher 16 Byte. Natürlich kann anstelle des Char Arrays hier ein Struct als Ringpufferinhalt  verwendet werden.

Bei Echtzeitsystemen werden häufig die versendeten Informationen auf den relevanten Bereich beschränkt um somit die Nachrichten kurz zu halten (z.B. CAN). Wenn ein Messwert lediglich in einem Bereich von 0-255 liegt, empfiehlt es sich ein unsigned Byte zu verwenden.  Es ist aber auch möglich (um z.B. Typkonvertierungen zu vermeiden) ein unsigned Integer zu verwenden und nur die ersten 8 Bits zu benutzen. Dies wird durch ein Bitfeld Struct ermöglicht.

  • typedef struct{
  •      unsigned int temperatur: 9;
  •      int lambdaWert: 6;
  •      unsigned int motorOK: 1;
  • } Messwert _t;

Ein Element dieses Structs würde ohne Bitfeld 12 Byte belegen, durch die Verwendung als Bitfeld wird es auf 2 Byte verkleinert.

Beispiel Nachrichtenübertragung:

Stellen Sie sich den Fall vor, dass über ein Netzwerk  diese Messwert_t Botschaften versendet werden. Beim Verschicken wird das Struct ‚Messwert_t‘ in seine 2 Bytes zerlegt versendet. Der Empfänger kriegt vom Netzwerkcontroller folgendes:

  • Int messageID;
  • Byte[2] messageContent;

Um die Information nun zu extrahieren können Bitmasken, wenn auch Umständlich, helfen:

  • Int lambdaWert = (messageContent[1] ^ 01111110)>>1;

Unions

Eine Union sieht dem Struct zwar ähnlich, hat jedoch eine gänzlich andere Funktion:

  • Union typKonvert {
  •       int integer;
  •       float gleitkomma;
  •       byte[4] byteArray;
  •       char[4] charArray;
  • };

Dieses Struct belegt im Speicher immer 4 Byte! Es sind also immer die gleichen Bytes, auf die der Nutzer zugreift, egal ob er das Float oder das Byte Array referenziert. Achtung: Dabei wird ein Integer jedoch nicht in ein Float gecastet, die Bits werden lediglich als Float Interpretiert – das Ergebnis ist stark verschieden.

Allerdings wird einem durch die Union auch Folgendes ermöglicht:

  • Typedef Union{
  •        Messwert_t mWert;
  •         Byte[2] data;
  • } Netzwerknachricht_t;

Nach dem Empfangen der Nachricht können die gesendeten Bytes direkt in ein Messwert_t Struct Konvertiert werden!

Bsp:

  • Netzwerknachricht_t * nachricht;
  • networkRead(nachricht->data);
  • ausgabeAufBildschirm(nachricht->mWert.lambdaWert);

Die Konvertierung des Inhalts der Netzwerknachricht geschieht also ohne zusätzlichen Aufwand.

Achtung: Wenn sich die Endian Typen (also die Byte Reihenfolge) von Nachrichteninhalt und Empfängerrechner unterscheiden, schlägt die Konvertierung über eine Union fehl, hier muss man auf andere Verfahren ausweichen (z.B. per Bitmaske).