Unions en C embarqué expliquées : partage mémoire efficace et messagerie
Comprendre les unions en C embarqué
Dans les systèmes embarqués, une union est une structure de données puissante qui permet de stocker différents types de données au même emplacement mémoire. Cet article parcourt les fondamentaux des unions, montre des exemples pratiques et explique pourquoi elles sont un outil incontournable pour les applications contraintes en mémoire et sensibles à la bande passante.
La différence entre structure et union en C embarqué
Précédemment, nous avons couvert les structures : un moyen de regrouper des variables hétérogènes en une seule entité. En C, les unions sont une construction complémentaire qui regroupe également différents types, mais avec une distinction clé — chaque membre partage la même adresse mémoire.
Exemple introductif
Déclarer une union est presque identique à déclarer une structure ; vous remplacez simplement struct par union :
union test { uint8_t c; uint32_t i; };
Le modèle définit deux membres : c occupe un octet, et i en occupe quatre. Pour créer une variable de ce type :
union test u1;
L'accès aux membres utilise l'opérateur point :
u1.i = 10; m = u1.c; // m doit être uint8_t
Puisque la taille de stockage d'une union équivaut à la taille de son plus grand membre, u1 occupe quatre octets. L'écriture dans i écrase la même mémoire que c utiliserait, les deux membres sont donc mutuellement exclusifs.
Avons-nous besoin d'un espace mémoire partagé ?
Imaginez deux périphériques embarqués, Périphérique A et Périphérique B, qui échangent des données de statut, vitesse et position (voir Figure 1). Les éléments de données et leurs tailles sont :

Figure 1
| Variable | Taille (octets) | Signification |
|---|---|---|
| power | 1 | Charge batterie |
| op_mode | 1 | Mode de fonctionnement |
| temp | 1 | Température ambiante |
| x_pos | 2 | Coordonnée X |
| y_pos | 2 | Coordonnée Y |
| vel | 2 | Vitesse |
Si le Périphérique B nécessite chaque information, une simple structure aurait besoin de neuf octets, et chaque tour de communication transférerait une trame de 9 octets (voir Figure 2). Cependant, dans de nombreux scénarios réels, les trois types de messages sont mutuellement exclusifs : parfois seulement le statut, parfois seulement la position, parfois seulement la vitesse. Dans ce cas, la plus grande charge utile est de quatre octets (le message de position). Une union partageant un tampon de quatre octets peut contenir l'un des trois messages, réduisant la taille de la trame de neuf à quatre octets (voir Figure 3).

Figure 2

Figure 3
Utilisation des unions pour les paquets de messages
Ci-dessous, nous créons deux structures — une pour le statut et une pour la position — et les intégrons dans une union aux côtés du champ vitesse :
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; };
La taille de l'union est de quatre octets (la taille de position_s), partagée entre les trois types de messages.
Comment suivre le membre actif de l'union
Lorsqu'un récepteur lit une union, il doit savoir quel membre est valide. Le motif courant est l'union étiquetée : un champ séparé qui enregistre le membre actif. Nous pouvons ajouter un octet msg_type avant l'union :
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; };
En transmettant l'octet msg_type aux côtés de l'union, le destinataire peut interpréter correctement la charge utile sans connaissance préalable de sa forme.
La solution alternative : allocation mémoire dynamique
Une autre approche consiste à allouer de la mémoire à la volée pour chaque type de message. L'émetteur définirait msg_type et allouerait juste assez d'octets pour cette charge utile. Le récepteur interpréterait alors les données entrantes en fonction de msg_type et libérerait la mémoire une fois terminé. Bien que cela puisse être légèrement plus économe en mémoire, l'allocation dynamique introduit une surcharge à l'exécution et la nécessité d'une libération explicite, c'est pourquoi de nombreux développeurs embarqués préfèrent la stratégie d'union statique.
Prochainement : Applications des unions
Les unions sont également fréquemment utilisées pour extraire des morceaux de données plus petits d'un mot plus grand — une technique particulièrement précieuse lors de l'analyse de protocoles de communication ou de registres matériels. Restez à l'écoute pour le prochain article de cette série, où nous plongerons dans ces cas d'utilisation.
Pour explorer plus d'articles sur le C embarqué, visitez l'index des articles.
Embarqué
- Fondamentaux de la programmation microprocesseur
- Exploiter les données dans l'Internet de la fiabilité : stratégies pour une gestion efficace
- Démocratiser l'Internet des objets : la prochaine génération satellite IoT apporte connectivité universelle, abordable
- Découvrez les 9 langages de programmation à apprendre absolument en 2021
- Les unions en C : Gestion mémoire efficace pour types multiples
- L'avenir des centres de données : tendances, technologies et durabilité
- Exploiter la puissance du Cloud pour l'IoT : connectivité fluide et insights données
- Comprendre la programmation robotique : Méthodes expliquées
- Unités de mesure des compresseurs : guide clair des termes courants