LOCODUINO

Forum de discussion
Dépôt GIT Locoduino

jeudi 19 janvier 2017

41 visiteurs en ce moment

Les structures

. Par : Jean-Luc

Nous avons vu dans « Types, constantes et variables » comment déclarer des données, variables et constantes, et comment les utiliser dans « Calculer avec l’Arduino (1) » et « Calculer avec l’Arduino (2) ». Toutefois, ces données ne peuvent contenir qu’une seule valeur à la fois, ce sont des scalaires [1]. Nous allons maintenant examiner la manière de regrouper des données en paquets, on dit aussi collections, afin de pouvoir, d’une part, les manipuler plus aisément, et, d’autre part, d’améliorer la lisibilité des programmes.

Nous allons nous intéresser aux collections de données hétérogènes, c’est à dire les structures. Les structures permettent de regrouper plusieurs données, chacune ayant un type pour ensuite les manipuler d’un bloc. Les structures forment la base des classes qui sont une forme plus évoluées et qui ont été présentées dans « Le monde des objets (2) » mais ne contiennent que des données.

Comment déclarer une structure

La déclaration d’une structure utilise le mot-clé struct suivi éventuellement d’un nom. Comme la plupart du temps il est préférable de nommer les structures, nous allons le faire systématiquement.

Supposons que nous voulions gérer un feu tricolore composé de 3 DEL   et nécessitant donc 3 broches de connexion. Plutôt que d’avoir 3 broches déclarées séparément, il est plus intéressant de regrouper ces 3 broches dans une struct. Voici la déclaration de cette structure :

  1. struct FeuTricolore {
  2. byte vert;
  3. byte jaune;
  4. byte rouge;
  5. };

Notez qu’à la différence des paires d’accolades employées pour délimiter les boucles, les if ... else et les switch ... case, celle utilisée ici est suivie d’un point-virgule.

Ceci n’est pas une déclaration de variable, pas encore. Nous avons juste décrit comment la structure FeuTricolore est organisée. C’est une sorte de nouveau type de donnée. vert, jaune et rouge sont les membres de la structure. Nous pouvons maintenant déclarer une variable de ce nouveau type, comme ceci :

  1. struct FeuTricolore feuSortieGare;

Ce qui a pour effet de déclarer une variable feuSortieGare qui a pour type struct FeuTricolore.

Initialiser une structure

De même que pour une variable scalaire, il est possible de l’initialiser mais il faut pourvoir donner une valeur à chacun des membres. Ceci se fait en listant les valeurs appropriées entre accolades dans l’ordre où les membres apparaissent dans la structure. Comme ceci :

  1. struct FeuTricolore feuSortieGare = { 5, 6, 7 };

La valeur 5 est ainsi attribuée au membre vert, la valeur 6 au membre jaune et la valeur 7 au membre rouge. Ces valeurs étant des numeros de broche, il est logique de rendre la structure constante en ajoutant le mot-clé const au début.

  1. const struct FeuTricolore feuSortieGare = { 5, 6, 7 };

Utiliser une structure

Il ne suffit pas de déclarer des types structurés. Il faut aussi pouvoir les utiliser en accédant aux membres des variables et constantes de ce type. Pour accéder à un membre d’une structure, il suffit de suffixer le nom de la variable ou de la constante avec un point et le nom du membre. Ainsi pour accéder à chacun des membres de notre structure pour programmer les broches en sortie, on écrira :

  1. pinMode(feuSortieGare.vert, OUTPUT);
  2. pinMode(feuSortieGare.jaune, OUTPUT);
  3. pinMode(feuSortieGare.rouge, OUTPUT);

On peut bien entendu affecter à une variable de type structure une variable ou une constante de même type sans avoir à le faire membre par membre. Supposons que nous ayons 2 variables, feu1 et feu2 déclarées comme ceci :

  1. struct FeuTricolore feu1;
  2. struct FeuTricolore feu2;

On peut ecrire :

  1. feu1 = feu2;

Ce qui engendrera la copie de chacun des membres de feu2 dans les membres correspondant de feu1 .

Des structures dans des structures

Il est tout à fait légal qu’un membre d’une structure soit également une structure. Supposons que nous définissions une structure destinée à représenter un canton. On y trouvera, par exemple, une variable booléenne pour donner l’état d’occupation, une variable booléenne pour donner le sens de circulation et deux feux, un pour pour chaque sens de circulation. Comme nous avons déjà une structure pour les feux, il suffit de la réutiliser dans notre structure Canton :

  1. struct Canton {
  2. boolean occupe;
  3. boolean sens;
  4. const struct FeuxTricolore feuSensNormal;
  5. const struct FeuxTricolore feuSensInverse;
  6. };

L’initialisation respecte les même règles. Comme les membres feuSensNormal et feuSensInverse sont des struct, leur initialisation nécessitera une paire d’accolades supplémentaire :

  1. struct Canton cantonVoiePrincipale1 = {
  2. false, // inoccupé
  3. true, // sens normal
  4. { 4, 5, 6 }, // broches du feu sens normal
  5. { 7, 8, 9 } // broches du feu sens inverse
  6. };

Comme nous pouvons le voir, il ne faut pas hésiter à passer à la ligne et à ajouter des commentaires pour clarifier les initialisations.

