Structures en C embarqué : guide pratique pour la définition, l'utilisation et l'optimisation mémoire
Les structures sont une pierre angulaire de la programmation C embarquée efficace. Dans cet article, nous explorerons leurs applications pratiques, la syntaxe pour les déclarer, et l'impact de l'alignement des données sur l'utilisation mémoire. En ordonnant stratégiquement les membres de structure, vous pouvez souvent réduire l'empreinte de vos objets de données, une considération critique dans les environnements à ressources limitées.
Cet article offre une vue d'ensemble concise mais complète des structures en C embarqué. Nous couvrirons leurs applications réelles, comment les déclarer correctement, et les règles subtiles d'alignement des données qui affectent la taille de vos structures.
Structures
Lorsque plusieurs variables du même type partagent une relation logique, un tableau est un regroupement pratique. Par exemple, pour conserver les 50 derniers échantillons d'un CAN qui numérise une entrée vocale, vous pourriez utiliser :
uint16_t voice[50];
Ici, uint16_t est un entier non signé 16 bits défini dans stdint.h, assurant une taille cohérente across les plateformes.
Mais que faire si vous devez regrouper des variables de différents types — disons, un tableau d'échantillons et la fréquence d'échantillonnage du CAN ? Les déclarer séparément comme variables indépendantes crée une surcharge inutile. Au lieu de cela, vous pouvez les encapsuler dans une structure :
struct record { uint16_t voice[50]; float sample_rate; };
La structure record contient maintenant à la fois un tableau de uint16_t et un float en un seul objet de données logiquement connecté.
Pourquoi les structures sont-elles importantes ?
Les structures vous permettent d'associer des variables hétérogènes en une unité cohésive, simplifiant la manipulation et améliorant la lisibilité. Elles forment la base de structures de données plus complexes, telles que les listes chaînées et les tampons de messages, omniprésentes dans les communications embarquées et la gestion de données de capteurs.
Par exemple, les périphériques mappés en mémoire sur les microcontrôleurs sont souvent représentés par des structures qui reflètent la disposition des registres matériels, permettant un accès direct aux registres via la syntaxe de membre.

Figure 1. Les structures peuvent être utilisées pour implémenter une liste chaînée.

Figure 2. Carte mémoire d'un MCU STM32. Image courtesy of Embedded Systems with ARM.
Déclaration d'une structure
D'abord, vous définissez un modèle de structure. Considérez cet exemple :
struct record { uint16_t voice[4]; float sample_rate; };
Le modèle lui-même ne consomme pas de mémoire ; l'allocation se produit seulement quand vous déclarez une variable de ce type :
struct record mic1;
Maintenant, mic1 occupe assez d'espace pour quatre éléments uint16_t et un float. Accédez aux membres via l'opérateur point :
mic1.voice[0] = 100; float fs = mic1.sample_rate;
Autres façons de déclarer une structure
Bien que le motif le plus courant définisse la structure d'abord puis déclare les variables, C offre des syntaxes alternatives :
Syntaxe 1 : Balise et variable ensemble
struct record { uint16_t voice[4]; float sample_rate; } mic1;
Les variables ultérieures peuvent être déclarées séparément :
struct record mic2;
Syntaxe 2 : Modèle anonyme
struct { uint16_t voice[4]; float sample_rate; } mic1;
Ici, vous ne pouvez pas réutiliser le modèle ailleurs car il n'a pas de nom.
Syntaxe 3 : Structure anonyme imbriquée
struct test { struct { float f; char a; }; } test_var;
Accédez aux membres internes directement :
test_var.f = 1.2f;
Les structures anonymes réduisent la verbosité mais doivent être utilisées judicieusement pour la clarté.
Disposition mémoire pour une structure
Le standard C garantit que les membres sont disposés en mémoire séquentiellement, dans l'ordre déclaré. Par exemple :
struct Test2 { uint8_t c; uint32_t d; uint8_t e; uint16_t f; } MyStruct;
Sur une machine 32 bits, sizeof(MyStruct) est de 12 octets en raison du rembourrage qui aligne uint32_t sur une limite de 4 octets. Réorganiser les membres peut éliminer le rembourrage :
struct Test2 { uint32_t d; uint16_t f; uint8_t c; uint8_t e; } MyStruct;
Maintenant sizeof(MyStruct) tombe à 8 octets — une économie précieuse dans le firmware à mémoire contrainte.

Figure 3. L'alignement des données peut ajouter du rembourrage à la disposition mémoire d'une structure.
Résumé
- Les structures vous permettent de regrouper des variables hétérogènes en une seule unité gérable, améliorant la clarté et l'efficacité du code.
- Les structures de données personnalisées supportent des tâches comme la messagerie inter-périphériques et le stockage de données de capteurs.
- Elles sont essentielles pour mapper les registres matériels dans les périphériques mappés en mémoire.
- Réorganiser les membres pour respecter l'alignement peut réduire significativement la taille de la structure.
Explorez plus de nos tutoriels C embarqué ici.
Embarqué
- Vis de terrasse (deck screws) expliquées : Types, avantages et comparaison aux vis à bois
- Joints à rappel par ressort (spring-energized seals) : conception, fonctionnement et bénéfices industriels
- Vis à bois expliquées : types, caractéristiques et conseils de sélection
- Les Alliages d'Acier Expliqués : Types, Composition et Applications
- Exploiter les données dans l'Internet de la fiabilité : stratégies pour une gestion efficace
- Structures de données Java : Exploration des classes de base et Collections Framework
- Internet industriel des objets (IIoT) : définition, portée et impact industriel
- Données de maintenance expliquées : la clé d'une gestion d'actifs fiable
- Le travail du bois moderne : tendances, technologies et techniques