LOCODUINO

Les structures

.
Par : Jean-Luc

DIFFICULTÉ :

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, à la différence des classes, elles 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 :

struct FeuTricolore {
    byte vert;
    byte jaune;
    byte rouge;
};

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 :

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 :

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.

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 :

pinMode(feuSortieGare.vert,  OUTPUT);
pinMode(feuSortieGare.jaune, OUTPUT);
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 :

struct FeuTricolore feu1;
struct FeuTricolore feu2;

On peut ecrire :

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 :

struct Canton {
    boolean occupe;
    boolean sens;
    const struct FeuxTricolore feuSensNormal;
    const struct FeuxTricolore feuSensInverse;
};

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 :

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

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 :

pinMode(cantonVoiePrincipale1.feuSensNormal.vert,  OUTPUT);
pinMode(cantonVoiePrincipale1.feuSensNormal.jaune, OUTPUT);
pinMode(cantonVoiePrincipale1.feuSensNormal.rouge, OUTPUT);
pinMode(cantonVoiePrincipale1.feuSensInverse.vert,  OUTPUT);
pinMode(cantonVoiePrincipale1.feuSensInverse.jaune, OUTPUT);
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 :

struct FeuTricolore {
    byte vert:4;
    byte jaune:4;
    byte rouge:4;
};

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.

14 Messages

  • Les structures 19 février 2015 14:09, par DDEFF

    Bonjour Jean-Luc,
    Je voudrais insister sur un point important, sous-entendu dans cet article, mais qui va mieux en le disant :
    En utilisant des structures, les noms des variables sont vraiment "parlants" et on évite les erreurs dans les programmes.
    Par exemple :
    "cantonVoiePrincipale1.feuSensNormal.vert" : tout est dit !
    A+

    Répondre

  • Les structures 19 février 2015 15:19, par jerome SAVARY

    Superbe article, une fois de plus.

    J’ai hâte de lire le suivant.

    Merci encore et continuez comme cela.

    Jérôme.

    Répondre

  • Les structures 20 février 2015 10:23, par Francis8

    Pareil que mon camarade Jérôme, l’article est bien utile.

    Merci et on attends la suite avec intérêt.

    Répondre

  • Les structures 20 février 2015 10:30, par Jean-Luc

    Merci à vous deux. La suite arrive avec les fonctions et les tableaux et on aura bientôt fait le tour du langage :)

    Répondre

  • Les structures 25 octobre 2018 18:45, par Nikiema Faissal

    Bonjour
    Bon cours bien limpide.Je suis noviste en programmation. Dans certains documents qui traitent des structures les élément ont une tête de liste et une queue qui pointe vers l’addresse mémoire de l’élément suivant. Mais ici je n’ai pas vu cela, qu’en est il ? merci

    Répondre

    • Les structures 30 octobre 2018 08:46, par Jean-Luc

      Une structure pouvant rassembler des données de toutes natures, on peut tout à fait avoir des membres qui soient des pointeurs vers une structure. Ceci permet notamment de faire des listes chaînées. Mais ce n’est pas obligatoire.

      Répondre

  • Les structures 29 novembre 2018 17:44, par Joel

    Bonjour,
    quelle est la syntaxe pour déclarer et remplir un tableau de structures ?

    cela nécessite-t-il une librairie ?
    merci

    Répondre

    • Les structures 29 novembre 2018 18:07, par Jean-Luc

      Bonjour,

      Non cela ne nécessite pas une librairie.

      Si on veut par exemple initialiser un tableau de FeuTricolore, on écrira :

      struct FeuTricolore mesFeux[3] = { 
        { 5, 6, 7 }, /* mesFeux[0] */
        { 2, 3, 4 }, /* mesFeux[1] */
        { 8, 9, 10 }  /* mesFeux[2] */
      };

      Accéder, par exemple, au vert du feu 1 s’écrira :

      mesFeux[1].vert

      Répondre

  • Les structures 21 janvier 2019 11:14, par coco

    Bonjour et merci pour cet excellent tuto.
    Comment fait on pour créer une structure avec des chaines de caractères comme Nom,Prénom,Age.

    Répondre

    • Les structures 21 janvier 2019 13:08, par Jean-Luc

      Sans doute pas Age :)
      2 solutions :

      1. mettre un tableau de caractères, char nom[20]; dans la struct. Il faut décider à priori de la taille maximum de la chaine (+1 pour le terminateur) ;
      2. mettre un pointeur de caractère, char *nom;. Il faut gérer séparément l’allocation des chaines.

      Répondre

  • Les structures 16 décembre 2020 20:04, par Fantasio

    Bonjour
    Une question de débutant :
    La déclaration d’une structure peut elle se faire dans le sketch ou faut il l’appeler comme une bibliothèque ?

    Merci par avance

    Répondre

  • Les structures 16 décembre 2020 23:19, par Dominique

    C’est dans l’article Les structures
    Vous y êtes !

    Répondre

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 »

Les derniers articles

Les articles les plus lus