L’accès aux membres d’une structure située à l’intérieur d’une structure respecte également les même règles. Il suffit de suffixer une fois de plus pour descendre à l’intérieur de la structure. Ainsi, l’initialisation des broches en sortie pour les deux feux du canton s’écrira de cette manière :

  1. pinMode(cantonVoiePrincipale1.feuSensNormal.vert, OUTPUT);
  2. pinMode(cantonVoiePrincipale1.feuSensNormal.jaune, OUTPUT);
  3. pinMode(cantonVoiePrincipale1.feuSensNormal.rouge, OUTPUT);
  4. pinMode(cantonVoiePrincipale1.feuSensInverse.vert, OUTPUT);
  5. pinMode(cantonVoiePrincipale1.feuSensInverse.jaune, OUTPUT);
  6. pinMode(cantonVoiePrincipale1.feuSensInverse.rouge, OUTPUT);

gagner de la place

Pour terminer avec les struct, nous nous devons de mentionner une possibilité qui est peu utilisée mais qui, au regard de la faible capacité en mémoire de données des Arduino, se révèle particulièrement utile.

Normalement, il est impossible de déclarer des données d’une taille inférieure à 1 octets. Malgré cela, les structures permettent de déclarer des membres en spécifiant le nombre de bits utilisés. Pour chacun des membres, il suffit d’ajouter deux points suivi du nombre de bits voulus. Par exemple, dans notre exemple de structure, on sait que les deux membres booléens ne nécessitent qu’un seul bit car seules deux valeurs sont nécessaires [2]. Si par ailleurs les DEL   des feux seront branchées sur les broches 0 à 13 de l’Arduino, seuls 4 bits sont nécessaires pour coder le numéro de broche [3]. Nous pouvons donc déclarer nos structures comme ceci :

  1. struct FeuTricolore {
  2. byte vert:4;
  3. byte jaune:4;
  4. byte rouge:4;
  5. };

Pour indiquer que chacun des membres occupe 4 bits, soit 12 bits au total, soit 2 octets car la taille totale de la structure est arrondie à l’octet supérieur. La structure est compactée et nous avons donc gagné un octet par rapport à la déclaration originale.

Le programme n’a pas à être modifié pour utiliser ces structures plus compactes, le compilateur se charge de tout.

Ceci est à manier avec précaution. Si une valeur trop grande est écrite dans un membre, un débordement se produira silencieusement. Comme toujours, il est essentiel de veiller à ce que les tailles des variables, et donc des membres, soient suffisantes pour y stocker les informations que l’on souhaite traiter.
 
De plus, si l’utilisation des structures compactées permet de gagner de la place en mémoire de données, elle produit un programme plus lent dans les portions où les membres des structures sont manipulés.

Un prochain article traitera des collections homogènes : les tableaux.

[1En informatique, un scalaire est une donnée qui contient une valeur atomique en opposition a une valeur composite, c’est à dire composée de plusieurs scalaires.

[2false correspond à la valeur 0 et true à la valeur 1

[3Avec 4 bits, 16 valeurs sont possibles, de 0 à 15 dans notre cas. Voir « Systèmes de numération » pour en savoir plus sur le codage des nombres.

5 Messages

Réagissez à « Les structures »

Qui êtes-vous ?
Votre message

Pour créer des paragraphes, laissez simplement des lignes vides.

Lien hypertexte

(Si votre message se réfère à un article publié sur le Web, ou à une page fournissant plus d’informations, vous pouvez indiquer ci-après le titre de la page et son adresse.)

Rubrique « Programmation »

Comment gérer le temps dans un programme ?

La programmation, qu’est ce que c’est

Types, constantes et variables

Installation de l’IDE arduino

Répéter des instructions : les boucles

Les interruptions (1)

Instructions conditionnelles : le if ... else

Instructions conditionnelles : le switch ... case

Comment gérer l’aléatoire ?

Calculer avec l’Arduino (1)

Calculer avec l’Arduino (2)

Les structures

Systèmes de numération

Les fonctions

Trois façons de déclarer des constantes

Transcription d’un programme simple en programmation objet

Les chaînes de caractères

Trucs, astuces et choses à ne pas faire !

Le monde des objets (1)

Le monde des objets (2)

Le monde des objets (3)

Le monde des objets (4)

Les pointeurs (1)

Les pointeurs (2)

Les Timers (I)

Les Timers (II)

Les Timers (III)

Les Timers (IV)

Les derniers articles

Trucs, astuces et choses à ne pas faire !


Dominique

Le monde des objets (4)


Thierry

Les pointeurs (2)


Thierry

Les chaînes de caractères


Thierry

Transcription d’un programme simple en programmation objet


Dominique, Guillaume, Jean-Luc

Les pointeurs (1)


Thierry

Comment gérer l’aléatoire ?


Dominique, Guillaume, Jean-Luc

Les Timers (IV)


Christian Bézanger

Les fonctions


Jean-Luc

Le monde des objets (3)


Thierry

Les articles les plus lus

Les interruptions (1)

Les Timers (I)

Comment gérer le temps dans un programme ?

Calculer avec l’Arduino (1)

Répéter des instructions : les boucles

Les Timers (II)

Le monde des objets (1)

Types, constantes et variables

Les chaînes de caractères

Comment gérer l’aléatoire ?