Eingebettete C-Unions erklärt: Effizientes Speicher-Sharing & Messaging
Verständnis von Unions in eingebettetem C
In eingebetteten Systemen ist eine Union eine leistungsstarke Datenstruktur, die es ermöglicht, verschiedene Datentypen an derselben Speicherposition zu speichern. Dieser Artikel geht die Grundlagen von Unions durch, zeigt praktische Beispiele und erklärt, warum sie ein bevorzugtes Werkzeug für speicherbeschränkte und bandbreitenempfindliche Anwendungen sind.
Der Unterschied zwischen Structure und Union in eingebettetem C
Zuvor haben wir Structures behandelt: eine Möglichkeit, heterogene Variablen in eine einzelne Entität zu gruppieren. In C sind Unions ein komplementäres Konstrukt, das ebenfalls verschiedene Typen gruppiert, aber mit einem entscheidenden Unterschied – jedes Mitglied teilt sich dieselbe Speicheradresse.
Einführendes Beispiel
Das Deklarieren einer Union ist fast identisch mit dem Deklarieren einer Structure; Sie ersetzen einfach struct durch union:
union test { uint8_t c; uint32_t i; };
Das Template definiert zwei Mitglieder: c belegt ein Byte, und i belegt vier Bytes. Um eine Variable dieses Typs zu erstellen:
union test u1;
Der Zugriff auf Mitglieder erfolgt über den Punktoperator:
u1.i = 10; m = u1.c; // m muss uint8_t sein
Da der Speicherplatz einer Union der Größe ihres größten Mitglieds entspricht, belegt u1 vier Bytes. Das Schreiben in i überschreibt denselben Speicher, den c verwenden würde, sodass die beiden Mitglieder sich gegenseitig ausschließen.
Benötigen wir gemeinsamen Speicherplatz?
Stellen Sie sich zwei eingebettete Geräte, Gerät A und Gerät B, vor, die Status-, Geschwindigkeits- und Positionsdaten austauschen (siehe Abbildung 1). Die Datenpunkte und ihre Größen sind:

Abbildung 1
| Variable | Größe (Bytes) | Bedeutung |
|---|---|---|
| power | 1 | Batterieladung |
| op_mode | 1 | Betriebsmodus |
| temp | 1 | Umgebungstemperatur |
| x_pos | 2 | X-Koordinate |
| y_pos | 2 | Y-Koordinate |
| vel | 2 | Geschwindigkeit |
Wenn Gerät B jede Information benötigt, bräuchte eine einfache Structure neun Bytes, und jeder Kommunikationszyklus würde einen 9-Byte-Frame übertragen (siehe Abbildung 2). In vielen realen Szenarien sind die drei Nachrichtentypen jedoch gegenseitig ausschließend: manchmal nur Status, manchmal nur Position, manchmal nur Geschwindigkeit. In diesem Fall ist die größte Payload vier Bytes (die Positionsnachricht). Eine Union, die einen vier-Byte-Puffer teilt, kann jeden der drei Nachrichtentypen halten, was die Frame-Größe von neun auf vier Bytes reduziert (siehe Abbildung 3).

Abbildung 2

Abbildung 3
Verwendung von Unions für Nachrichtenpakete
Im Folgenden erstellen wir zwei Structures – eine für Status und eine für Position – und betten sie in eine Union zusammen mit dem Geschwindigkeitsfeld ein:
struct status_s { uint8_t power; uint8_t op_mode; uint8_t temp; }; struct position_s { uint16_t x_pos; uint16_t y_pos; }; union msg_union { struct status_s status; struct position_s position; uint16_t vel; };
Die Größe der Union beträgt vier Bytes (die Größe von position_s), die unter allen drei Nachrichtentypen geteilt wird.
Wie man das aktive Union-Mitglied verfolgt
Wenn ein Empfänger eine Union liest, muss er wissen, welches Mitglied gültig ist. Das gängige Muster ist eine getaggte Union: ein separates Feld, das das aktive Mitglied aufzeichnet. Wir können ein ein-Byte-msg_type vor der Union hinzufügen:
struct message_s { uint8_t msg_type; // 's' = status, 'p' = position, 'v' = velocity union { struct status_s status; struct position_s position; uint16_t vel; } msg; };
Durch die Übertragung des msg_type-Bytes zusammen mit der Union kann der Empfänger die Payload korrekt interpretieren, ohne vorherige Kenntnis ihrer Form.
Die Alternative: Dynamische Speicherallokation
Ein anderer Ansatz ist die Allokation von Speicher zur Laufzeit für jeden Nachrichtentyp. Der Sender würde msg_type setzen und gerade genug Bytes für diese Payload allokieren. Der Empfänger würde dann die eingehenden Daten basierend auf msg_type interpretieren und den Speicher freigeben, wenn er fertig ist. Obwohl dies etwas speichereffizienter sein kann, führt dynamische Allokation Laufzeit-Overhead und die Notwendigkeit expliziter Freigabe ein, weshalb viele eingebettete Entwickler die statische Union-Strategie bevorzugen.
Nächstes: Anwendungen von Unions
Unions werden auch häufig verwendet, um kleinere Datenteile aus einem größeren Wort zu extrahieren – eine Technik, die besonders wertvoll ist beim Parsen von Kommunikationsprotokollen oder Hardware-Registern. Bleiben Sie dran für den nächsten Artikel in dieser Serie, wo wir in diese Anwendungsfälle eintauchen.
Um weitere Artikel zu eingebettetem C zu erkunden, besuchen Sie den Artikel-Index.
Eingebettet
- Die ideale Programmiersprache für Industrial-IoT-Lösungen wählen
- Mikroprozessor-Programmiergrundlagen
- Daten im Internet of Reliability nutzen: Strategien für effektives Management
- Demokratisierung des Internet der Dinge: Next-Gen Satelliten-IoT bringt universelle, bezahlbare Konnektivität
- Die 9 wichtigsten Programmiersprachen 2021, die Sie lernen sollten
- C-Unions erklärt: Effizientes Speichermanagement für mehrere Datentypen
- Zukunft der Rechenzentren: Trends, Tech und Nachhaltigkeit
- Cloud-Power für IoT nutzen: Nahtlose Konnektivität & Dateneinblicke freischalten
- Roboterprogrammierung verstehen: Methoden erklärt
- Kompressor-Einheiten meistern: Klare Anleitung zu gängigen Messbegriffen