98
BTS Informatique de gestion 2 e année Option administrateur de réseaux locaux d’entreprise Jean-Yves Février Algorithmique et langages Cours Directrice de publication : Valérie Brard-Trigo Les cours du Cned sont strictement réservés à l’usage privé de leurs destinataires et ne sont pas destinés à une utilisation collective. Les personnes qui s’en serviraient pour d’autres usages, qui en feraient une reproduction intégrale ou partielle, une traduction sans le consentement du Cned, s’exposeraient à des poursuites judiciaires et aux sanctions pénales prévues par le Code de la propriété intellectuelle. Les reproductions par reprographie de livres et de périodiques protégés contenues dans cet ouvrage sont effectuées par le Cned avec l’autorisation du Centre français d’exploitation du droit de copie (20, rue des Grands Augustins, 75006 Paris).

Cours - Matière 3987_83987TGPA0007

  • Upload
    hanane

  • View
    231

  • Download
    4

Embed Size (px)

DESCRIPTION

algo

Citation preview

Page 1: Cours - Matière 3987_83987TGPA0007

BTS Informatique de gestion 2e annéeOption administrateur de réseaux locaux d’entreprise

Jean-Yves Février

Algorithmique et langages

Cours

Directrice de publication : Valérie Brard-TrigoLes cours du Cned sont strictement réservés à l’usage privé de leurs destinataires et ne sont pas destinés à une utilisation collective. Les personnes qui s’en serviraient pour d’autres usages, qui en feraient une reproduction intégrale ou partielle, une traduction sans le consentement du Cned, s’exposeraient à des poursuites judiciaires et aux sanctions pénales prévues par le Code de la propriété intellectuelle. Les reproductions par reprographie de livres et de périodiques protégés contenues dans cet ouvrage sont effectuées par le Cned avec l’autorisation du Centre français d’exploitation du droit de copie (20, rue des Grands Augustins, 75006 Paris).

Page 2: Cours - Matière 3987_83987TGPA0007
Page 3: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 003

Conseils généraux

Ce cours est constitué de 3 séquences et 3 TD. Il est accompagné d’un fascicule d’autocorrection. Pour l’évaluation « notée » vous avez un fascicule de 2 devoirs à envoyer à la correction.

Voici l’ordre dans lequel il faut exploiter tout cela :

• vous faites les séquences 1 à 2 ;

• pour valider vos connaissances, vous envoyez le devoir 1 à la correction ;

• vous faites la séquence 3 ;

• ensuite, vous faites le TD 1 ;

• pour valider vos connaissances, vous envoyez le devoir 2 à la correction ;

• ensuite, vous faites le TD 2 ;

• ensuite, vous faites le TD 3.

Pour votre option, la programmation est un petit cours. Ne le bâclez pas en une semaine dès octobre, mais étudiez-le de temps en temps pour bien assimiler les concepts tout au long de l’an-née. Dans le meilleur des cas, vous devriez commencer la séquence 1 après avoir reçu les cours et envoyer le devoir 2 à la correction en fin d’année scolaire (c’est-à-dire, pour vous, en mars). Je vous conseille :

• de travailler en un mois ou deux le cours (il n’est pas très long) ;

• de passer ensuite beaucoup de temps sur les TD qui, eux, sont longs, et d’en faire un exercice de temps en temps pour garder le contact avec la programmation tout au long de l’année.

La place de la programmation

Dans le référentielLe cours que vous êtes en train de lire concerne la programmation en 2e année de BTS, option administrateur de réseaux locaux d’entreprise.

C’est un cours essentiel pour votre carrière d’informaticien. Certes, vous ne serez normalement pas amené à développer des programmes comme vos collègues de l’autre option. Cela dit, la démar-che algorithmique sera toujours présente dans votre métier. L’exemple le plus classique, ce sont les scripts (shells) qui vous permettront d’automatiser considérablement votre travail... si vous êtes capable de les écrire. Pour cela, la maîtrise des boucles et des tests est indispensable.

Dans le référentiel de la formation, ce cours est inclus dans le savoir S3 DAIGL (développement d’applications informatiques et génie logiciel) avec l’analyse. Plus précisément, nous traiterons ici le savoir S35 du référentiel intitulé conception et développement d’applications à l’aide d’un langage de programmation procédural.

Je vous avoue que vous avez étudié le plus gros en première année puisque le tronc commun en programmation correspond à ce que vous, administrateurs réseaux, devez savoir.

Page 4: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 004

Ce cours vous apportera quelques compléments mais sera pour l’essentiel l’occasion de réviser et de mettre en œuvre les notions étudiées en première année.

Si vous aviez des difficultés l’année dernière en programmation, donnez-vous une chance : le recul, l’approche différente puisque les exigences ne sont plus les mêmes... Tout cela peut vous permettre, non pas d’adorer la programmation, mais au moins d’en assimiler les bases sans trop de difficulté. À l’examen (épreuve d’étude de cas), vous aurez de l’algorithmique relativement simple... si vous n’êtes pas totalement allergique à la discipline.

Dans la formationEn formation initiale (en lycée), la seconde année s’étend sur 29 semaines (hors stage), le volume horaire hebdomadaire de la programmation étant pour votre option d’environ une heure de cours et une heure de travaux dirigés. Cela représente 60 heures.

Faut-il donc passer 60 heures sur ce support ? Non, sans doute pas, car bien qu’apportant les mêmes connaissances qu’un cours en lycée (vous n’êtes pas volé), il est présenté de façon plus concise et l’apprentissage autonome est plus rapide. Néanmoins, il ne faut pas espérer maîtriser la programmation en lisant d’une traite ce fascicule. Vous devez apprendre à passer du temps sur les concepts présentés.

Chacun de vous a un rythme de travail différent. La question ne doit donc pas être « combien de temps passer sur un cours ? », mais « ai-je compris et retenu les concepts présentés ? ». Si la réponse à cette seconde question est oui, vous pouvez passer à la séquence suivante. Si c’est non, il faut reprendre la séquence courante un peu plus tard.

En effet, il y a une grande différence entre « comprendre plus ou moins le principe » et « parfaite-ment assimiler un concept ». Dans le premier cas, le savoir n’est pas assimilé et cela vous bloquera dans la suite du cours ; dans le second cas, tout ira bien.

Pour chacun des concepts que nous verrons, il faudra d’une part apprendre parfaitement sa défini-tion (travail de mémoire) et d’autre part identifier ce que cela signifie (travail de compréhension).

Présentation du support de cours

Ce cours a été conçu pour pallier au maximum les difficultés de l’apprentissage à distance : les notions à retenir (définitions...) sont mises en avant et des exercices et questions sont présents tout au long du support pour vous permettre de vérifier votre compréhension.

Mais j’insiste sur le point suivant : quelle que soit la qualité pédagogique de ce cours, il ne vous permettra pas d’assimiler la programmation par simple imprégnation visuelle. Vous devrez four-nir un travail d’apprentissage (le cours), de réflexion (toujours le cours) et d’entraînement (les exercices).

Le cours est constitué de deux fascicules : celui que vous avez en main et un fascicule contenant la correction de tous les exercices. Les programmes de ce cours sont à télécharger en tapant l’adresse suivante : http://www.campus-electronique.tm.fr/BTS-Informatiquegestion, puis en allant à la rubri-que téléchargez.

Page 5: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 005

OrganisationLe fascicule de cours contient différentes choses :

• trois séquences de cours correspondant aux savoirs de S35 ; à la fin de chaque séquence, vous trouverez une fiche synthèse vous rappelant les choses essentielles (à apprendre !) ;

• des exercices intégrés aux séquences de cours. Vous devez faire ces exercices quand vous arri-vez dessus puis aller consulter la correction. Attention, ces exercices permettent de vérifier votre assimilation du cours. Leur corrigé, très détaillé, ne doit pas être négligé : j’y présente des situations, des techniques, des idées et des raisonnements qui ne sont pas anecdotiques et font partie intégrante du cours. S’ils ne sont pas physiquement dans le fascicule de cours, c’est uniquement pour conserver une certaine lisibilité au document ;

• trois séries d’exercices jouant le rôle de travaux dirigés. Elles sont placées à la fin du cours.

Le fascicule d’autocorrection comprend :

• la correction des exercices intégrés aux séquences ;

• la correction des TD.

• la correction du devoir « autocorrectif ».

En plus de vous donner la solution des exercices, j’ai essayé de détailler ces corrections pour envi-sager les différentes solutions possibles, les erreurs à éviter... Plus que des corrections, ce sont de véritables éléments de cours. Il ne faut donc pas les prendre à la légère !

ContenuLes trois séquences abordent les points suivants :

• variables, types et instructions de contrôle dans la 1re séquence ;

• les sous-programmes et les paramètres dans la 2e ;

• les tableaux et les types structurés dans la 3e.

Les séquences ont été définies pour vous aider à établir votre progression. Elles représentent donc approximativement un volume de travail identique. Le découpage n’est néanmoins pas arbitraire : les différents concepts ont été répartis au mieux pour que chaque séquence reste cohérente.

NotationsPour vous aider à identifier les différents constituants du cours, j’ai utilisé les représentations suivantes :

• tout ce qui est mis en vert doit être appris par cœur. Cela correspond aux définitions ou explications qu’il est absolument nécessaire de connaître pour s’en sortir en programmation. Quand je dis apprendre, ce n’est pas retenir pour l’heure qui suit afin d’épater les convives au prochain repas. Il s’agit d’une vraie leçon, dont vous devez vous souvenir tout au long de votre vie d’informaticien, ou, plus prosaïquement, au moins jusqu’à l’examen. Ces informa-tions sont reprises à la fin de chaque séquence dans la fiche synthèse ;

• les exercices intégrés dans les séquences et destinés à vérifier votre compréhension doivent être faits au fur et à mesure de la lecture du cours. Leur correction se trouve dans le fascicule correction des exercices. Ils sont présentés sur un fond vert .

Page 6: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 006

Quelques conseilsLe seul conseil utile que je puisse vous donner est de garder à l’esprit la fable de La Fontaine Le lièvre et la tortue : il ne sert à rien de travailler comme un fou en juin ; travaillez plutôt dès maintenant quelques heures par semaine ré-gu-li-è-re-ment (j’aurais pu écrire régulièrement ou RÉGULIÈREMENT, mon but étant juste d’insister sur le mot).

La difficulté de l’enseignement par correspondance réside dans le fait que, par définition, vous êtes seul face au cours, personne n’est là pour vous guider, insister sur l’essentiel ou établir la progression du cours.

Pour vous aider à suivre un rythme correct, disons que chaque séquence correspond à un travail d’environ 4 à 6 heures.

Attention à l’environ ! Vous avez sans doute le souvenir de vos études où, pour obtenir un même résultat, certains travaillaient toute la soirée et d’autres se contentaient d’être présents en cours. Il en est de même ici. Les 5 heures ne sont qu’un ordre de grandeur signifiant juste que 15 minutes, ce n’est pas assez, mais 25 heures, c’est trop.

Retenez qu’il vaut mieux passer 8 heures sur une séquence et la comprendre parfaitement, que faire exactement 300 minutes (5 heures) en passant à côté de l’essentiel.

De plus, le cours contient des dizaines de petits exercices à faire au fur et à mesure. Plus vous passerez du temps dessus (et c’est conseillé), plus vous risquez de dépasser les 5 heures.

Sommaire

Séquence 1 : Variables et instructions 7

Séquence 2 : Procédures et fonctions 35

Séquence 3 : Tableaux, structures et types 53

Travaux dirigés 1 81

Travaux dirigés 2 89

Travaux dirigés 3 93

Devoir « autocorrectif » à ne pas envoyer à la correction 95

Page 7: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 007

Séquence 1

Variables et instructionsDurée indicative : 5 heures

Dans cette séquence, nous allons reprendre les éléments clés de la première année.

Capacités attendues• Se souvenir du cours de l’année dernière

• Avec le recul, maîtriser ces concepts

Contenu1. Introduction .............................................................................................. 8

2. Le programme ......................................................................................... 8

2A. Syntaxe générale .......................................................................................... 8

2B. Commentaire ................................................................................................ 9

3. Les variables et les types .................................................................. 11

3A. Définition .................................................................................................... 11

3B. Pourquoi le type est-il obligatoire ? ......................................................... 12

3C. Gestion de la mémoire ............................................................................... 14

3D. Exemple ....................................................................................................... 14

3E. Pourquoi tant de détails ? ......................................................................... 19

3F. Manipulation des variables ....................................................................... 19

4. L’alternative ............................................................................................ 21

5. Les boucles (répétitives) .................................................................... 23

5A. Tant que… faire .......................................................................................... 23

5B. Répéter… jusqu’à ....................................................................................... 25

5C. Pour ............................................................................................................. 26

5D. Conclusion sur les boucles ......................................................................... 29

6. Application .............................................................................................. 30

Synthèse

Page 8: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 008

Séquence 1

1. Introduction

Ma grande idée est que l’informatique n’a rien inventé : elle copie tous ses concepts de ce qui l’entoure.

Autrement dit, les concepts que les informaticiens manipulent ne sont que des métapho-res des choses de la vie réelle. De plus, rien n’est jamais inutile ni incohérent en informa-tique. C’est ce qui en fait l’élégance. Lorsque quelque chose ne nous semble pas logique, c’est généralement parce qu’il nous manque des éléments théoriques.

Nous allons travailler de la façon suivante : je vais vous demander de définir précisément chaque concept étudié en première année. L’objectif est de vous plonger dans vos souve-nirs et de vérifier si vous maîtrisez réellement ces différentes notions. Il faut donc jouer le jeu et répondre sérieusement. Ne vous contentez pas de répondre oralement voire dans votre tête ! Prenez le temps de réfléchir et de rédiger votre réponse.

Je vous proposerai ensuite ma définition puis quelques exemples.

2. Le programme

2A. Syntaxe générale

Exercice 1

Commençons simplement : qu’est-ce qu’un programme ? Comment l’écrit-on ? Comment s’exé-cute-t-il ? Quelle est la différence entre programme et algorithme ?

Un programme ou un algorithme est une suite d’instructions. Il doit réaliser quelque chose, à savoir un traitement précis à partir de données fournies en entrée.

Comment l’écrit-on ? Et bien, on énumère les variables utilisées, puis on écrit les sous-programmes (procédures et fonctions) et enfin le programme principal. (Lorsque l’on utilise un langage événementiel, on peut souvent se passer de programme principal.)

La différence entre un algorithme et un programme est assez ténue : l’algorithme est une construction intellectuelle sur papier avant tout destinée à réfléchir pour trouver une solution à un problème. Le programme, c’est la traduction plus ou moins simple d’un algorithme dans un langage de programmation quelconque (VB, Delphi, C…) pour pouvoir l’exécuter.

On pourrait dire que l’algorithme, c’est le plan de l’architecte et le programme, c’est la maison terminée… sauf qu’il est plus difficile de passer du plan à la maison que de l’al-gorithme au programme.

Comment s’exécute un programme ? Ma foi, on traite séquentiellement (une à une dans l’ordre) toutes les instructions, de la première à la dernière. (Cela n’étant valable qu’en absence de plantage !)

Page 9: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 009

Variables et instructions

Il est important de se souvenir qu’une instruction peut en contenir d’autres. Ainsi, exé-cuter une alternative ou une boucle peut conduire à exécuter dix instructions (nous y reviendrons).

La syntaxe générale d’un algorithme (sans sous-programme pour l’instant) est :

Algorithme nom de l’algorithme

var déclaration des variables

début instructionsfin

2B. Commentaire

2B1. Rôle du commentaire

Il est nécessaire de placer des commentaires dans un programme. Ils ne sont destinés qu’au développeur et sont totalement ignorés par le compilateur.

Vous mettrez des commentaires pour expliquer le déroulement de vos algorithmes :

• pour identifier les différentes étapes ;

• pour expliquer des instructions complexes ou des indices de boucle malaisés à com-prendreå.

Le commentaire est utile à tous :

• faire l’effort de rédiger une explication vous oblige à formaliser les choses beaucoup plus que lors de la simple écriture du code. Cela peut vous permettre de détecter une erreur ;

• si vous devez intervenir sur votre programme quelques jours ou semaines plus tard, vous ne comprendrez plus les parties complexes de vos algorithmes. Un commen-taire vous rafraîchira la mémoire. À défaut, vous devrez recommencer le processus intellectuel vous ayant amené à ce code. Et ça, c’est assez humiliantç ;

• dans le cas d’un développement professionnel en entreprise, vous interviendrez constamment sur du code qu’un autre aura rédigé, soit parce que vous travaillerez à plusieurs, soit tout bonnement parce qu’il faut toujours maintenir les anciennes applications. Or, chacun possède sa propre logique et ses tics de programmation. Du coup, lire un code sans commentaire peut se révéler très complexe. Et face à une instruction bizarre, il faudra beaucoup réfléchir pour déterminer si c’est un bug (à corriger) ou une astuce de programmation ;

å Par exemple, si une boucle va de l’indice i à i+j+2, il est sans doute intéressant d’expliquer pourquoi la valeur finale contient « +2 » et pas « +1 » ou n’est pas simplement i+j. En effet, c’est typiquement sur ce genre de choses que le développeur produit des bugs.

ç Je vous assure que voir un bout de code que l’on a écrit et ne plus réussir à comprendre ce qu’il signifie, c’est vraiment humiliant. Et après, plein de questions malsaines viennent à l’esprit, du genre « est-ce que je serais encore capable d’écrire un code aussi efficace ? ».

Page 10: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0010

Séquence 1

• un commentaire permet de signaler que, même si ce n’est pas évident au premier coup d’œil, votre code prend en compte telle situation ou tel cas d’erreur ou, au contraire, il peut expliquer pourquoi cette situation ou cas d’erreur n’est pas prise en compte (par exemple, parce que du code en amont a déjà réglé le problème). Le lecteur sait alors que ces cas ont été envisagéså ;

• tout ceci est valable quelle que soit votre option. Dans mon lycée, je gère le parc infor-matique du BTS IG. J’ai naturellement développé des scripts pour automatiser certai-nes tâches d’administration du réseau, comme tout administrateur de réseau local doit le faire. Vous trouverez beaucoup d’exemples de scripts dans les documentations ou sur internet. Je vous assure qu’ils sont beaucoup plus denses et complexes qu’un algorithme de gestion standard. Et sans commentaires, les adapter à vos besoins est une gageure.

2B2. Écriture du commentaire

Écrire des commentaires, c’est tout un art. Il ne faut pas en mettre trop ou trop peu. Vous éviterez les deux écueils qui suivent.

La paraphrase

Réservez le commentaire pour les parties compliquées du code et ne faites pas de tra-duction en français des instructions.

Par exemple, envisageons ce bout de code :

pour i de 22 à LongueurTableau+3 faire table1[i+3] := table2[i+1] …fin pour

Exercice 2

Concernant ce code, que pensez-vous du commentaire suivant :« On prend la case i+1 de table2 pour la mettre dans la case i+3 de table1 pour toutes les

valeurs de i allant de 22 à LongueurTab+3. » ?

En revanche, les points qui méritent une explication sont :

• pourquoi la boucle va de 22 à LongueurTableau+3 et pas de 1 à LongueurTableau comme on pourrait s’y attendre ?

• pourquoi on affecte la case i+1 d’un tableau à la case i+3 d’un autre ? En effet, habituellement, on affecte deux cases de même rang (ici i+1 ou i+3) ;

• le corps de la boucle n’utilise jamais i directement mais i+1 et i+3. Ce n’est pas très lisible. Y a-t-il une raison à ce décalage du corps par rapport à l’indice ?

Dans un code, on commente en fonction des difficultés de ce code. Un algorithme sim-ple contiendra des commentaires sur des choses simples. En revanche, il est évident que, dans un programme très complexe, les commentaires feront l’impasse sur les parties pas trop dures pour se concentrer sur ce qui est réellement difficile. En clair, un morceau de code pourra être valablement expliqué dans un algorithme et tout aussi valablement pas expliqué dans un autre.

å Lorsque vous intervenez sur un petit morceau de code, vous n’êtes pas au fait de l’ensemble de l’application.

Page 11: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0011

Variables et instructions

Le verbiage

Un commentaire doit être écrit :

• dans un français irréprochable pour lever toute ambiguïté. L’objet d’un commen-taire, c’est d’expliquer. S’il n’est pas compréhensible, il ne sert à rien ;

• de façon concise. Plus vos commentaires sont brefs et tranchants, plus ils seront exploités.

Nous retiendrons que :

Un commentaire est précédé par des caractères // (deux barres de division). Dit autrement, tout ce qui suit les deux barres est ignoré par le compilateur.

Le commentaire doit être clair, concis, sans ambiguïté et n’expliquer que ce qui doit l’être. Ce n’est pas une fioriture mais une plus-value à votre code.

3. Les variables et les types

3A. Définition

Exercice 3

Définissez variable et type en précisant bien le lien entre les deux. Expliquez ce qu’est la déclaration de variable et à quoi elle sert.

Reprenons (et apprenons) la correction :

Type :Le type d’une variable décrit sa nature, ce qu’elle contient (entier, booléen, réel, caractère…).

Variable :C’est un espace mémoire contenant une valeur d’un type donné.

Déclaration :La déclaration d’une variable contient un nom de variable explicite (qui a un sens pour le développeur) et un type. Elle entraîne la réservation d’un espace mémoire de taille suffisante (fonction du type) pour y stocker la valeur. Le nom de la variable est associé à l’espace mémoire.

Exemple :

var âge : entier

Je viens de déclarer la variable entière âge. Plus précisément, je viens de réserver un emplacement mémoire (où dans la mémoire ? ce n’est pas mon problème) de taille suffi-sante pour y stocker une valeur entière. Cet emplacement mémoire est nommé âge.

Page 12: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0012

Séquence 1

La syntaxe (l’écriture) de la déclaration dépendra du langage utilisé mais vous aurez toujours à fournir un nom et un type de donnéeå.

3B. Pourquoi le type est-il obligatoire ?

3B1. ThéorieTout est stocké sous la forme de 0 et de 1 dans la mémoire de l’ordinateur (RAM), que ce soient les instructions en cours d’exécution (le programme) ou les données en mémoire (entiers, booléens, réels, caractères…).La mémoire contient des bitsç (valeur 0 ou 1) regroupés par 8 pour former un octet. La mémoire est donc divisée en (beaucoup de) octets.J’ai écrit un petit programme sous Delphi et je suis allé voir le contenu de la mémoire en piochant au hasard les seize bits suivants : 0110001011001001. Seize bits, c’est deux octets, soit : 01100010 11001001. En traduction décimale, cela donne 98 et 201. Nous allons essayer de comprendre ce que représentent ces deux octets.Dans la mémoire, on trouve la valeur des variables, mais aussi les instructions du pro-gramme qui s’exécute. Je repose ma question : que représentent ces deux octets ? Et bien, il est totalement impossible de le savoir.En effet, puisque toute information (valeur d’un type quelconque ou instruction) est codée sous la forme de 0 et de 1, on dira de façon symétrique que toute suite de 0 et de 1 est capable de représenter une instruction ou une variable d’un type quelconque.

3B2. Exemple

Nous allons prendre l’exemple de mes deux octets. Sous Delphi, les deux octets 01100010 11001001 sont la représentation en mémoire de choses très diversesé :

• la chaîne de caractères «bÉ». Étant constituée de deux caractères, elle occupe deux octets en mémoire. Notez que rien n’est simple : peut-être qu’en vrai, mon pro-gramme a stocké une chaîne de 5 caractères (par exemple «XbÉTy») en mémoire et que, en prenant mes 16 bits au hasard, j’ai extrait ceux codant les 2e et 3e caractères de cette chaîne ;

• les deux caractères b et É (donc deux valeurs indépendantes mais stockées dans des zones mémoire contiguës) ;

• le tableau de deux octetsè [98,201] ;

• les deux octets 98 et 201 (donc deux variables indépendantes) ;

å Enfin, certains langages ne demandent pas de type (PHP) ou peuvent s’en passer (VB). Le com-pilateur se débrouille alors pour déterminer tout seul le type de la variable (si vous écrivez une instruction i := 1, il déterminera que i est un entier) ou il utilise un type générique (variant) permettant, au prix d’un gros gâchis de ressources et d’une nette dégradation des performances, de stocker n’importe quel type de donnée de base. Tout cela pour vous dire qu’il faut typer ses variables dès que le langage l’y autorise.

ç Attention aux homonymes ! Le bit informatique est un nom masculin et sans e. C’est vraiment une coquille à éviter, à l’oral comme à l’écrit.

é Ce qui suit est exact car j’ai exploité la documentation de Delphi pour connaître les modes de codage. Je ne vous les explique pas, il faut me faire confiance.

è L’octet (type Byte sous Delphi) est un entier allant de 0 à 255 codé sur 8 bits (un octet).

Page 13: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0013

Variables et instructions

• le motå 51554 ;

• l’instruction assembleur bound ecx, ecxç ;

• l’entier courté –13982 ;

• deux valeurs booléennes vrai et vrai (ou le tableau de deux booléens [vrai, vrai]) ;

• …

Mes deux octets peuvent donc être interprétés de multiples façons et, sans plus de préci-sion, je n’ai aucun moyen de savoir quelle valeur retenir puisque toutes sont valides. Pour trancher, je dois impérativement connaître le type de la valeur stockée. Vous remarque-rez en passant que rien ne distingue deux valeurs indépendantes d’un tableau de deux valeurs. Nous reviendrons dessus dans la séquence 3, paragraphe 2.

Quand j’accède à la mémoire, je dois donc savoir quoi y chercher. Par exemple, si je sais que mes deux octets stockent la valeur :

• d’un mot, je n’ai qu’à appliquer les règles de codage des mots : un mot m compris entre 0 et 65 535 sera codé sur deux octets successifs x et y tels que m = x + y*256. Ici, x et y valent respectivement 98 et 201 donc m = 98 + 201*256 = 51 554 ;

• de deux caractères (qu’ils soient dans un tableau ou indépendants ne change rien), j’applique les règles de codage des caractères : chacun est représenté par son code ASCII. Or, les caractères de code 98 et 201 sont respectivement b et É ;

• …

3B3. Allons plus loin

Dans les différentes interprétations que j’ai données des deux octets, je n’ai pas parlé de nombres réels. Pourquoi ? Car ces derniers sont stockés sur au moins quatre octets (trente-deux bits). Ainsi, mes deux octets ne seront au mieux qu’une partie des bits nécessaires au stockage d’un nombre réel.

D’ailleurs, j’ai bien dit que je prenais mes seize bits au hasard. Peut-être les ai-je prélevés en plein milieu de vrais octets (en gras mes seize bits) :

11100110 00101100 10010101

Dans ce cas, il ne s’agit plus des deux octets 98 et 201, mais des trois octets 230, 44 et 149.

Ces deux dernières remarques me permettent de préciser une chose importante. Pour accéder à une valeur stockée en mémoire, il faut savoir :

• quoi chercher ;• où le chercher.

Par exemple, si je cherche un mot (entier de type Word, répondant à quoi), je sais que je devrai considérer deux octets consécutifs. Si je connais l’emplacement mémoire du premier (répondant à où), je n’ai plus qu’à lire les deux puis à convertir leur contenu en une valeur de type mot comme ci-dessus.

å Le mot (Word) est un entier entre 0 et 65 535 codé sur 16 bits (deux octets).

ç Que fait cette instruction ? Je n’en sais rien et cela n’a aucune importance.

é L’entier court (SmallInt) est un entier entre –32 768 et 32 767 codé sur 16 bits.

Page 14: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0014

Séquence 1

3C. Gestion de la mémoireJ’ai dit dans le paragraphe précédent que la mémoire était divisée en octets. Comme toujours, pour accéder à quelque chose, il faut l’identifier. Pour accéder à un octet de la mémoire, il faut donc l’identifier. Pas question de dire « je veux cet octet là, non, pas le gros débraillé à gauche, celui qui est juste derrière ». Il n’est évidemment pas possible de nommer les milliards d’octets constituant la mémoire. Le plus simple est donc de les numéroter comme le sont les éléments d’un tableau.

Dans la réalité, on passe par une notion de page équivalente à celle d’un livre (il est plus rapide de trouver la 24e ligne de la page 356 que la 17 824e ligne d’un livre). Nous simplifions donc en considérant la mémoire comme un tableau Excel. Cela ne change rien au propos.

De même, vous savez que les systèmes d’exploitation actuels sont multitâches. Plusieurs applications tournant simultanément, chacune doit avoir son propre espace mémoire pour stocker ses données en toute sécurité. Nous ne gérerons pas cela (c’est un problème de système d’exploitation) et nous considérerons que toute la mémoire est à nous.

Dans le paragraphe 3A, j’ai dit que la déclaration de variable réservait quelque part dans la mémoire une zone de taille suffisante pour y stocker son contenu. En effet, c’est le compilateur qui gère la mémoire. Ainsi, le programmeur donne un nom à sa varia-ble tandis que le système lui attribue un ou plusieurs octets consécutifs numérotés. Le compilateur doit donc faire le lien entre les deux car lorsque le développeur parle de la variable âge, l’application doit savoir :

• que c’est un entier ;

• dans quels octets de la mémoire la valeur de âge est stockée.

Pour cela, l’application va gérer une table (un tableau) associant à chaque nom de varia-ble son type et l’emplacement dans la mémoire du premier octet qu’elle occupe.

(Ce tableau est lui-même stocké dans une autre zone de la mémoire.)

3D. Exemple

3D1. Début de programme

Nous sommes au début du programme, aucune variable n’est définie et la mémoire est vide. Mais que veut dire vide ? En fait, il y a toujours quelque chose, par exemple les données du programme que vous venez de quitter ou des valeurs aléatoires dépendant de l’état électrique de la mémoire. Les octets correspondants sont néanmoins réputés libres, dans la mesure où ce qu’ils contiennent n’a pas de signification pour nouså. C’est pour cette raison que vous devez initialiser vos variables avant usage : pour éviter de récupérer des données stockées par d’anciens programmes.

å Un policier venant arrêter quelqu’un et ne le trouvant pas chez lui dira à son supérieur « L’appartement était vide. » Le déménageur, une fois avoir enlevé toutes vos affaires, dira « l’ap-partement est vide ». Vous êtes d’accord que ce ne sont pas les deux mêmes vides ?

Page 15: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0015

Variables et instructions

Voilà l’état de la mémoire au commencement. J’insiste : le contenu des cases A1 à J4 ne signifie rien. J’ai donc écrit en gris les données pour bien marquer le coup.

Tableau des variables État de la mémoireVARIABLE TYPE ADRESSE DÉBUT A B C D E F G H I J

1 22 2 246 235 222 109 20 170 225 552 24 240 41 108 85 45 230 15 145 553 73 213 65 214 206 56 245 41 92 964 224 73 221 153 180 124 122 119 8 211

3D2. Déclaration des variables

Je déclare un entier de type mot (stocké dans deux octets) et une chaîne de sept caractères :

var Taille : mot Nom : chaîne[7] // chaîne de 7 caractères

L’application va alors réserver quelque part là où il y a de la place (pour le moment, partout) deux octets pour le mot et sept pour la chaîne puis mettre à jour le tableau des variables.

Tableau des variables État de la mémoireVARIABLE TYPE ADRESSE DÉBUT A B C D E F G H I J

Taille mot D2 1 22 2 246 235 222 109 20 170 225 55Nom chaîne[7] B4 2 24 240 41 108 85 45 230 15 145 55

3 73 213 65 214 206 56 245 41 92 964 224 73 221 153 180 124 122 119 8 211

Notez qu’il ne s’est rien passé en mémoire de A1 à J4. Seul le tableau des variables a été modifié. Cela dit, dès ce moment, les cellules D2, E2 et B4 à H4 contiennent quelque chose pour le programme (la valeur de ses variables), raison pour laquelle elles sont encadrées et écrites en noir. Si vous accédez à la variable :

• Taille, l’application ira chercher ce qui se trouve en D2 et E2 et l’interprétera comme un entier de type mot ;

• Nom, l’application interprétera le contenu de B4 à H4 comme les codes ASCII des différents caractères constituant la chaîne.

3D3. Valeur avant initialisation

Voyons cela ; supposons alors que, juste après les avoir initialisées, j’affiche le contenu de mes variables :

Afficher (Taille)Afficher (Nom)

Que vais-je obtenir ?

• l’affichage de Taille donnera 21868 (car 108+85*256 = 21 868, voir le paragraphe 3B2 ci-dessus) ;

• l’affichage de Nom donnera IÝ™´|zw. Cette chaîne est formée par les caractères dont les codes ASCII sont dans les zones B4 à H4. Par exemple, le code ASCII de Ý est 221.

Page 16: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0016

Séquence 1

3D4. Initialisation des variables

Ces valeurs ont-elles un sens pour notre application ? Non, aucun. C’est la raison pour laquelle je dois initialiser mes variables dans le programmeå :

Taille := 51554Nom := «février»

Que fait l’application ? Elle cherche dans le tableau des variables à quel endroit de la mémoire se trouve Taille (elle commence à l’octet D2 et comme c’est un mot, elle se con-tinue sur E2). Elle y stocke le nombre 51 554 codé, nous l’avons vu, par les deux octets 98 et 201ç.

Ensuite, c’est le tour de Nom. Nous avons vu précédemment que pour stocker une chaîne, on stockait successivement les codes ASCII de chaque caractère. Au final, on obtient :

Tableau des variables État de la mémoireVARIABLE TYPE ADRESSE DÉBUT A B C D E F G H I J

Taille mot D2 1 22 2 246 235 222 109 20 170 225 55Nom chaîne [7] B4 2 24 240 41 98 201 45 230 15 145 55

3 73 213 65 214 206 56 245 41 92 964 224 102 233 118 114 105 101 114 8 211

(Ici et dans les tableaux suivants, les changements dans l’état de la mémoire sont en gras.)

3D5. Modifications des variables

Modification de Taille

Dans notre programme, nous incrémentons notre variable numérique :

Taille := Taille+2

Que va faire l’application ? Toute affectation se réalise en deux temps :

1. on évalue l’expression à droite de l’opérateur d’affectation ;

2. on affecte le résultat à la variable à gauche.

Pour calculer l’expression, on va récupérer la valeur de Taille (soit le contenu des deux octets D2 et E2 interprétés comme un mot), on lui ajoute 2 et on obtient 51 556.

Ce résultat est ensuite affecté à la variable Taille, les deux cases D2 et E2 interprétées comme un mot devenant respectivement 100 et 201.

On obtient ceci :

Tableau des variables État de la mémoireVARIABLE TYPE ADRESSE DÉBUT A B C D E F G H I J

Taille mot D2 1 22 2 246 235 222 109 20 170 225 55Nom chaîne [7] B4 2 24 240 41 100 201 45 230 15 145 55

3 73 213 65 214 206 56 245 41 92 964 224 102 233 118 114 105 101 114 8 211

å Les langages de programmation modernes initialisent automatiquement les variables décla-rées à 0. N’exploitez pas cette caractéristique dans vos algorithmes car le correcteur ne saura pas si vous n’avez fait aucune initialisation parce que vous saviez que le compilateur la faisait, ou parce que vous n’y aviez pas pensé.

ç Car 98 + 201*256 = 51 554.

Page 17: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0017

Variables et instructions

Modification de Nom

J’appelle alors la fonction Majuscule pour mettre en majuscule la première lettre du nom. Comme ma chaîne est en fait un tableau de caractères, il me suffit de modifier le premier caractère de ce tableau. Cela donne le code suivant :

Nom[1] := Majuscule(Nom[1])

Que fait l’application ? Il y a trois étapes :

1. on recherche dans le tableau des variables la position du premier caractère de Nom. C’est évidemment la position de Nom lui-même, soit B4 ;

2. on récupère la valeur en B4 et on lui retranche 32å. On passe donc du code ASCII 102 (caractère f) au code 70 (caractère F) ;

3. on stocke le résultat (soit 70) dans la zone mémoire du premier caractère de Nom, soit B4.

Cela nous donne l’état de la mémoire suivant :

Tableau des variables État de la mémoireVARIABLE TYPE ADRESSE DÉBUT A B C D E F G H I J

Taille mot D2 1 22 2 246 235 222 109 20 170 225 55Nom chaîne [7] B4 2 24 240 41 100 201 45 230 15 145 55

3 73 213 65 214 206 56 245 41 92 964 224 70 233 118 114 105 101 114 8 211

3D6. Simplification

Maintenant que nous avons vu le fonctionnement précis de l’allocation mémoire, nous allons nous empresser de tout simplifier. En effet, à notre niveau, il est inutile de gérer le codage des données. Nous allons donc estimer que les cases D2 et E2 contiennent directement la valeur de Taille et que les cases B4 à H4 contiennent directement celle de Nom. Cela donne la représentation suivante :

Tableau des variables État de la mémoireVARIABLE TYPE ADRESSE DÉBUT A B C D E F G H I J

Taille mot D2 1 22 2 246 235 222 109 20 170 225 55Nom chaîne [7] B4 2 24 240 41 51556 45 230 15 145 55

3 73 213 65 214 206 56 245 41 92 964 224 Février 8 211

Les zones mémoire qui ne sont pas liées à notre programme ne changent pas puisque leurs valeurs n’ont pas de sens pour nous.

Du coup, supposons que j’exécute ces deux instructions divisant par deux Taille et met-tant tout Nom en majuscules :

Taille := Taille/2Nom := Majuscule(Nom)

å Car le code ASCII de chaque lettre majuscule est égal au code de sa minuscule moins 32.

Page 18: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0018

Séquence 1

Les deux instructions précédentes ont sur la mémoire l’impact suivant :

Tableau des variables État de la mémoireVARIABLE TYPE ADRESSE DÉBUT A B C D E F G H I J

Taille mot D2 1 22 2 246 235 222 109 20 170 225 55Nom chaîne [7] B4 2 24 240 41 25778 45 230 15 145 55

3 73 213 65 214 206 56 245 41 92 964 224 FÉVRIER 8 211

3D7. Fin du programme

Laissons le programme continuer tranquillement en supposant qu’il ne modifie plus ni Taille ni Nom. Que se passe-t-il lorsqu’il est fini ? Ma foi, il se termine. En d’autres termes, il s’arrête. On pourrait également dire qu’il met un terme à son exécution.

Et la mémoire dans tout cela ? Et bien, les zones mémoire occupées par les variables du programme (ici D2, E2 et B4 à H4) sont libérées. Va-t-on parcourir toutes les cases en mémoire pour les vider ? Non ! D’ailleurs, que signifie vider ? Mettre la valeur 0 ? Ce n’est pas acceptable car 0 est une valeur entière tout à fait correcte. On ne pourrait donc pas distinguer le 0 valeur entière du 0 absence de valeur. On va donc simplement vider le tableau des variables, ce qui a en outre l’avantage de la rapidité. Les octets alors libérés sont de nouveau disponibles, ce qui signifie que les valeurs qu’ils stockent ne correspondent plus à rien.

C’est pourquoi plus rien n’est encadré et tout est écrit en gris dans l’état mémoire ci-dessous :

Tableau des variables État de la mémoireVARIABLE TYPE ADRESSE DÉBUT A B C D E F G H I J

1 22 2 246 235 222 109 20 170 225 552 24 240 41 178 100 45 230 15 145 553 73 213 65 214 206 56 245 41 92 964 224 70 201 86 82 73 69 82 8 211

Observez bien le contenu des cases :

• D2 et E2. Elles contiennent respectivement 178 et 100, ce qui correspond bien à 178+100*256 soit 25 778, la dernière valeur en date de Taille ;

• B4 à H4. Elles contiennent respectivement les codes ASCII des lettres constituant le mot FÉVRIER, soit la dernière valeur en date de Nom.

Vous noterez que j’ai remis la valeur de chaque octet et que j’ai effacé l’encadrement autour des cellules. Pourquoi ? Car toutes ces zones mémoire sont maintenant indépen-dantes : les cases D2 et E2 ne stockent plus à elles deux un mot (cette variable n’existe plus) mais possèdent des valeurs arbitraires et sans signification laissées là par l’exécution d’un programme maintenant terminé. Ces valeurs peuvent être interprétées comme des caractères, des entiers… si un nouveau programme réserve la case C4 pour y stocker :

• un entier, elle y trouvera la valeur 201 laissée par le programme que l’on vient de quitter. D’où l’importance d’initialiser cette variable avant de s’en servir :

• un caractère, elle y trouvera un É laissé par le programme ;

• …

L’intérêt de ce mécanisme est évident lorsque vous pensez aux variables locales : quand vous arrivez dans un sous-programme, ses variables locales sont créées, utilisées tout au long du sous-programme puis détruites lorsque ce dernier se termine (nous le reverrons).

Page 19: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0019

Variables et instructions

3E. Pourquoi tant de détails ?Je vous ai fait croire que nous faisions des rappels et, mine de rien, je suis allé bien plus loin que le cours de 1re année. Pourquoi ? Car nous aurons besoin de ces connaissances plus tard. Cela dit, c’est tout de même visuel et intuitif.

3F. Manipulation des variables

3F1. En écriture

On va modifier la valeur de la variable. Deux techniques : soit on demande à l’utilisateur de saisir la valeur (instruction saisir), soit on réalise une affectation (instruction « := »).

Saisie

La syntaxe est :

saisir chaîne, variable

Lorsque cette instruction est exécutée, que se passe-t-il ? Quelque chose qu’il faut apprendre.

1. La chaîne de caractères chaîne est affichée à l’écran.

2. L’application attend que l’utilisateur rentre (saisisse au clavier) quelque chose.

3. La valeur saisie est affectée à la variable (valeur et variable doivent être de même typeå).

Voici deux exemples permettant de saisir deux variables. Vous noterez que chaîne est là pour avertir l’utilisateur de la nature de ce qu’il doit saisir. Il faut donc évidemment mettre quelque chose de lisible.

saisir "Entrez votre nom :", NomPersonnesaisir "Entrez votre âge :", AgePersonne

Affectation

La syntaxe est :

variable := expression

Lorsque cette instruction est exécutée, que se passe-t-il ? Quelque chose qu’il faut apprendre :

1. L’expression est évaluée (calculée).

2. Le résultat est affecté à la variable (l’ancienne valeur de la variable est perdue car remplacée par la nouvelle). L’expression et la variable doivent être de même typeç.

å Enfin, la théorie le voudrait : affecter une chaîne de caractères à un booléen n’a pas de sens. Néanmoins, les affectations cohérentes sont admises : on peut affecter un entier à un réel (les valeurs entières faisant partie des réels, tout entier peut être converti en réel), un entier court à un entier long…

ç Avec les mêmes tolérances que pour la saisie. On dispose de plus de fonctions permettant de changer le type d’une valeur (qui peut s’en trouver modifiée). La plus connue est int, convertissant par troncature un réel en un entier. Ainsi, i étant un entier, i := 3.5 est incorrect. En revanche, i := int (3.5) est valide puisque int (3.5) est égal à la valeur entière 3. On affecte donc bien un entier à une variable entière.

Page 20: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0020

Séquence 1

3F2. En lecture

On récupère la valeur de la variable pour l’afficher ou pour s’en servir. Exemple :

AnnéesAvantRetraite := 65 – AgePersonne // AgePersonne est lue

On peut afficher à l’écran (à destination de l’utilisateur) la valeur d’une variable avec l’instruction Afficher :

Afficher expression1, expression2…, expressionn

Le fonctionnement est simple.

Chaque expression est successivement évaluée puis son résultat est affiché.

Exemple :

Afficher "Vous serez en retraite dans ", 65 – ÂgePersonne, " ans."

Que fait cette instruction ? Si ÂgePersonne vaut 34, alors :

• elle évaluera "Vous serez en retraite dans" (c’est vite fait puisque c’est une cons-tante) et affichera le résultat, soit Vous serez en retraite dans ;

• elle évaluera 65 – ÂgePersonne, soit 65 – 34 soit 31 et affichera donc 31 ;

• elle évaluera "ans." (c’est vite fait puisque c’est une constante) et affichera le résul-tat, soit ans.

Au final, sur l’écran, on obtiendra :

Vous serez en retraite dans 31 ans.

Une remarque importante :

Afficher va à la ligne ou pas en fonction de ce qui nous arrange.

Ainsi, le code :

Afficher "Boujour"Afficher "à tous !"

produira au choix l’un des deux résultats suivants :

Bonjour à tous ! Bonjourà tous !

En bref, ne vous souciez pas d’un retour à la ligne quelconque. L’algorithmique a ceci de sympathique que, si j’ai réellement besoin de réaliser un algorithme produisant une sortie très contrôlée, je n’ai qu’à dire qu’Afficher ne va jamais à la ligne et je dote mon langage d’une instruction ChangeLigne qui, lorsqu’elle est exécutée, change de ligne.

Page 21: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0021

Variables et instructions

4. L’alternative

On parle d’instruction de contrôle car l’alternative ne réalise rien par elle-même : pas d’affichage, pas de modification de variable… Son objet est de déterminer en fonction d’une condition quelles instructions – qui elles, feront quelque chose – seront exécutées. L’alternative contrôle (dirige) l’exécution du programme.

Exercice 4

Allez, sans lire la suite, donnez-moi la syntaxe de l’alternative (l’instruction si) et expliquez son fonctionnement.

L’alternative a pour syntaxe générale si alors sinon, la branche sinon étant facultative (ce qui donne si alors). La syntaxe associe une expression booléenne et des suites d’instruc-tions (dans le alors et dans le sinon) :

si booléen alors instructions_oui sinon instructions_nonfin si

si booléen alors instructions_ouifin si

S’il n’y a aucune instruction dans le sinon, on ne met pas le mot sinon et on obtient le si alors.

Lorsque l’exécution arrive à l’instruction si, voici ce qui se passe :

1. Le booléen est évalué (calculé).

2. En fonction de la valeur obtenue :

2.1. si elle vaut vrai, les instructions de la branche alors sont exécutées puis l’exécu-tion passe à l’instruction suivant le si (l’éventuelle branche sinon est ignorée) ;

2.2. si elle vaut faux, les instructions de la branche sinon sont exécutées (s’il y en a) puis l’exécution passe à l’instruction suivant le si (la branche alors est ignorée).

En paraphrasant l’instruction, on peut dire que :

Si le booléen est vrai, alors on exécute les instructions du alors et sinon on exécute celles du sinon.

Page 22: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0022

Séquence 1

L’illustration suivante montre les instructions exécutées selon la valeur du booléen. Ce qui est en couleur est réalisé par l’application de façon automatique. (C’est la traduction du si.)

ALGORITHME TRACE DE L’EXÉCUTION

instr_1instr_2instr_3si booléen alors instr_o_1 instr_o_2 instr_o_3 sinon instr_n_1 instr_n_2fin siinstr_4instr_5

Cela appelle deux remarques.

1. On déduit aisément que les instructions si peuvent être imbriquées (une instruction si peut contenir un autre si). En effet, les instructions qui sont dans les branches alors et sinon peuvent être n’importe quelle instruction : affectation, saisie, autre si…

2. Il est très important d’indenter correctement (les instructions des blocs alors et sinon doi-vent être décalées vers la droite par rapport au si). Cela augmente la lisibilité du codeå pour le développeur qui identifie facilement les instructions appartenant au si.

Exercice 5

Pour s’entraîner un peu, écrivez un programme permettant de déterminer si un nombre est pair ou impair. Une petite aide : le nombre n est pair si n mod 2 vaut 0 (l’opérateur mod ren-voie le reste de la division entière).

Je vous rappelle l’existence de l’instruction selon cas. Je ne la reprends pas ici car c’est une autre forme du si. Il ne faut pas pour autant l’oublier !

Je vous disais que l’informatique n’a rien inventé. Et bien, dans le langage courant, voici exactement une instruction si :

si j’ai le bts alors je paye le champagne puis je cherche du travail sinon je redouble

å C’est un euphémisme : cela n’augmente pas la lisibilité mais rend le code lisible. Dit autrement, un code non indenté est illisible.

instr_4instr_5

instr_1instr_2instr_3

instr_o_1 instr_n_1instr_o_2 instr_n_2instr_o_3

booléenvrai

booléen faux

évaluation booléen

Page 23: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0023

Variables et instructions

Si j’inverse le test :

si je n’ai pas le bts alors je redouble sinon je paye le champagne puis je cherche du travail

Au fait, dans le dictionnaire, vous verrez que l’alternative est un système logique de deux propositions où l’une est vraie et l’autre fausse. C’est pile notre si alors sinon. Pour faire de l’informatique, il vaut mieux être bon en vocabulaire, cela aide !

5. Les boucles (répétitives)

On parle d’instructions de contrôle car les boucles ne réalisent rien par elles-mêmes : pas d’affichage, pas de modification de variable… leur objet est de faire exécuter 0, 1 ou plusieurs fois d’autres instructions (qui elles, feront quelque chose) en fonction d’une condition. Ces instructions contrôlent (dirigent) l’exécution du programme.

Les boucles permettent d’exécuter une ou plusieurs instructions un certain nombre de fois (de 0 à l’infini). À chaque exécution des instructions de la boucle, on évalue la valeur d’un booléen pour savoir si on recommence ou non.

Il y a trois instructions de boucle différentes. Certaines sont redondantes. Mais alors, pourquoi s’en préoccuper ? Car chacune a des caractéristiques propres qui la rendront plus ou moins efficace selon le contexte. C’est le même principe que le si et le selon cas : on pourrait se passer de cette dernière instruction, mais elle a été créée pour simplifier le code dans des cas précis.

Exercice 6

Donnez-moi les trois boucles (syntaxe et fonctionnement).

5A. Tant que… faireJe vous prouve que cette instruction existe déjà dans la vraie vie :

tant que la fin du cours n’a pas sonné les étudiants travaillent.

Voici la syntaxe :

tant que booléen faire instructionsfin tant que

Le fonctionnement de cette instruction est le suivant :

1. Le booléen est évalué (calculé).

2. Si sa valeur est vrai, les instructions incluses dans la boucle sont exécutées et l’on retourne à la première étape. Sinon, on passe à l’instruction suivant le tant que.

Page 24: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0024

Séquence 1

En paraphrasant l’instruction, on peut dire que :

Tant que le booléen est vrai, on exécute les instructions dans la boucle.

L’illustration suivante montre les instructions exécutées. Je rappelle que ce qui est en cou-leur est automatiquement réalisé par l’application. (C’est la traduction du tant que.)

ALGORITHME TRACE DE L’EXÉCUTION

instr_1instr_2instr_3tant que booléen faire instr_tq_1 instr_tq_2 instr_tq_3fin tant queinstr_4instr_5

Exercice 7

Vous pensez avoir compris ? Oui, bien sûr, puisque vous utilisez cette instruction depuis un an maintenant. Bien. Dites-moi alors ce que ces quatre blocs d’instructions vont afficher (en expliquant évidemment). Faites attention, il y a de nombreux pièges.

1er bloc 2e bloc

afficher "avant boucle"tant que (2 = 3) faire afficher "dans boucle" x := x/0fin tant queafficher "après boucle"

afficher "avant boucle"i := 1tant que (i > 0) faire afficher "dans boucle" i := i+1fin tant queafficher "après boucle"

3e bloc 4e bloc

afficher "avant boucle"i := 1tant que (i > 0) faire afficher "dans boucle" i := i-1fin tant queafficher "après boucle"

afficher "avant boucle"i := 5j := -1tant que (i > 0) faire afficher "dans boucle" j := j+1fin tant queafficher "après boucle"

instr_1instr_2instr_3

booléenvrai

booléen faux

instr_tq_1instr_tq_2instr_tq_3

instr_4instr_5

évaluation booléen

Page 25: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0025

Variables et instructions

5B. Répéter… jusqu’àDans la vraie vie :tu mangerasjusqu’à ce que ton assiette soit vide.

Voici la syntaxe :

répéter instructionsjusqu’à booléen

Le fonctionnement de cette instruction est le suivant :

1. Les instructions incluses dans la boucle sont exécutées.

2. Le booléen est évalué. S’il est faux, on retourne à la première étape. Sinon, on passe à l’instruction suivant le répéter.

En paraphrasant l’instruction, on peut dire que :

On répète les instructions dans la boucle jusqu’à ce que le booléen soit vrai.

L’illustration suivante montre les instructions exécutées. Encore une fois, ce qui est en cou-leur est réalisé par l’application de façon automatique. (C’est la traduction du répéter.)

ALGORITHME TRACE DE L’EXÉCUTION

instr_1instr_2répéter instr_rjq_1 instr_rjq_2 instr_rjq_3jusqu’à booléeninstr_3

Exercice 8

Vous pensez avoir compris ? Dites-moi alors ce que ces deux blocs d’instructions vont afficher (en expliquant évidemment).

1er bloc 2e bloc

répéter saisir "Entrez l’âge : ", âgejusqu’à (âge > 0) et (âge < 120)

répéter x := x/0jusqu’à x = x

instr_1instr_2

instr_rjq_1instr_rjq_2instr_rjq_3

booléenfaux

booléenvrai

instr_3

évaluation booléen

Page 26: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0026

Séquence 1

5B1. Comparons Tant que et répéter

Ce sont deux sœurs trop souvent confondues. Il faut donc apprendre ce qui suit.

Comparaisons des deux boucles TANT QUE ... FAIRE REPETER ... JUSQU'A

test du booléen…

la 1re exécution de la boucleavant après

nombre d’itérations (d’exécu-tions des instructions de la

boucle)0 à ∞ 1 à ∞

on sort de la boucle quand le booléen est…

faux vrai

Les deux boucles partagent une règle de validation que je vais vous livrer. C’est un résumé des deux exercices 7 et 8. Assimilez-la et vérifiez-la à chaque boucle que vous écrivez. Elle vous permettra d’éviter une erreur trop fréquente dans les copies que vous m’envoyez à la correction et qui me peine car j’ai l’impression que mon cours n’a pas été assimilé ou qu’il n’est pas bien rédigéå.

Reprenons les remarques des deux exercices précédents :

• une boucle dont les instructions « à l’intérieur » ne modifient pas les constituants du booléen est forcément fausse (voir blocs 1 et 4 de l’exercice 7) ;

• une boucle dont les instructions modifient les constituants du booléen peut être correcte (bloc 3 de l’exercice 7, bloc 1 de l’exercice 8) mais ce n’est pas une certitude (blocs 2 des exercices 7 et 8).

Voici donc la règle de validation qui constitue une condition nécessaire (mais pas suffi-sante) à la correction de la boucle.

Une boucle répéter ou tant que exécute les instructions qu’elle contient jusqu’à ce qu’un test soit ou non vérifié. La seule chose qui change entre deux évaluations du test, c’est que le corps de la boucle a été exécuté.

Ainsi, pour avoir une chance que la boucle s’arrête, les variables constituant le boo-léen du test doivent être modifiées par les instructions de la boucle. Si ce n’est pas le cas, le test est une constante vis-à-vis de la boucle et vaudra donc toujours vrai ou faux (ce qui est une erreur).

5C. Pour

Syntaxe

Les deux boucles précédentes réalisaient un nombre d’itérations variable. La boucle pour est utile dès que l’on connaît le nombre d’itérations à réaliser. Voici la syntaxe :

pour variable de entier1 à entier2 pas entier3

instructionsfin pour

å Dans la précédente version de ce cours, je ne présentais pas cette règle de validation, les com-mentaires des exercices 7 et 8 me semblant suffisant. Mais après avoir retrouvé cette erreur très fréquemment dans vos copies, j’ai compris que je devais la formaliser dans le cours. Et la voilà.

Page 27: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0027

Variables et instructions

Lorsque entier3 vaut 1 (c’est le cas général), on peut omettre la clause pas. Le principe général est le suivant :

Variable est une variable entière appelée l’indice de la boucle. L’idée est que l’indice va aller de entier1 à entier2 en ajoutant entier3 à chaque fois. À chaque étape (itéra-tion), le corps de la boucle sera exécuté.

La progression de l’indice

Il est facile de passer de 5 à 15 en ajoutant 1 : on passera par 5, 6, 7, 8… jusqu’à 15. Idem pour passer de 0 à –3 en ajoutant –1 (soit en retranchant 1) : on passera par 0, –1, –2 puis on atteindra –3.

D’une façon générale, si entier1 est inférieur à entier2, entier3 doit être positif. Si entier1 est supérieur à entier2, entier3 doit être négatif. Sinon, la progression de entier2 à entier1 est impossibleå.

Le fonctionnement de cette instruction est le suivant :

1. L’indice de boucle est initialisé à entier1.

2. Tout dépend des indices : 2.1 [si entier1 < entier2 et donc entier3 > 0]

Si l’indice de boucle est inférieur ou égal à entier2, alors les instructions incluses dans la boucle sont exécutées. sinon l’exécution de la boucle est terminée et on passe à l’instruction suivant le pour.

2.2 [si entier1 > entier2 et donc entier3 < 0] Si l’indice de boucle est supérieur ou égal à entier2, alors les instructions incluses dans la boucle sont exécutées. sinon l’exécution de la boucle est terminée et on passe à l’instruction suivant le pour.

3. On incrémente l’indice de boucle de entier3 et on retourne à l’étape 2.

Notez bien que la troisième étape incrémentant l’indice de entier3 fera :

• augmenter l’indice si entier3 est positif ;

• diminuer l’indice si entier3 est négatif.

L’illustration suivante montre les instructions exécutées. Dois-je rappeler que ce qui est en couleur est réalisé par l’application de façon automatique ? (C’est la traduction du pour.) Cela prouve que le pour est une instruction complexe qui travaille dans l’ombre.

Je présente le cas habituel entier1 < entier2

å J’ai un ami qui a essayé d’aller de 10 à 13 en ajoutant –1. Eh bien, il essaye toujours. (Sa sœur a voulu prendre le problème dans l’autre sens et a tenté d’aller de 13 à 10 en ajoutant 1. Et bien, elle tente toujours.) Je ne les fréquente plus, ils sont trop occupés à essayer pour l’un et à tenter pour l’autre.

Page 28: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0028

Séquence 1

ALGORITHME TRACE DE L’EXÉCUTION

instr_1instr_2pour indice de e1 à e2 pas e3

instr_p_1 instr_p_2 instr_p_3fin pourinstr_4instr_5

Exercice 9

Vous pensez avoir compris ? Dites-moi alors ce que vont afficher à l’écran ces deux blocs d’ins-tructions (en expliquant évidemment).

1er bloc 2e bloc

pour i de 1 à 10 pas 5 afficher ifin pour

pour i de 10 à 1 pas -1 pour j de 10 à 1 pas -1 afficher i, "x", j, "=", i*j fin pourfin pour

Exercice 10

Que penser de l’instruction suivante ?

pour i de 1 à 5 afficher i i := i-1 fin pour

Cet exercice nous fournit la règle de validation suivante :

Dans une boucle pour, l’initialisation puis l’incrémentation de l’indice est géré par l’application. Il est donc interdit (et d’ailleurs sans intérêt) de modifier dans le code la valeur de l’indice. Vous n’utilisez cette variable qu’en lecture.

Si vous avez un super algorithme optimisé qui vous conduit à modifier la valeur de l’in-dice, changez-le (l’algorithme, pas l’indice). N’utilisez pas une boucle pour mais un tant que ou un répéter.

L’optimisation du code n’est pas un argument magique autorisant à faire n’importe quoi.

J’aime le côté direct et sans complaisance de cette phrase. De plus, si vous respectez son message, vous éviterez beaucoup d’âneries.

instr_1instr_2

instr_p_1instr_p_2instr_p_3

instr_4instr_5

ouinon

indice := e1

indice ≤ e2 ?

indice := indice + e3

Page 29: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0029

Variables et instructions

Ainsi :

L’optimisation du code n’est pas un argument magique autorisant à faire n’importe quoi.

Exercice 11

Transformez la boucle suivante en boucle répéter puis en boucle tant que :

pour i de 1 à 10 afficher i fin pour

La leçon de cet exercice est la suivante :

Une boucle pour peut toujours être écrite sous la forme d’un tant que ou d’un répéter. Mais cela oblige le développeur à gérer lui-même l’indice. C’est moins lisible et source d’erreur.

Ainsi, à chaque fois que c’est possible, on emploiera une boucle pour à la place d’un tant que ou d’un répéter.

Notez bien qu’il n’est pas toujours possible d’employer un pour. Nous dirons que cette boucle existe pour simplifier les deux autres types de boucle dans des cas particuliers.

5D. Conclusion sur les bouclesConcernant la boucle de l’exercice 11 (quelle que soit sa version), nous dirons que :

• l’exécution de la boucle affiche les valeurs de 1 à 10 ;

• une itération de la boucle affiche la valeur courante de l’indice.

En formalisant, on obtient deux définitions.

L’exécution d’une boucle regroupe tous les traitements qui sont réalisés jusqu’à ce que l’exécution passe à l’instruction suivant la boucle.

Une itération d’une boucle, c’est une exécution des instructions incluses dans la boucle.

Ainsi, une exécution de boucle, c’est une itération, puis un test pour savoir si on con-tinue ou pas, puis une itération, puis un test, puis une itération, puis un test, puis une itération… puis un test. (On s’arrête toujours après un test qui décide que l’instruction est terminée.)

Voici une remarque basée sur la conclusion de l’exercice 10 mais s’appliquant à toutes les boucles (pour, répéter et jusqu’à).

Dans le cas de boucles imbriquées (l’une est à l’intérieur de l’autre), chaque itération de la boucle externe entraîne une exécution complète de la boucle interne.

Si vous avez compris ces deux paragraphes à apprendre, vous maîtrisez les boucles.

Les exercices qui suivent ne seront qu’une formalité.

Page 30: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0030

Séquence 1

Exercice 12

Appliquez l’algorithme suivant :

tant que vous n’avez pas compris faites une pause (de une heure à quelques jours) recommencez l’étude du paragraphe 5 fin tant que

Exercice 13

Supposons que j’aie donné cet algorithme en début de paragraphe 5 et non à la fin. Donnez alors son code pour qu’il ait exactement le même sens que celui de l’exercice 12.

Attention, c’est un vrai exercice, ce n’est pas une ânerie. Le code ne sera plus le même.

Exercice 14

Et si je vous disais que, quel que soit votre degré de compréhension, il faut lire le paragraphe 5 cinq fois pour bien s’en imprégner, quel algorithme obtiendriez-vous ?

Exercice 15

« En fait, la vie n’est pas simple. Travailler raisonnablement, c’est faire preuve de sérieux mais pas d’acharnement. Nous dirons donc que vous devez lire le paragraphe 5 au maximum 5 fois. Dès que vous l’avez compris, vous passez à la suite du cours et si, au bout de 5 lectures, ce n’est toujours pas clair, vous devez quand même passer à la suite. »

Si ce conseil vous est donné en fin de paragraphe 5, quel est l’algorithme correspondant ? [Passez du temps sur cet exercice.]

6. Application

Nous allons mettre tout ce savoir (boucles, alternative et variables) en pratique.

Exercice 16

Je vous demande d’écrire un algorithme affichant les heures et minutes de 00:00 (minuit) à 23:59. Je veux donc : 00:00, 00:01, 00:02… 23:58, 23:59.

Attention, il y a une petite difficulté pas évidente à voir !

Page 31: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0031

Synthèse

Syntaxe générale d’un algorithme

(Sans sous-programme pour le moment.)

Algo nom de l’algorithmevar déclaration des variablesdébut instructionsfin

Un commentaire est précédé par des caractères // (deux barres de division). Dit autrement, tout ce qui suit les deux barres est ignoré par le compilateur.

Le commentaire doit être clair, concis, sans ambiguïté et n’expliquer que ce qui doit l’être. Ce n’est pas une fioriture mais une plus-value à votre code.

Les variables

Quelques définitions

Type

Le type d’une variable décrit sa nature, ce qu’elle contient (entier, booléen, réel, caractère…).

Variable

C’est un espace mémoire contenant une valeur d’un type donné.

Déclaration

La déclaration d’une variable contient un nom de variable explicite (qui a un sens pour le développeur) et un type. Elle entraîne la réservation d’un espace mémoire de taille suffisante (fonction du type) pour y stocker la valeur. Le nom de la variable est associé à l’espace mémoire.

Manipulation

Saisie

saisir chaîne, variable

Fonctionnement

1. La chaîne de caractères chaîne est affichée à l’écran.

2. L’application attend que l’utilisateur rentre (saisisse au clavier) quelque chose.

3. La valeur saisie est affectée à la variable (valeur et variable doivent être de même type).

Page 32: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0032

Affectation

variable := expression

Fonctionnement

1. L’expression est évaluée (calculée).

2. Le résultat est affecté à la variable (l’ancienne valeur de la variable est per-due car remplacée par la nouvelle). L’expression et la variable doivent être de même type.

Affichage

Afficher expression1, expression2…, expressionn

Chaque expression est successivement évaluée puis son résultat est affiché.

Alternative

si booléen alors instructions_oui sinon instructions_nonfin si

si booléen alors instructions_ouifin si

Le fonctionnement de cette instruction est le suivant :

1. Le booléen est évalué (calculé).

2. En fonction de la valeur obtenue :

2.1. Si elle vaut vrai, les instructions de la branche alors sont exécutées puis l’exécution passe à l’instruction suivant le si (l’éventuelle branche sinon est ignorée).

2.2. Si elle vaut faux, les instructions de la branche sinon sont exécutées (s’il y en a) puis l’exécution passe à l’instruction suivant le si (la branche alors est ignorée).

Si le booléen est vrai, alors on exécute les instructions du alors et sinon on exécute celles du sinon.

Boucles

Tant que

tant que booléen faire instructionsfin tant que

Le fonctionnement de cette instruction est le suivant :

1. Le booléen est évalué.

2. Si sa valeur est vrai, les instructions incluses dans la boucle sont exécutées et l’on retourne à la première étape. Sinon, on passe à l’instruction suivant le tant que.

Page 33: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0033

Tant que le booléen est vrai, on exécute les instructions dans la boucle.

Répéter

répéter instructionsjusqu’à booléen

Le fonctionnement de cette instruction est le suivant :

1. Les instructions incluses dans la boucle sont exécutées.

2. Le booléen est évalué. S’il est faux, on retourne à la première étape. Sinon, on passe à l’instruction suivant le répéter.

On répète les instructions jusqu’à ce que le booléen soit vrai.

Comparaisons tant que et répéter TANT QUE ... FAIRE REPETER ... JUSQU'A

test du booléen…

la 1re exécution de la boucleavant après

nombre d’itérations (d’exécutions des instructions de la boucle)

0 à ∞ 1 à ∞

on sort de la boucle quand le booléen est…

faux vrai

Une boucle répéter ou tant que exécute les instructions qu’elle contient jusqu’à ce qu’un test soit ou non vérifié. La seule chose qui change entre deux évaluations du test, c’est que le corps de la boucle a été exécuté.

Ainsi, pour avoir une chance que la boucle s’arrête, les variables constituant le booléen du test doivent être modifiées par les instructions de la boucle. Si ce n’est pas le cas, le test est une constante vis-à-vis de la boucle et vaudra donc toujours vrai ou faux (ce qui est une erreur).

Pour

pour variable de entier1 à entier2 pas entier3

instructionsfin pour

Variable est une variable entière appelée l’indice de la boucle. L’idée est que l’in-dice va aller de entier1 à entier2 en ajoutant entier3 à chaque fois. À chaque étape (itération), le corps de la boucle sera exécuté.

Le fonctionnement de cette instruction est le suivant :

1. L’indice de boucle est initialisé à entier1.

2. Tout dépend des indices :

2.1 [si entier1 < entier2 et donc entier3 > 0]

Si l’indice de boucle est inférieur ou égal à entier2,

alors les instructions incluses dans la boucle sont exécutées

sinon l’exécution de la boucle est terminée et on passe à l’instruction suivant le pour.

Page 34: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0034

2.2 [si entier1 > entier2 et donc entier3 < 0]

Si l’indice de boucle est supérieur ou égal à entier2, alors les instructions incluses dans la boucle sont exécutées sinon l’exécution de la boucle est terminée et on passe à l’instruction

suivant le pour.

3. On incrémente l’indice de boucle de entier3 et on retourne à l’étape 2.

Dans une boucle pour, l’initialisation puis l’incrémentation de l’indice est géré par l’application. Il est donc interdit (et d’ailleurs sans intérêt) de modifier dans le code la valeur de l’indice. Vous n’utilisez cette variable qu’en lecture.

Une boucle pour peut toujours être écrite sous la forme d’un tant que ou d’un répéter. Mais cela oblige le développeur à gérer lui-même l’indice. C’est moins lisible et source d’erreur. Ainsi, à chaque fois que c’est possible, on emploiera une boucle pour à la place d’un tant que ou d’un répéter.

Quelques remarques sur les boucles

L’exécution d’une boucle regroupe tous les traitements qui sont réalisés jusqu’à ce que l’exécution passe à l’instruction suivant la boucle.

Une itération d’une boucle, c’est une exécution des instructions incluses dans la boucle.

Dans le cas de boucles imbriquées (l’une est à l’intérieur de l’autre), chaque itéra-tion de la boucle externe entraîne une exécution complète de la boucle interne.

Aphorisme

L’optimisation du code n’est pas un argument magique autorisant à faire n’im-porte quoi.

Page 35: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0035

Séquence 2

Procédures et fonctionsDurée indicative : 5 heures

Dans cette séquence, nous terminons l’étude des éléments clés de la première année.

Ñ Capacités attendues• Se souvenir du cours de l’année dernière

• Avec le recul, maîtriser ces concepts

Ñ Contenu1. Introduction ............................................................................................ 36

1A. Définition .................................................................................................... 36

1B. Définition du sous-programme ................................................................. 36

1C. Intérêt .......................................................................................................... 36

2. Les procédures et les fonctions ...................................................... 36

2A. Définitions .................................................................................................. 36

2B. Syntaxe ....................................................................................................... 37

3. Les paramètres ...................................................................................... 40

3A. Syntaxe ....................................................................................................... 40

3B. Mode de passage ....................................................................................... 41

3C. Corrigeons l’exercice .................................................................................. 42

3D. Une fonction est une procédure qui s’ignore .......................................... 46

Synthèse

Page 36: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0036

Séquence 2

1. Introduction

1A. DéfinitionLes sous-programmes ne constituent pas des notions complexes. Pourtant, les étudiants font souvent des erreurs assez grossières. Nous allons tenter d’éviter cela en reprenant quelques concepts essentiels.

Il existe deux types de sous-programmes : les procédures et les fonctions.

1B. Définition du sous-programmeUn sous-programme est un ensemble d’instructions réalisant quelque chose de précis.

Tiens, cela ressemble fort à la définition d’un programme ! Et bien oui.La différence essentielle entre un sous-programme et un programme, c’est que :

• le programme communique avec l’utilisateur qui fournit les données en entrée du programme et récupère les données en sortie ;

• le sous-programme communique avec le programme : c’est le programme qui fournit les données en entrée et qui récupère et exploite les résultats du sous-pro-gramme.

Tout langage de programmation possède de nombreux sous-programmes intégrés que vous utilisez sans vous en rendre compte. Par exemple ? Et bien afficher, saisir, sin (la fonction sinus), random (la fonction aléatoire)…

1C. IntérêtLa grande force des langages de programmation, c’est que l’utilisateur peut écrire ses propres sous-programmes. C’est utile pour plusieurs raisons :

• lorsque vous exécutez plusieurs fois la même suite d’instructions, soit vous les écri-vez plusieurs fois, soit, et c’est préférable, vous écrivez une seule fois le sous-pro-gramme correspondant et vous l’appelez autant de fois que nécessaire ;

• lorsque vous écrivez une application, il est plus simple de diviser le travail en petites tâches autonomes (sous-programmes) car vous divisez la difficulté ;

• un sous-programme est autonome vis-à-vis de l’application. Vous pouvez le copier/coller dans un autre programme si vous en avez besoin. C’est la réutilisation du code que nous reverrons avec la programmation objet (option développeur d’ap-plications).

2. Les procédures et les fonctions

2A. DéfinitionsOn utilisera une fonction lorsque le sous-programme renvoie un seul résultat. Dans le cas contraire (il renvoie aucun ou plusieurs résultats), il faut une procédure.

Quand je parle de résultat, c’est au sens large. Ce peut être un paramètre en entrée qui est modifié. Par exemple, j’envoie un tableau au sous-programme qui me le retourne trié.

De même que la boucle pour est un cas particulier de boucle tant que (tout pour peut s’écrire sous la forme d’un tant que), la fonction est un cas particulier de procédure. Nous le vérifierons dans le paragraphe 3D.

Page 37: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0037

Procédures et fonction

Mais, dès à présent, nous retiendrons :

Toute fonction peut s’écrire sous la forme d’une procédure.

La fonction est donc un concept redondant. Mais alors, pourquoi l’avoir créé ? Pour la même raison que le pour existe : c’est très pratique. Cela dit, parfois, on doit s’en passer comme l’explique le paragraphe qui suit.

En pratique (avec un langage de programmation et non plus un algorithme), on peut se trouver face à des limitations techniques : la majorité des langages n’acceptent comme résultat de fonction que des types de base (entier, booléen, chaîne, carac-tère…). Ainsi, même si d’un point de vue algorithmique il est correct de vouloir faire une fonction, vous devrez parfois la coder sous la forme d’une procédure.

La fonction est une analogie de la fonction mathématique.

2B. Syntaxe

2B1. IntroductionRésumons ce qui précède :

Un sous-programme réalise une tâche précise, il prend des valeurs en entrée et retourne aucun résultat (procédure), un seul (fonction) ou plusieurs (procédure). Un sous-programme doit être autonome (donc doit pouvoir être utilisé dans un pro-gramme par simple copier/coller). Un sous-programme est constitué d’instructions.

Pour réaliser cela, chaque sous-programme devra posséder :

• un nom permettant de l’appeler lorsque l’on souhaite s’en servir ;

• la description des données en entrée et en sortie (les paramètres) ;

• les instructions réalisant le traitement du sous-programme ;

• les variables locales au sous-programme.

Nous allons voir comment traduire cela sous la forme de procédure ou de fonction. Pour le moment, on ne détaille pas les paramètres.

2B2. La procédure

Voici la syntaxe :

procédure nom (paramètres)

var déclarations

début instruction1

... instructionn

fin

Comment utiliser la procédure dans un programme ? On l’appelle par son nom comme on le ferait avec une instruction.

Page 38: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0038

Séquence 2

2B3. La fonctionVoici la syntaxe :

fonction nom (paramètres) : type

var déclarations

début instruction1

... instructionn

Retourner (expression) // Retour du résultatfin

Rappelez-vous que la fonction renvoie toujours un résultat. C’est pourquoi :

• à la fin de l’en-tête (ligne contenant le mot-clé fonction), on indique le type du résultat ;

• il faut explicitement indiquer quel est le résultat renvoyé par la fonction. Je choisis la convention de l’instruction Retourner dont le paramètre contient le résultat. Dans ce cas, exécuter Retourner termine la fonction (il est donc sans objet de mettre des instructions derrière).

Il est classique d’oublier de renvoyer le résultat (ici, cela revient à oublier d’appeler Retourner). Dans ce cas, la fonction renvoie un résultat indéterminé (aléatoire). C’est vraiment une erreur bête mais classique (mais bête). Comme je l’ai trop souvent vue dans vos copies, je rajoute en règle de validation :

Une fonction renvoyant toujours un résultat, son code comprend toujours le type du résultat dans l’en-tête et se termine toujours par un appel à l’instruction Retourner pour renvoyer un résultat précis.

Comment utiliser une fonction ? On l’appelle par son nom mais ce n’est pas une instruc-tion. Par exemple, sin(8) est un nombre, pas une instruction. On utilisera donc une fonc-tion renvoyant une valeur d’un type donné à la place d’une expression de même type.

Je reviens sur le prêt-à-apprendre précédent. Lorsque je dis que la fonction doit se ter-miner par un appel à Retourner, cela signifie que toute exécution de la fonction, quelles que soient les instructions exécutées (cela dépend des tests) doit se terminer par un Retourner.

Pour comprendre cela, prenons l’exemple de Nom1 et Nom2 :

fonction Nom1 (paramètres) : type

début instruction1

... instructionn

Retourner (expression)fin

fonction Nom2 (paramètres) : type

début si booléen alors instruction1

sinon instruction2

fin si Retourner (expression)fin

Page 39: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0039

Procédures et fonction

Dans les deux cas, Retourner est la dernière instruction exécutée donc tout va bien : les fonctions renvoient toujours un résultat.Prenons maintenant l’exemple de Nom3 et Nom4 :

01 fonction Nom3 (paramètres) : type02 03 début04 si booléen05 alors06 instruction1

07 sinon08 instruction2

09 Retourner (expression)10 fin si11 fin

01 fonction Nom4 (paramètres) : type02 03 début04 si booléen05 alors06 si booléen207 alors08 instruction1

09 Retourner (expression)10 sinon11 Retourner (expression)12 fin si13 sinon14 instruction2

15 fin si16 fin

Les deux fonctions sont incorrectes :

• la branche alors de Nom3 ne contient pas d’instruction Retourner. Ainsi, si le test est vrai, on n’exécutera que la ligne 6 (instruction1) et c’est tout, la fonction ne retournera aucun résultat ;

• de même, dans Nom4, la branche sinon (ligne 14) du si externe (le premier, des lignes 4 à 15) ne contient pas de Retourner, donc dans ce cas, la fonction ne renvoie rien. Comme Nom4 est relativement complexe, nous allons envisager tous ses cas d’exécution.

La fonction est constituée d’une instruction si ligne 4. Si son booléen est :

• vrai, on exécute la partie alors, soit le si des lignes 6 à 12. Si le test ligne 6 est :

– vrai, on exécute la branche alors, soit les lignes 8 et 9. Cette dernière instruction est Retourner donc tout va bien ;

– faux, on exécute la branche sinon, soit la ligne 11 qui est l’instruction Retourner donc tout va bien ;

• faux, on exécute la partie sinon, soit l’instruction ligne 14 et c’est tout. Aucun Retourner n’est exécuté donc la fonction ne renvoie rien dans ce cas, c’est une erreur.

L’étude de Nom3 et Nom4 était relativement évidente car j’avais des structures vides. Dans le cas d’une vraie fonction, plus longue et contenant plus d’alternatives dépendant des paramètres, la vérification est beaucoup moins simple. C’est pourquoi je reçois régu-lièrement des copies contenant des fonctions ne renvoyant rien ; comme cela me peine, je vous demande d’apprendre ce qui suit.

Une fonction contient généralement des tests et des boucles qui peuvent entraîner une exécution complexe. On ne sait donc pas immédiatement quelles instructions seront exécutées et lesquelles seront ignorées ; cela dépendra des tests dans les alternatives et les boucles.

Page 40: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0040

Séquence 2

Comme une fonction renvoie toujours un résultat, il faut s’assurer que, quels que soient les paramètres, la dernière instruction exécutée est toujours Retourner.

2B4. Sémantique des sous-programmesJe reprends ce qui précède car c’est important.

Une procédure est une instruction, une fonction est une expression du même type que son résultat.

Ainsi :

– partout où vous mettez une instruction, vous pouvez mettre l’appel à une procédure ;

– partout où vous mettez un booléen (exemple : dans un test ou une boucle), vous pouvez mettre l’appel à une fonction renvoyant un booléen, partout où vous met-tez un entier, vous pouvez mettre l’appel à une fonction renvoyant un entier ;

– plus généralement, partout où vous mettez une expression d’un type T, vous pouvez mettre l’appel d’une fonction renvoyant une valeur de type T.

3. Les paramètres

Cela terminera l’étude des sous-programmes. Attention, c’est encore une notion très importante.

3A. SyntaxeNous avons vu que les sous-programmes possédaient des paramètres. Ce sont les don-nées qui entrent et sortent du sous-programme.

La syntaxe sera simple : on indique le nom de chaque paramètre suivi d’un « : » et de son type. On retrouve l’obligation d’associer toute donnée à un type. Les paramètres sont séparés par une virgule.

Exemples :

procédure Échange (a : entier, b : entier)début …fin

fonction Maximum (n1 : réel, n2 : réel) : réeldébut … Retourner (…)fin

Je voudrais attirer votre attention sur un point très important.

Nous avons déjà vu qu’un sous-programme formait un ensemble cohérent et indé-pendant de tout programme. Ainsi, le nom des paramètres est tout à fait arbitraire et n’a aucun lien avec les variables du programme.

Page 41: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0041

Procédures et fonction

Si l’on nomme les paramètres, c’est pour pouvoir y faire référence dans le corps du sous-programme. Le nom est juste une étiquette n’apportant aucune sémantique à l’applica-tion. (Mais c’est utile pour le développeur. Dire Paramètre1 est moins explicite que Âge.)

Pour être certain que vous en êtes conscient, je vous conseille de mettre systémati-quement des noms différents aux paramètres et aux variables.

3B. Mode de passageOn distingue deux modes de passage de paramètre.

Le passage par valeur : Même si la valeur du paramètre change dans le sous-programme, la variable corres-pondante dans le programme principal reste inchangée. Le paramètre est une varia-ble locale au sous-programme initialisée avec la valeur de la variable du programme.

Le passage par adresse : Si la valeur du paramètre change dans le sous-programme, la variable corres-pondante est modifiée dans le programme principal. Le paramètre est à la même adresse mémoire que la variable du programme ; concrètement, le paramètre est la variable.

Le passage par valeur est le mode par défaut. Pour passer un paramètre par adresse, on met le mot var devant.

Exemple :

procédure Retraite (âge : entier, var DateRetraite : entier, var MontantPension : entier)

Âge est passé par valeur, DateRetraite et MontantPension par adresse.

Vous allez faire un petit exercice pour fixer tout cela.

Page 42: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0042

Séquence 2

Exercice 17

Envisageons l’algorithme suivant :

Algorithme échange

var e1, e2 : entier

procédure échange (a : entier, b : entier)var c : entierdébut c := a a := b b := cfin

début saisir "Entrez le premier nombre ", e1 saisir "Entrez le second nombre ", e2 afficher "Vos nombres dans l’ordre : ", e1, " et ", e2 échange (e1, e2) afficher "Vos nombres permutés :", e1, " et ", e2fin

Indiquez comment sont passés les deux paramètres dans la procédure échange : par valeur ou par adresse ? Donnez et expliquez l’affichage réalisé par le programme quand on l’exécute. Modifiez le mode de passage (qui doit devenir par adresse si l’on était par valeur ou l’inverse) puis donnez à nouveau l’affichage du programme.

3C. Corrigeons l’exercicePour voir la différence entre les deux modes de passage, nous allons étudier la mémoire tout au long du déroulement du programme en reprenant le formalisme de la séquence 1, para-graphe 3D. On supposera que les entiers occupent deux octets (ce sont donc des mots).Pour alléger l’écriture, les zones mémoire dont je ne me sers pas seront vides. Je vous rap-pelle qu’en vrai, elles contiennent des valeurs aléatoire qui ne me concernent pas.Voici donc l’état de la mémoire avant création des variables.

Tableau des variables État de la mémoireVARIABLE TYPE ADRESSE DÉBUT A B C D E F G H I J

1234

J’insiste : du point de vue du programme qui s’exécute, la mémoire est vide.

Page 43: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0043

Procédures et fonction

Quel que soit le mode de passage des paramètres, le programme débute de la même façon. Les deux variables globales sont créées. Elles ne sont pas encore initialisées donc leur contenu est indéterminé pour le programme. Cela donne :

Tableau des variables État de la mémoireVARIABLE TYPE ADRESSE DÉBUT A B C D E F G H I J

e1 entier a1 1 ??? ???e2 entier f1 2

34

(Ici et dans les tableaux suivants, les changements dans l’état de la mémoire sont en gras.)

On exécute les deux saisies. L’utilisateur rentre 50 et 100. La mémoire est alors :

Tableau des variables État de la mémoireVARIABLE TYPE ADRESSE DÉBUT A B C D E F G H I J

e1 entier a1 1 50 100e2 entier f1 2

34

L’application affiche :

• Vos nombres dans l’ordre : 50 et 100

Nous appelons ensuite la procédure Échange. Et là, il y a deux cas.

3C1. Premier cas : passage par valeur

procédure échange (a : entier, b : entier)

Comme nous avons un passage par valeur, les paramètres vont être des variables nou-velles et temporaires initialisées avec la valeur des variables appelantes (soit a initialisé par e1 et b par e2).

L’état de la mémoire est alors le suivant.Tableau des variables État de la mémoire

VARIABLE TYPE ADRESSE DÉBUT A B C D E F G H I Je1 entier a1 1 50 100e2 entier f1 2a entier c3 3 50b entier g4 4 100

Il est évident que modifier a ou b n’entraîne aucune modification de e1 ou e2 puisque ce sont des variables physiquement différentes. De la même façon, lorsque vous lavez votre voiture… la mienne reste sale.

Nous arrivons dans la procédure. La variable locale c est créée.Tableau des variables État de la mémoire

VARIABLE TYPE ADRESSE DÉBUT A B C D E F G H I Je1 entier a1 1 50 100e2 entier f1 2a entier c3 3 50b entier g4 4 100c entier c5 5 ???

Page 44: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0044

Séquence 2

On réalise l’affectation c := a :Tableau des variables État de la mémoire

VARIABLE TYPE ADRESSE DÉBUT A B C D E F G H I Je1 entier a1 1 50 100e2 entier f1 2a entier c3 3 50b entier g4 4 100c entier c5 5 50

Puis a := b :Tableau des variables État de la mémoire

VARIABLE TYPE ADRESSE DÉBUT A B C D E F G H I Je1 entier a1 1 50 100e2 entier f1 2a entier c3 3 100b entier g4 4 100c entier c5 5 50

Enfin b := c :Tableau des variables État de la mémoire

VARIABLE TYPE ADRESSE DÉBUT A B C D E F G H I Je1 entier a1 1 50 100e2 entier f1 2a entier c3 3 100b entier g4 4 50c entier c5 5 50

La procédure est terminée. Que se passe-t-il alors ? Et bien, les variables temporaires (ici c) et les paramètres (ici, a et b) sont supprimés comme le montre l’état de la mémoire suivant.

Tableau des variables État de la mémoireVARIABLE TYPE ADRESSE DÉBUT A B C D E F G H I J

e1 entier a1 1 50 100e2 entier f1 2

34

On revient dans le programme principal et on affiche alors :Vos nombres permutés : 50 et 100.

Bref, les valeurs de e1 et e2 n’ont pas changé.

3C2. Second cas : passage par adresseprocédure échange (var a : entier, var b : entier)

Comme nous avons un passage par adresse, les paramètres vont être à la même adresse mémoire que les variables appelantes. On utilise alors le même espace mémoire appelé par deux noms différents.

Page 45: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0045

Procédures et fonction

L’état de la mémoire est le suivant.Tableau des variables État de la mémoire

VARIABLE TYPE ADRESSE DÉBUT A B C D E F G H I Je1 entier a1 1 50 100e2 entier f1 2a entier a1 3b entier f1 4

Il est évident que modifier a revient à modifier e1 (et modifier b, c’est modifier e2).

De la même façon, si mon frère lave la voiture de son frère, la mienne devient propre (cela fonctionne car je n’ai qu’un frère).

Nous arrivons dans la procédure. La variable locale c est créée.Tableau des variables État de la mémoire

VARIABLE TYPE ADRESSE DÉBUT A B C D E F G H I Je1 entier a1 1 50 100e2 entier f1 2a entier a1 3 ???b entier f1 4c entier c3 5

On réalise l’affectation c := a :Tableau des variables État de la mémoire

VARIABLE TYPE ADRESSE DÉBUT A B C D E F G H I Je1 entier a1 1 50 100e2 entier f1 2a entier a1 3 50b entier f1 4c entier c3 5

Puis a := b :Tableau des variables État de la mémoire

VARIABLE TYPE ADRESSE DÉBUT A B C D E F G H I Je1 entier a1 1 100 100e2 entier f1 2a entier a1 3 50b entier f1 4c entier c3 5

Puis b := c :Tableau des variables État de la mémoire

VARIABLE TYPE ADRESSE DÉBUT A B C D E F G H I Je1 entier a1 1 100 50e2 entier f1 2a entier a1 3 50b entier f1 4c entier c3 5

Page 46: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0046

Séquence 2

La procédure est terminée. Que se passe-t-il alors ? Eh bien, les variables temporaires (ici c) et les paramètres (ici, a et b) sont supprimées comme le montre l’état de la mémoire suivant.

Tableau des variables État de la mémoireVARIABLE TYPE ADRESSE DÉBUT A B C D E F G H I J

e1 entier a1 1 100 50e2 entier f1 2

On revient dans le programme principal et on affiche alors :Vos nombres permutés : 100 et 50

Bref, les valeurs de e1 et e2 ont été modifiées.

Exercice 18

Écrivez un algorithme réalisant la saisie de deux nombres et affichant le plus grand. Il devra contenir et utiliser un sous-programme renvoyant le plus grand de deux nombres. Attention aux modes de passage des paramètres.

Exercice 19

Écrivez un algorithme calculant la remise, le port et le net à payer (qui vaut montant facture – remise + port) associés à une commande, sachant que :

• la remise totale est de 5 % à partir de trois articles achetés, 10 % dès cinq et 15 % pour huit ou plus ;

• le port est de 7,7 € pour une commande de moins de 77 € (remise comprise), 4 € de 77 à 150 € et gratuit au-delà.

L’exercice est simple. Je ne vous demande donc pas de faire « juste » le programme, mais de réfléchir très sérieusement aux données nécessaires et aux sous-programmes. Je vous demande notamment de faire un sous-programme pour calculer la remise et un autre pour le port.

La conclusion de l’exercice mérite d’être apprise. D’ailleurs, nous l’avions déjà vu en fin de paragraphe 2B3. Retournez-donc le lire !

3D. Une fonction est une procédure qui s’ignoreJ’ai divisé les sous-programmes en deux clans (les procédures et les fonctions) en préci-sant que c’était une erreur grave d’employer l’un pour l’autre.

Ce discours, je l’avais déjà eu avec les boucles, tout en vous montrant qu’une boucle pour pouvait s’écrire sous la forme d’un tant que.

Eh bien, toute fonction peut s’écrire sous la forme d’une procédure. Le résultat de la fonction devient alors un paramètre passé par adresse dans la procédure.

Exercice 20

Prenons une petite fonction toute simple : Minimum, qui renvoie le minimum de deux nom-bres entiers. Écrivez cette fonction ainsi qu’une instruction faisant appel à elle.Dans un second temps, recommencez sous la forme d’une procédure Minimum.

Page 47: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0047

Procédures et fonction

Exercice 21

Généralisons : prenez la syntaxe générale d’une fonction (voir paragraphe 2B3) et transfor-mez-la en procédure.

Corrigeons cela. Voici une fonction :

fonction nom (paramètres) : typevar déclarationsdébut instruction1

... instructionn

Retourner (expression)fin

Pour la transformer en procédure, il suffit de rajouter un paramètre passé par adresse et contenant le résultat de feue la fonction. Ainsi, partout où dans la fonction on mettait Retourner (xxx), on remplacera par Param := xxx, Param étant le nouveau paramètre.

Comme dans l’exercice précédent, mon nouveau paramètre s’appellera Résultat pour simplifier.

Cela donne (en gras ce qui est important) :

procédure nom (paramètres, var Résultat : type)var déclarationsdébut instruction1

... instructionn

Résultat := expressionfin

J’insiste sur le fait que cette transformation ne signifie pas que vous deviez dorénavant vous passer de fonction. Je vous ai proposé ces exercices pour vous aider à bien assimiler les paramètres et les sous-programmes.

Mais, partout où l’on peut écrire une fonction, on écrit une fonction !

Vous pouvez, dés maintenant, faire et envoyer à la correction le devoir 1 (voir fascicule « Devoirs » réf, 3987 DG).

Page 48: Cours - Matière 3987_83987TGPA0007
Page 49: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0049

Synthèse

Sous-programme

Rôle et constitution des sous-programmes :

• un sous-programme réalise une tâche précise ;

• il prend des valeurs en entrée et retourne aucun résultat (procédure), un seul (fonction) ou plusieurs (procédure) ;

• un sous-programme doit être autonome (donc pouvoir être utilisé dans un programme par simple copier/coller) ;

• un sous-programme est constitué d’instructions.

On utilisera une fonction lorsque le sous-programme renvoie un seul résultat. Dans le cas contraire (il renvoie aucun ou plusieurs résultats), il faut une procédure.

Toute fonction peut s’écrire sous la forme d’une procédure. En pratique (avec un langage de programmation et non plus un algorithme), on peut se trouver face à des limitations techniques : la majorité des langages n’acceptent comme résultat de fonction que des types de base (entier, booléen, chaîne, caractère…). Ainsi, même si d’un point de vue algorithmique il est correct de vouloir faire une fonction, vous devrez parfois la coder sous la forme d’une procédure.

Une procédure est une instruction, une fonction est une expression du même type que son résultat. Ainsi :

• partout où vous mettez une instruction, vous pouvez mettre l’appel à une procédure ;

• partout où vous mettez un booléen (exemple : dans un test ou une boucle), vous pouvez mettre l’appel à une fonction renvoyant un booléen ;

• partout où vous mettez un entier, vous pouvez mettre l’appel à une fonction renvoyant un entier ;

• plus généralement, partout où vous mettez une expression d’un type T, vous pouvez mettre l’appel d’une fonction renvoyant une valeur de type T.

Procédure

Syntaxe de la procédure :

procédure nom (paramètres)var déclarationsdébut instruction1

... instructionn

fin

Page 50: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0050

Séquence 2

Fonction

Syntaxe de la fonction

fonction nom (paramètres) : typevar déclarationsdébut instruction1

... instructionn

Retourner : (expression)fin

Une fonction renvoyant toujours un résultat, son code :

• comprend toujours le type du résultat dans l’en-tête ;

• se termine toujours par un appel à l’instruction Retourner pour renvoyer un résultat précis (fourni en paramètre de l’instruction).

Une fonction contient généralement des tests et des boucles qui peuvent entraî-ner une exécution complexe. On ne sait donc pas immédiatement quelles instruc-tions seront exécutées et lesquelles seront ignorées ; cela dépendra des tests dans les alternatives et les boucles.

Comme une fonction renvoie toujours un résultat, il faut s’assurer que, quels que soient les paramètres, la dernière instruction exécutée est toujours Retourner.

ParamètresNous avons déjà vu qu’un sous-programme formait un ensemble cohérent et indé-pendant de tout programme. Ainsi, le nom des paramètres est tout à fait arbitraire et n’a aucun lien avec les variables du programme.

Pour être certain que vous en êtes conscient, je vous conseille de mettre systéma-tiquement des noms différents aux paramètres et aux variables.

On distingue deux types de passage de paramètre :

• l e passage par valeur : même si la valeur du paramètre change dans le sous-pro-gramme, la variable correspondante dans le programme principal reste inchan-gée. Le paramètre est une variable locale au sous-programme initialisée avec la valeur de la variable du programme ;

• l e passage par adresse : si la valeur du paramètre change dans le sous-programme, la variable correspondante est modifiée dans le programme principal. Le paramètre est à la même adresse mémoire que la variable du programme ; concrètement, le paramètre est la variable.

Le passage par valeur est le mode par défaut. Pour passer un paramètre par adresse, on met le mot var devant.

RemarqueJe ne la reprends pas ici, mais j’aimerais bien que vous reteniez la façon dont sont implantés les deux modes de passage de paramètres (nous l’avons vu dans le paragra-phe 3C). Cela vous sera utile pour votre apprentissage.

Page 51: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0051

Séquence 3

Tableaux, structures et typesDurée indicative : 5 heures

Nous allons terminer les rappels de première année en étudiant les tableaux. Nous introduirons ensuite les types de données définis par l’utilisateur, les structures et les tableaux de structures.

Ñ Capacités attenduesSavoir définir ses propres types de données

Ñ Contenu1. Introduction ............................................................................................ 52

2. Les tableaux ........................................................................................... 53

2A. Sémantique ................................................................................................. 53

2B. Syntaxe ....................................................................................................... 54

2C. Remarque sur l’indice ................................................................................ 58

2D. Tableau, élément et indice ........................................................................ 59

2E. Gestion mémoire ........................................................................................ 61

2F. Indice et contenu ........................................................................................ 65

3. Définir ses propres types .................................................................. 65

3A. Introduction ................................................................................................ 65

3B. Syntaxe de la définition ............................................................................ 67

3C. Intérêt des définitions de type ................................................................. 67

3D. Les types structurés ................................................................................... 67

3E. Quand utiliser une structure ? .................................................................. 70

4. Allons plus loin avec les structures ............................................... 70

4A. Introduction ................................................................................................ 70

4B. Structure contenant un tableau ................................................................ 70

4C. Tableau contenant une structure .............................................................. 74

4D Ce qui est à venir ....................................................................................... 75

Synthèse

Page 52: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0052

Séquence 3

1. Introduction

Dans un premier temps, nous allons réviser les notions relatives aux tableaux.

Dans un second temps, nous découvrirons des notions nouvelles. Nous apprendrons notamment à définir nos propres types de données (oui, je dis bien type !).

Définir un type est très simple, il suffit de l’écrire. L’intérêt, c’est de définir un type struc-turé (nous l’étudierons finement).

Enfin, nous mélangerons les deux notions de la séquence pour faire des tableaux de structures, des structures de tableaux, des structures de tableaux de tableaux de struc-tures de tableaux…

Cette séquence est très importante pour les deux options :

• c’est la dernière séquence pour l’option administrateur de réseaux. Les concepts abordés sont les plus importants du cours car ce sont les plus pointus. De plus, la manipulation de tableaux de structures est un sujet d’examen classique. Cela ne pose pas de difficulté si l’on maîtrise bien les notions, mais, si l’on est fragile, c’est redoutableå ;

• pour les étudiants de l’option développeur d’applications, cette séquence est une charnière entre les concepts de base et avancés (pointeurs et objets). Les types structurés sont très importants car ils apportent une partie de la syntaxe et du for-malisme utilisés en programmation objetç.

å Raison pour laquelle c’est en effet un bon thème d’examen : l’algorithme à écrire est court, mais ne se devine pas.

ç Je suis certain que les administrateurs réseau qui ont lu ce paragraphe regrettent de ne pas avoir choisi l’autre option. (D’ailleurs, pour lire un paragraphe consacré aux développeurs, il faut déjà avoir des doutes sur son orientation, non ?)

Page 53: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0053

Tableaux, structures et types

2. Les tableaux

2A. Sémantique

2A1. Qu’est-ce qu’un tableau ?

Un tableau est avant tout une variable. À ce titre, il possède un nom, un type et une valeur. Cela dit, un tableau est une variable complexe. (La notion de variable complexe est une invention didactique de ma part. Ne l’employez donc pas sans explication dans un examen.)

Attention, je n’utilise pas complexe dans le sens difficileå mais dans le sens qui contient plusieurs éléments.

Ainsi, j’oppose :

• les variables simples (classiques) qui n’ont qu’une valeur. Il s’agit des variables entiè-res, booléennes et de type caractère, chaîne…

• les variables complexes, qui sont des variables contenant plusieurs variables. Par cohérence, nous dirons qu’en tant que variable, elles ne possèdent qu’une valeur (la leur)ç, mais que cette valeur est décomposable en plusieurs valeurs. Il s’agit des tableaux et des structures.

Le tableau est une variable complexe dans la mesure où :

• un tableau d’entiers contient plusieurs variables entières ;

• un tableau de caractères contient plusieurs variables caractères…

On ne parlera donc pas de tableau (tout court), mais de tableau de quelque chose, sous-entendu de « tableau contenant des éléments d’un type donné ». Un tableau ne peut contenir que des éléments de même typeé.

Mélanger des entiers et des caractères dans un tableau est donc interdit. Pourquoi ? Cela vient de la nature du tableau que nous abordons maintenant.

2A2. Que contient un tableau ?

Déjà, un peu de vocabulaire. (Attention, chaque mot compte ici.)

Un tableau est un ensemble de variables de même type. Chacune de ces variables est un élément du tableau. Au sein d’un tableau, chaque élément est identifié par un numéro entier appelé indice.

Ainsi, au lieu de définir un tableau de dix entiers, je peux très bien définir les varia-bles entières a, b, c, d, e, f, g, h, i et j ou Nina, Pollen, Flocon, Alf, Neko, PetiteChose1, PetiteChose2, Bipsie, Zoé et Démon.

å Et je fais bien, car cette acception hélas fréquente est abusive.

ç Cela permettra d’affecter ou de comparer tableaux et structures en une seule instruction : on pourra écrire « si a=b alors… » que a et b soient des entiers, des tableaux, des chaînes, des struc-tures… La seule contrainte est toujours que, quel que soit le type de a et b, ce doit être le même pour les deux.

é Pour les développeurs : la programmation objet nous permettra de lever cette contrainte en dis-tinguant des types statiques et dynamiques.

Page 54: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0054

Séquence 3

Que m’apporte la définition d’un tableau vis-à-vis de variables indépendantes ? Deux choses :

• la simplicité de la déclaration. Chaque variable indépendante doit être explicite-ment déclarée, tandis que la déclaration d’un tableau se fait en une fois, quel que soit son nombre d’éléments ;

• l’uniformisation : les variables indépendantes sont identifiées par un nom, tandis que celles d’un tableau ne sont identifiées que par un numéro (indice) au sein du tableau. Ainsi, je parlerai de la variable PrixTTC et de l’élément numéro 4 du tableau t.

Attention à ne pas en faire trop, ces deux caractéristiques ne font pas d’un tableau la panacée à tous vos problèmes de variable. Son emploi n’est pas toujours intéressant : pour gérer des prix TTC et HT et un taux de TVA, des variables indépendantes sont pré-férables car chaque valeur possède une sémantique propreå.

Mais alors, quand un tableau est-il utile ?

On utilisera un tableau pour stocker un grand nombre de variables équivalentes et interchangeables (un ensemble de quelques choses : d’adresses, de notes, de ports réseau…). Le fait que, au sein d’un tableau, chaque élément soit identifié par un indice permet des traitements très puissants avec une simple boucle pour.

2B. Syntaxe

2B1. Déclaration

Un tableau peut avoir une dimension, deux dimensions (comme une feuille de calcul Excel), trois dimensionsç… ou n dimensions.

La déclaration est simple : on donne le nom du tableau, suivi du mot-clé tableau puis, entre crochets la taille (nombre d’éléments) de chaque dimension. On indique ensuite le type de tous les éléments précédé du mot-clé de :

var Nom : tableau[dim1, dim2, dim3,… , dimn] de type

å C’est la même problématique que sous Excel. Une feuille de calcul étant un tableau à deux dimensions, une cellule est identifiée par ses deux coordonnées (par exem-ple, C5 pour colonne C et ligne 5). Lorsqu’une cellule prend de l’importance et contient une valeur qui possède une sémantique, il faut la nommer. Ainsi, en programmation, les variables sont par défaut nommées mais on peut les définir dans un tableau. Sous Excel, c’est le contraire : elles sont par défaut dans un tableau mais on peut les nommer. Ne poussez cependant pas trop loin la comparaison. Sous Excel, on peut nommer toute cellule de la feuille ; un même espace mémoire pourra donc à la fois avoir un nom et être dans le tableau. En programmation, un espace mémoire sera associé soit à un élément d’un tableau, soit à une variable indépendante, mais pas les deux à la fois. (En vrai, si, c’est possible, mais pas à votre niveau.)

ç Si la feuille Excel est un tableau à deux dimensions, on peut considérer le classeur comme un tableau à trois dimensions, la première identifiant la feuille et les deux suivantes la cellule dans la feuille.

Page 55: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0055

Tableaux, structures et types

Exemples :

Moyennes : tableau[20] d’entiersJe dispose de vingt variables entières pour stocker les moyennes (arrondies à l’entier le plus proche) de mes vingt étudiants.

Notes : tableau[5,20] de réelsJ’ai réalisé cinq interrogations ce trimestre. Chacun de mes vingt étudiants pos-sède une note par interrogation. Ce tableau me permet de les stocker. Attention, c’est moi qui décide arbitrairement que le premier indice représente les interrogations (et variera donc de 1 à 5) et le second les élèves (de 1 à 20).

NotesBis : tableau[20,5] de réelsJ’ai vingt étudiants dans ma classe, tous ayant une note à chacun des cinq con-trôles que j’ai réalisé ce trimestre. Attention, c’est moi qui décide arbitrairement que le premier indice représente mes étudiants (et variera donc de 1 à 20) et le second les contrôles (de 1 à 5).

Concernant ces trois exemples, je vous dois une remarque importante.

Comme nous rédigeons un algorithme et pas un programme dans un langage spécifi-que, la syntaxe doit rester flexible. Toute déclaration donnant sans ambiguïté les trois caractéristiques d’un tableau (le fait que c’est un tableau, ses dimensions et le type de ses données) est correcte. Il est évident qu’une grammaire rigide dans un algorithme est un non-sens.

C’est pourquoi j’ai décidé de respecter autant que possible la langue française en me permettant :

• le pluriel pour les types (entiers et réels au lieu d’entier et réel).

• l’apostrophe (d’entiers et non de entiers).

Observez bien les deux tableaux Notes et NotesBis. Ils sont identiques car ils modélisent la même réalité. Les deux versions sont absolument équivalentes, tout traitement fait avec l’un pouvant être fait avec l’autre par une simple permutation des indices. En revan-che, une fois que l’on a choisi une de ces deux versions, il faut s’y tenir : si l’on définit Notes avec le deuxième indice représentant l’étudiant, pas question de changer d’idée en cours de route.

Souvent, trop souvent, mes étudiants me demandent ce que l’on doit mettre en ligne et en colonneå dans le tableau. C’est parce qu’ils n’ont pas compris que cela ne change rien. Tout dépend de votre perception des données.

Par exemple, sous Excel, que je parle de la cellule ligne 5, colonne 6 ou de la cellule colonne 6, ligne 5, c’est pareil ! Cela dit, pour éviter toute ambiguïté ou interrogation déraisonnableç, Excel utilise des chiffres pour identifier les lignes et des lettres pour

å La ligne étant la première dimension, la colonne la seconde… ou l’inverse ! Cela n’a toujours pas d’importance tant que l’on conserve les mêmes notations.

ç Déraisonnable car, nous venons de le voir, tant que vous restez cohérent, il n’y a pas d’erreur possible.

Page 56: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0056

Séquence 3

les colonnes. Ainsi, que vous parliez de la cellule F5 ou 5F, tout le monde comprend. D’ailleurs, si vous permutez les coordonnées, Excel est capable de corriger comme le prouve cette copie d’écran :

Exercice 22

Je veux stocker toutes les notes de ma section de BTS. Déclarez les tableaux correspondant aux différentes situations suivantes, de plus en plus précises :

• ma section contient 60 étudiants et je stocke leur moyenne annuelle ;

• en fait, j’ai 2 classes de BTS (1re et 2e année) contenant chacune 30 étudiants. Je stocke toujours leur moyenne annuelle ;

• j’ai 2 classes de BTS de 30 étudiants chacune et je stocke les vingt notes que cha-cun obtient sur toute l’année scolaire.

Vous avez compris le corrigé ? Alors on continue encore plus fort.

Exercice 23

Je veux stocker toutes les notes de ma section de BTS. Déclarez les tableaux correspondant aux différentes situations suivantes, de plus en plus précises :

• j’ai 2 classes de BTS de 30 étudiants chacune et l’année est divisée en 2 semestres. J’effectue 10 contrôles par semestre ;

• le BTS est divisé en 11 matières. Chacune attribue 10 notes par semestre à chacun des 30 étudiants de mes 2 années de BTS.

En fait, dans mon lycée, il y a deux BTS (IG et NRC). Si l’organisation du BTS NRC est identique à celle du BTS IG (mêmes nombres d’années, étudiants, semestres, matières et notes), peut-on regrouper l’ensemble des notes des BTS dans un tableau ? Si oui, donnez sa déclaration. Si non, dites pourquoi.

Page 57: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0057

Tableaux, structures et types

Exercice 24

Avez-vous bien compris ? Dans ce cas, en considérant le dernier tableau de l’exercice précé-dent, donnez les syntaxes et le principe du traitement (une ligne, n’écrivez pas d’algorithme) permettant d’obtenir :

• la troisième note d’économie (8e matière) du premier semestre pour l’étudiant 17 en seconde année de BTS IG (1er BTS) ;

• les notes d’économie au troisième contrôle du premier semestre pour tous les étudiants de seconde année de BTS IG ;

• les notes d’économie au premier semestre pour tous les étudiants de seconde année de BTS IG ;

• les notes d’économie au premier semestre pour tous les étudiants de BTS IG ;• les notes d’économie au premier semestre pour tous les étudiants, tous BTS

confondus ;• les notes d’économie, toutes classes, semestres et étudiants confondus.

La correction était claire ? Vérifions…

Exercice 25

En utilisant le tableau de l’exercice précédent, calculez la moyenne d’économie pour l’ensemble de mes BTS (toutes années, semestres et étudiants confondus). Faites un sous-programme… et jouez le jeu du sous-programme qui généralise !

2B2. Accès à un élément

Nous avons déjà dit que chaque élément d’un tableau était identifié par un indice. Nous avions alors précisé que l’indice était un nombre entierå. En pratique, comment cela fonctionne-t-il ? De la façon la plus naturelle qui soit : l’indice correspond au numéro de l’élément. Par exemple :

• si j’ai un tableau de 20 entiers, le premier sera indicé par 1, le deuxième par 2, le troisième par 3 et ainsi de suite jusqu’au vingtième qui sera indicé par 20.

• le principe sera le même avec un tableau à plusieurs dimensions. Si une dimension possède x éléments, l’indice correspondant ira de 1 à x.

Pour accéder à un élément d’un tableau, il faut faire référence à son indice. La syntaxe est simple : on écrit le nom du tableau, suivi entre crochets de l’indice.

Voici ce que cela donne avec nos trois tableaux Moyennes et Notes et NotesBis du paragraphe 2B1:

• pour accéder à la ie note de mon tableau Moyennes, j’écrirai Moyennes[i]. Mes vingt notes seront donc accessibles par Moyennes[1], Moyennes[2]… Moyennes[20] ;

• si je veux obtenir la note de l’étudiant numéro 15 à mon troisième contrôle, j’écrirai Notes[3,15] ;

• la même note (étudiant numéro 15, troisième contrôle) sera accessible par NotesBis[15,3].

å Vous ne pouvez pas l’avoir oublié car c’était juste au-dessus (paragraphe 2A2) et c’était à apprendre !

Page 58: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0058

Séquence 3

On retiendra :

Pour accéder à un élément d’un tableau, il faut préciser le tableau dont il est ques-tion puis, au sein de ce tableau, les indices de l’élément voulu.

Syntaxe : on écrit le nom du tableau, suivi entre crochets des indices séparés par des virgules. Par exemple, pour accéder au troisième élément du tableau Tab (à une dimension), on écrira Tab[3].

2C. Remarque sur l’indiceJe vous dis ici que l’indice d’un élément est son numéro. Ainsi, si j’ai 20 éléments, les indices iront de 1 à 20.

Sachez que dans certains langages de programmation, les indices commencent à 0. Dans ce cas, mes 20 éléments seraient indicés de 0 à 19. Rien ne change (c’est une bête trans-lation) mais cela complexifie le code et est source d’erreur car on a tendance à parcourir le tableau non pas des indices 0 à 19, mais de 0 à 20 ; on tente alors d’accéder à l’élément numéro 21 (d’indice 20) qui n’existe pas.

Commencer à 0 est classique en informatique car 0 est une valeur valide qui peut servir d’identifiant. C’était important jadis de ne pas « gâcher » une valeur car les espaces d’adressage n’étaient pas grands. Ce n’est plus vrai maintenant, donc nous choisissons une version plus simple.

Encore une fois, cela ne change rien. De façon théorique, rien ne m’empêche (sinon, peut-être, un peu de bon sens) d’indicer mes 20 éléments :

• par les valeurs 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52 et 53å ;

• ou, pire encore, par les valeurs 14, 73, –18, –56, –42, 27, –83, 37, –43, –73, 20, –17, 89, 54, –84, –88, –54, 5, 39 et –16.

Pourquoi ces deux versions, dont la seconde avec des valeurs à priori aléatoiresç, sont-elles correctes d’un point de vue théorique mais pas en pratique ? N’oubliez pas le dou-ble rôle de l’indice :

• pour l’application, l’indice identifie l’élément. Comme nous l’avons vu en analyse, toute valeur respectant les règles de l’identifiant (notamment son unicité) est valide ;

• pour l’utilisateur, l’indice permet d’accéder à l’élément. Plus l’indice est simple, plus le code sera facile à écrire.

Le numéro de l’élément est alors un indice parfait :

• c’est trivialement un identifiant donc l’application est contente ;

• on l’utilise très facilement avec une boucle pour parcourir le tableau donc le déve-loppeur est tout joyeux.

å Cela dit, indicer ainsi (par des valeurs consécutives commençant par un nombre particulier) peut être utile. Certains langages le permettent d’ailleurs.

ç Je les ai générées par la fonction alea.entres.bornes d’Excel. Elles sont donc bien aléatoires.

Page 59: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0059

Tableaux, structures et types

2D. Tableau, élément et indice

2D1. Élément et variable

Un tableau est constitué d’éléments d’un type donné. L’indice (tableau à une dimension) ou les indices (tableau à plusieurs dimensions) me permettent d’accéder à un élément.

Un élément donné est une variable comme une autre. Avec mes tableaux précédents :

• Moyennes[4] est un entier comme un autre. Tout ce que je peux faire avec une varia-ble entière (affectation, test, lecture, saisie, calcul…), je peux le faire avec n’importe quel élément de Moyennes ;

• Notes[5,2] est un réel comme un autre. Tout ce que je peux faire avec une variable réelle (affectation, test, lecture, saisie, calcul…), je peux le faire avec n’importe quel élément de Notes.

Finalement, qu’est-ce qui change entre une variable autonome et un élément d’un tableau ? Strictement rien, si ce n’est la syntaxe pour l’identifier :

• un élément d’un tableau est identifié par le nom du tableau puis le ou les indices ;

• une variable autonome est identifiée par son nom.

Un élément d’un tableau est strictement équivalent à une variable du même type (un élément d’un tableau d’entiers est un entier, un élément d’un tableau de chaînes est une chaîne…).

2D2. Accès à un élément

J’ai constaté dans vos copies beaucoup de confusions entre le tableau, l’indice et l’élé-ment. Je vais donc ajouter quelques explications.

Pour commencer, une remarque pas très claire mais dont l’explication suit :

Pour accéder à un élément d’un tableau, il faut fournir une valeur pour chaque indice (soit un indice par dimension).

Pour comprendre cela, étudions nos deux tableaux fétiches.

Cas du tableau Moyennes : tableau[20] d’entiersPour obtenir une note, je dois donner son indice. Le tableau ayant un seul indice (une dimension), je ne dois naturellement donner qu’un seul indice. Par exemple, Moyennes est le tableau et Moyennes[12] est une note.

Exercice 26

À quoi correspond l’écriture Moyennes[3,4] ? (Il y a deux interprétations possibles.)

Je n’ai rien à ajouter, le cas du tableau à une dimension n’est jamais complexe.

Cas du tableau Notes : tableau[5,20] de réels

Ce tableau contient 5*20 = 100 notes, soit une par contrôle (j’en ai 5) et par étudiant (j’en ai 20).

Ainsi, pour obtenir une note (soit une variable réelle), je dois identifier le contrôle et l’étu-diant. Par exemple, Notes est le tableau et Notes[1,15] est la première note de l’étudiant 15.

Page 60: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0060

Séquence 3

Exercice 27

À quoi correspond l’écriture Notes[3] ?

La conclusion de ces exercices est :

Pour accéder à un élément d’un tableau, il faut fournir une valeur pour chaque indice (soit un indice par dimension). Toute autre écriture est incorrecte.

Exercice 28

Je souhaite stocker les températures relevées à 13 h tous les jours d’une année donnée. Mon objectif est évidemment de réaliser ensuite des statistiques (moyennes pour un mois ou une saison donnée…). Définissez la ou les variables permettant de stocker toutes ces données. Attention, c’est moins simple qu’il n’y paraît. Prenez en compte l’usage que je veux faire de ces données.

Exercice 29

La solution retenue pour l’exercice précédent nous conduit à avoir des éléments qui ne doivent pas être utilisés. Par exemple, si je veux faire la moyenne des températures d’avril, que penser du code suivant ?

moyenne := 0 pour i de 1 à 31 moyenne := moyenne + temp[i,4] fin pour moyenne := moyenne/31

Proposez une façon efficace de remédier au problème. Par efficace, j’entends une solution uti-lisable pour tous les mois et pas seulement avril. Vous envisagerez deux solutions : un tableau ou une fonction.

Exercice 30

Continuons sur la correction de l’exercice précédent. Écrivez la fonction NbrJours renvoyant le nombre de jours d’un mois donné (ne tenez pas compte des années bissextiles).

Exercice 31

Nous continuons sur la correction de l’exercice 29. Définissez le tableau NbrJours stockant le nombre de jours de chaque mois puis un sous-programme initialisant le tableau (soit chaque élément).

Page 61: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0061

Tableaux, structures et types

Exercice 32

Écrivez un sous-programme calculant la température moyenne d’un mois donné. Vous utilise-rez la fonction NbrJours de l’exercice 30.

Exercice 33

Idem, cette fois en utilisant le tableau NbrJours de l’exercice 31. Attention, n’utilisez aucune variable globale !

2E. Gestion mémoireCette partie est à lire attentivement, à comprendre, mais pas à apprendre.

On est parfois tenté de mettre tout et n’importe quoi dans un tableau. Or, comme je l’ai déjà dit, un tableau ne peut contenir que des éléments de même type.

Pourquoi ? Pour l’accès direct ! Expliquons un peu cela.

2E1. Représentation en mémoire d’un tableau à plusieurs dimensions

Mise en ligne d’un tableau

Je vous rappelle ce que nous avons vu dans la séquence 1 (paragraphe 3A) : lorsque l’on déclare une variable, on réserve sa place en mémoire et on l’inscrit dans le tableau des variables.

Que se passe-t-il lors de la déclaration d’un tableau ? La nouveauté, c’est que notre tableau contient lui-même des variables. Est-ce que seule la variable tableau est stockée dans le tableau des variables, ou stocke-t-on tous ses éléments ? Placer tous les éléments dans le tableau des variables reviendrait à créer des variables indépendantes, ce qui est à l’opposé du but cherché. On ne stocke donc que la variable tableau elle-même. Mais alors, comment le programme fait-il pour accéder à un élément ?

En fait, le tableau est toujours stocké en ligne, c’est-à-dire sous la forme d’une ligne, donc d’un tableau à une dimension. Si le tableau est déjà à une dimension, cela ne change rien. S’il est à deux dimensions, on stockera toutes ses lignes les unes à la suite des autres. Le principe reste le même pour 3 dimensions ou plus.

Par exemple, supposons que je déclare un tableau t : tableau[4,3] contenant quatre lignes et trois colonneså. Je le représente ci-dessous en marquant dans chaque case le nom de l’élément correspondant :

t[1,1] t[1,2] t[1,3]

t[2,1] t[2,2] t[2,3]

t[3,1] t[3,2] t[3,3]

t[4,1] t[4,2] t[4,3]

å Pour la cinquante-sept mille trois cent quatre-vingt-douzième fois, dire que le premier indice représente les lignes et l’autre les colonnes est une convention. Prendre l’option inverse ne change-rait rien. L’important est de rester constant tout au long du programme.

Page 62: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0062

Séquence 3

Ce tableau sera en fait stocké en mémoire avec les lignes au bout les unes des autres :

t[1,1] t[1,2] t[1,3] t[2,1] t[2,2] t[2,3] t[3,1] t[3,2] t[3,3] t[4,1] t[4,2] t[4,3]

Ainsi, le compilateur ne gérera pas un tableau de quatre lignes et trois colonnes, mais un tableau d’une ligne et de 12 colonnes.

Finalement, le tableau t est à deux dimensions pour le développeur mais à une dimen-sion pour le compilateur. Voici la correspondance des différents éléments :

t[1,1] t[1,2] t[1,3] t[2,1] t[2,2] t[2,3] t[3,1] t[3,2] t[3,3] t[4,1] t[4,2] t[4,3]

t[1] t[2] t[3] t[4] t[5] t[6] t[7] t[8] t[9] t[10] t[11] t[12]

Ainsi, on voit que chaque élément de t à deux dimensions correspond à un unique élé-ment de t à une dimension et réciproquement. Plus précisément, à chaque couple d’in-dices ligne,colonne de t à deux dimensions ne correspond qu’un indice colonne dans t à une dimension. Par exemple, t[3,2], c’est t[8].

Je conserve le même nom de tableau avec les deux représentations pour insister sur le fait que nous n’avons affaire qu’à un seul tableau, manipulé selon des points de vue différents.

Maintenant, comment le passage d’un formalisme à l’autre s’effectue-t-il ? Quand vous :

• déclarez var t : tableau[4,3] d’entiers, le compilateur transformera cela en var t : tableau[12] d’entiers ;

• écrivez t[3,2] := t[1,1] + t[4,3], le compilateur le traduira en t[8] := t[1] +t[12].

La question en suspens, c’est : comment le compilateur passe-t-il d’un format de tableau à l’autre ? Stocke-t-il toutes les correspondances entre les deux systèmes d’indices ?

Passage de deux à une dimension

Reprenons : le système stocke-t-il toutes les correspondances entre les deux systèmes d’indices ? Non ! Il lui suffit de réaliser un petit calcul.

Dans le cas de notre tableau, le passage de deux à une dimension est réalisé en appli-quant la formule t[i,j] = t[(i-1)*3+j]. Par exemple :

t[3,2] = t[(3-1)*3+2] = t[2*3+2] = t[8]

D’une façon générale, si un tableau possède l lignes et c colonnes, on aura :t[i,j] = t[(i-1)*c+j]

Que prouve ce raisonnement ? Que les deux représentations sont strictement équivalen-tes. Le principe est le même quel que soit le nombre de dimensions du tableau. Je ne vous donne pas les formules, c’est sans intérêt et vite très complexe. Faites-moi confiance.

Intérêt de ce va-et-vient

Maintenant, reste à savoir à quoi sert cette gymnastique : pourquoi tout tableau est-il stocké en ligne et non comme on le déclare ? Pour des raisons d’optimisation mémoire : si l’on remplit la mémoire avec des lignes, on l’exploitera mieux car on peut les mettre bout à bout, tandis que si l’on y place des rectangles, on risque d’avoir des trous, donc de l’espace mémoire gâché. Une petite analogie pour être sûr de bien comprendre ? Eh bien, supposons que vous vouliez carreler une pièce : plus vous prendrez de petits carre-lages, moins vous aurez de découpes à faire.

En fait, ce n’est pas tant une raison d’optimisation qu’une obligation. Comme la mémoire est représentée par un tableau à deux dimensions, il est trivial d’y stocker des tableaux à deux dimensions. On se demande donc à quoi sert leur mise en ligne. Cependant, stocker

Page 63: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0063

Tableaux, structures et types

un tableau à trois dimensions dans un tableau n’en ayant que deux est moins évident. Et pour les dimensions plus élevées ? C’est impossible. De même, sur une feuille de papier (deux dimensions), dessiner des objets plats est évident, représenter une perspective pour gérer trois dimensions est moins simple et au-delà… on ne sait pas faire.

Finalement, la vraie question à poser, c’est le contraire : pourquoi le langage se fatigue-t-il à présenter au développeur des tableaux à plusieurs dimensions alors qu’en interne il ne les gère qu’avec une seule ?

Le langage gère en interne des tableaux à une dimension par obligation ; les calculs à réaliser pour accéder à une case donnée sont élémentaires pour lui. En revanche, ces cal-culs seront très complexes pour le développeur et source d’erreurs. Imaginez-vous faisant une boucle sur un indice d’un tableau à quatre dimensions : traduire cela avec le tableau linéarisé serait pénible et très peu intuitif ! C’est pourquoi cette tâche est réservée au compilateur.

La conclusion est amusante : vous manipulez des variables qui n’existent pas. Vos tableaux à plusieurs dimensions ne sont qu’un habillage destiné à vous simplifier la vieå. C’est un peu de l’ergonomie !

Les dimensions d’un tableau sont tellement illusoires que l’on peut facilement percevoir tout tableau comme étant à une seule dimension. Je vous rappelle qu’un tableau con-tient des éléments d’un type donné qui peut lui-même être un tableau. Ainsi :

• un tableau d’entiers à deux dimensions est un tableau de tableaux d’entiers à une dimension (une feuille Excel peut être vue comme un tableau de lignes ou de colon-nes) ;

• un tableau de réels à trois dimensions est un tableau de tableaux de réels à deux dimensions, soit un tableau de tableaux de tableaux de réels à une dimension ;

• …

2E2. Accès direct à un élément

Un exemple

Nous continuons à travailler sur notre tableau t : tableau[4,3] d’entiers avec quatre lignes et trois colonnes du paragraphe précédent.

J’ai toujours une question en suspens : comment accéder à l’élément t[3,2] alors que je ne connais que la position en mémoire du tableau lui-même (donc celle de la première case) ?

Déjà, la mise en ligne du tableau me simplifie la tâche puisqu’en fait, je dois accéder à t[8]. Ensuite, un tableau contient des éléments de même type. Cela signifie que chaque élément occupe le même nombre d’octets en mémoire. Et cela change tout.

Reprenons notre tableau représentant la mémoire des séquences précédentes. Pour simplifier les calculs, notez que j’inverse ma numérotation (lignes représentées par des lettres et colonnes par des chiffres).

åComme les multiples dimensions sont faites pour aider le développeur, vous seriez fort mal venu de trouver cela complexe.

Page 64: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0064

Séquence 3

Voici comment notre tableau t, déclaré t : tableau[4,3] d’entiers, va être stocké (on sup-pose que les entiers occupent deux octets en mémoire) :

Tableau des variables VARIABLE TYPE ADRESSE DÉBUT

t entier B2

Vous remarquerez que rien n’indique dans le tableau des variables que j’ai affaire à un tableau. Pour le compilateur, je n’ai qu’un entier dans les cases B2 et B3. Nous en repar-lerons.

État de la mémoire

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

A

B

C

t[1,1] t[1,3] t[2,1] t[4,3]

On sait passer de t[i,j] à t[i]. Maintenant, comment traduire une notation t[i] en adresse mémoire ? Voici quelques correspondances :

Cas général

Tableau(notation à deux dimensions)

t t[1,1] t[1,2] t[1,3] … t[4,3] t[i,j]

Tableau(notation à une dimension)

t t[1] t[2] t[3] … t[12] t[(i-1)*3+j] t[i]

Adresse mémoire du 1er octet B2 B2 B4 B6 … B24 B(2*i)

Vérifions le passage de t[i] à B(2*i) : t[5] devrait être à l’adresse mémoire B(2*5) soit B10. C’est exact !

Si l’on part de la notation à deux coordonnées, on aura :

t[i,j] = t[(i-1)*3+j] = B(2*((i-1)*3+j)) = B(6*i+2*j-6)

Vérifions : t[4,3] = t[(4-1)*3+3] = t[12] = B(6*4+2*3-6) = B24. C’est parfait !

Cas généralPrenons le cas général d’un tableau t :

• à l lignes et c colonnes ;

• contenant des éléments occupant chacun o octets en mémoire ;

• dont le premier élément est à l’adresse Ba (a représentant le nombre identifiant la colonne comme précédemment).

Dans ce cas, lorsque le programmeur veut accéder en lecture ou écriture à l’élément t[i,j], le compilateur ira à l’espace mémoire B(((i-1)*c+j-1)*o+a) et le lira avec les (o-1) suivants.

Page 65: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0065

Tableaux, structures et types

Il est totalement inutile de retenir cette formule. Par contre, elle vous prouve quelque chose qui faut apprendre :

Un tableau est un ensemble de variables (éléments) de même type. Ces éléments sont identifiés par des indices permettant au programme d’accéder directement à l’espace mémoire de l’élément requis. L’accès est donc immédiat.

Je ne vous le démontre pas, mais, faites-moi confiance, pour un tableau à trois, quatre…, n dimensions, c’est la même chose. La formule sera juste un peu plus complexe !

Petite remarque pointue et amusante

Retournez séquence 1, paragraphe 3B2. J’avais commenté les différentes interprétations possibles des deux octets piochés brutalement en mémoire en disant que l’on ne savait pas si on avait affaire à un tableau ou à des valeurs indépendantes. Les dimensions des tableaux ne sont donc pas stockées. Cela signifie que si vous écrivez t[90,78], le compi-lateur vous fera confiance en supposant que le tableau possède effectivement au moins 90 lignes et 78 colonnes. Si ce n’est pas le cas, que se passe-t-il ? Et bien, Joe le compilo va effectuer le calcul vu ci-dessus, ce qui donnera une adresse mémoire. L’application lira ou écrira à cette adresse, bien qu’il y ait une information sans rapport avec le tableau. En général, cela plante le programme ou modifie d’autres données.

En clair, l’application ne vérifie pas si vous vous cantonnez au tableau ou allez au-delà, ce qui est très dangereux.

C’est une erreur suffisamment délicate pour que certains langages proposent de vérifier les accès au tableau et préviennent si vous utilisez un indice invalide. Le problème, c’est qu’à chaque accès, la vérification est faite. L’application s’en trouve ralentie. Une bonne habitude de programmation consiste à activer cette vérification lors des tests pour débusquer ce type de bug assez fréquent, puis à la désactiver lors de l’utilisation réelle (la mise en production) pour gagner en efficacité.

2F. Indice et contenu

Attention à ne pas confondre l’élément d’un tableau avec son indice. Bref, ne con-fondez pas i et t[i].

À froid, cela vous semble évident… pourtant, dès que vous faites des exercices de tri ou de recherche (bref, dès que vous travaillez sur les indices), vous faites l’erreur. Je ne vous jette pas la pierre car c’est un piège classique. Si je vous en parle, c’est pour qu’à chaque programme écrit, vous vérifiez bien que vous n’êtes pas tombé dans le piège.

Les développeurs d’applications et moi retrouverons ce problème avec les pointeurs.

3. Définir ses propres types

3A. IntroductionVous connaissez les types classiques : entier, réel, tableau, booléen, chaîne de caractères, date…

Et bien, de même que vous pouvez créer vos propres instructions (procédures) et fonc-tions, vous pouvez créer vos propres types.

Page 66: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0066

Séquence 3

Attention, il est impossible de créer le type Teckel puis de définir une variable Nina de type Teckel. Enfin, c’est impossible si l’on pense au brave animal de chair, de truffe et de sang.

Je veux dire par là que pour fabriquer nos propres types, nous ne disposons comme briques que des types de base (de même que pour créer nos propres sous-programmes, nous n’avions accès qu’aux instructions classiques). Ainsi, pas de miracle.

Imaginez-vous face à un MCD, ébauche d’une informatisation à venir. Prenons par exem-ple l’entité Client :

date_client représente la date de la première commande du client (ce qui permet, par différence avec la date du jour, de connaître l’ancienneté du client).

L’entité Client possède six propriétés : une date, un entier (l’identifiant) et quatre chaî-nes (nom, prénom, adresse et téléphone). Lorsque vous manipulerez des occurrences de Client dans votre application, comment définirez-vous les variables correspondantes ? Prenons l’exemple concret de l’occurrence Client1.

Pour le moment, la seule solution consiste à définir une variable par propriété :

var NumClient_Client1 : entier NomClient_Client1 : chaîne PrénomClient_Client1 : chaîne AdrClient_Client1 : chaîne TélClient_Client1 : chaîne DateClient_Client1 : date

Maintenant, comment faire pour utiliser l’occurrence dans un sous-programme ? Il faut passer chacune de ces variables en paramètre. C’est un peu fastidieux !

Imaginez maintenant que j’ai aussi besoin des occurrences Client2, Client3, Client4… Il me faudrait définir six variables par client. Tout cela n’est pas bien raisonnable.

Je souhaiterais pouvoir définir un type Client, contenant toutes les propriétés de l’entité. Je pourrais alors définir mes occurrences ainsi :

var Client1, Client2, Client3, Client4 : Client

Mieux encore, si j’ai 1 000 clients, je pourrais alors les stocker dans un tableau :

var TabClients : tableau[1000] de Client

Il s’agit de transposer les concepts d’analyse entité et occurrence en concepts de pro-grammation type et variable.

CLIENT

num_clientnom_clientprénom_clientadr_clienttél_clientdate_client

Page 67: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0067

Tableaux, structures et types

3B. Syntaxe de la définitionComme toujours, tout ce qui est défini (variable, procédure, fonction mais aussi type) doit posséder un nom.

Pour définir un type, on utilisera le mot type suivi de son nom, du caractère « = » et de sa description.

type nom = description

Une fois le type défini, on peut l’utiliser comme n’importe quel type de base (de même qu’une fois un sous-programme défini, on peut s’en servir comme s’il avait toujours existé).

Notez bien que l’on déclare un type avec un « = » et une variable avec un « : ». Est-ce une coquetterie ? Non, il y a une raison sémantique : la définition du type est une équi-valence puisque le nouveau type est équivalent (égal) à sa description. Au contraire, une variable est d’un type donné mais n’est pas ce type.

D’un point de vue algorithmique, il est intéressant de faire la distinction pour bien mon-trer notre compréhension des concepts.

3C. Intérêt des définitions de typeVoyons un exemple de déclaration de type :

type Teckel = entier

Une fois mon type Teckel défini, je peux m’en servir comme tout autre type déjà existant. Je peux notamment définir des variables de ce type :

var i,j : Teckel

Maintenant, analysons ce que nous venons de faire. Teckel est défini comme étant stric-tement équivalent à un entier. Ainsi, mes variables i et j, étant de type Teckel, sont de type entier. En clair, en déclarant deux teckels i et j, j’ai en fait déclaré deux entiers.

Où veux-je en venir ? Et bien, j’ai redéfini le type entier en créant un synonyme. Quel est l’intérêt de cela ? Strictement aucun, d’autant que le terme Teckel n’est pas très explicite.

Définir ses propres types n’est intéressant que s’ils sont nouveaux. C’est ce que nous allons voir dans le paragraphe suivant où nous allons déclarer le type Client du paragraphe 3A.

3D. Les types structurésUn type structuré va permettre de définir des variables contenant plusieurs autres varia-bles. Vous allez me dire que ce n’est pas nouveau puisque les tableaux étaient déjà des variables en contenant d’autres.

Vous avez raison. Cependant, il y a une énorme différence : un tableau ne peut contenir que des variables de même type, tandis qu’une variable structurée peut contenir des variables de type quelconque.

Page 68: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0068

Séquence 3

La syntaxe de définition du type est simple : entre les mots clé structure et fin structure, on énumère les différentes variables (et leur type) constituant la structure :

type nom = structure variable1 : type1

variable2 : type2

... variablen : typen

fin structure

Les variables constituant le type structuré sont appelées des champs. Toute varia-ble d’un type structuré possède tous les champs définis dans le type. L’ordre des champs dans la structure n’a aucune importance.

Exercice 34

Essayez d’appliquer cela en définissant le type structuré Client correspondant à l’entité du paragraphe 3A.

Nous pouvons maintenant déclarer un client de type Client :

var Cl : Client

Maintenant, comment faire pour indiquer que notre client Cl s’appelle Nina ? Il faut définir son champ NomClient.

Exercice 35

À votre avis, comment faire ?

La première idée qui vient est d’écrire :

NomClient := "Nina"

Hélas, cette solution est incorrecte : le compilateur vous dira que la variable NomClient n’existe pas. Et le compilateur aura raison ! En effet, nous n’avons pas déclaré de variable NomClient (ou autre), mais une variable Cl contenant un champ NomClient.

Bref, il faut passer par Cl pour accéder à NomClient. C’est assez logique finalement : si nous avions cinq variables de type Client, utiliser directement NomClient serait ambigu puisque l’on n’aurait aucun moyen de savoir à laquelle de ces cinq variables on fait allusion.

C’est exactement le même raisonnement que pour accéder à un élément d’un tableau : on ne dit jamais « je veux l’élément i » mais « je veux l’élément i du tableau t ».

Page 69: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0069

Tableaux, structures et types

Résumons-nous : comment accéder au champ c de la variable structurée s ? Nous avons besoin d’une nouvelle syntaxe (enfin, nouvelle… vous la connaissez déjàå). C’est l’opéra-teur « . », parfois appelé opérateur de qualification car il qualifie (identifie) le champ.

La syntaxe sera s.c, soit :

variable.champ

Pour accéder au champ NomClient de la variable Cl, j’écrirai Cl.NomClient.

Vu la déclaration du type Client, la variable Cl.NomClient est une chaîne de caractères comme une autre. Ainsi, partout où j’utilise une chaîne, je peux utiliser Cl.NomClientç.

Voici un petit résumé de tout cela.

Quand une variable en contient d’autres (tableau t, structure s), il faut toujours pas-ser par la variable contenant pour accéder à une des variables contenues :

• pour un tableau t, on met l’indice entre crochets : t[i] identifie l’élément i du tableau t ;

• pour une structure s, on utilise l’opérateur « . » pour séparer la variable et ses champs : s.champ identifie le champ champ de la variable structurée s.

Avec un tableau Tab, Tab[i] identifie une variable de même type que les éléments du tableau. Avec une structure Str contenant un champ Ch, la variable Str.Ch est de même type que le champ Ch.

Ainsi, si nous avons :

type Bidon = structure Ch : chaîne ... fin structure

var a : chaîne b : tableau[50] de chaînes c : Bidon

Les trois variables a, b[8] et c.Ch sont absolument équivalentes : ce sont des chaînes de caractères. La seule chose qui les distingue, c’est la façon dont on les nomme : pour une variable simple, le nom suffit mais pour une variable incluse dans une autre, il faut pas-ser par la variable contenant (tableau ou structure) puis spécifier ce que l’on veut (un élément ou un champ).

å Dans le cours SQL de première année, nous avons vu cet opérateur pour préciser la table d’origine d’un champ dans une jointure.

ç C’est le même principe que pour les éléments d’un tableau qui sont – je l’ai abondamment dit – utilisables partout où j’ai des expressions de même type.

Page 70: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0070

Séquence 3

Exercice 36

Écrivez un sous-programme permettant d’initialiser une variable de type Client (les valeurs doivent être demandées à l’utilisateur). Vous ferez deux versions : avec une fonction puis une procédure.

3E. Quand utiliser une structure ?En tant qu’informaticien de gestion, vous ne devez pas avoir de difficulté à cerner l’inté-rêt des structures (classiquement, les entités seront codées sous la forme de structures).

Voici néanmoins la règle générale :

Lorsque vous voulez définir une chose décrite par plusieurs valeurs, il faut définir un type structuré. Dit autrement, un concept doit être représenté par une seule variable. Si une variable simple ne suffit pas, on définit un type structuré contenant toutes les informations nécessaires.

Attention, pour déclarer une variable structurée, il faut avoir préalablement défini le type correspondant.

4. Allons plus loin avec les structures

4A. IntroductionUne fois le type structuré défini, on peut déclarer une variable structurée qui est alors une variable comme n’importe quelle autre. Elle peut donc faire partie d’une autre structure.

De même, un tableau est une variable comme une autre. Il peut donc également faire partie d’une structure.

Dans le même thème, un type structuré est un type comme un autre. On peut donc défi-nir un tableau contenant des variables structurées.

Nous allons mettre tout cela en œuvre.

4B. Structure contenant un tableau

4B1. Posons le problème

C’est hyper-classique : un tableau possède un nombre d’éléments fixé lors de l’écriture du programme. Impossible de rajouter des éléments en cours de routeå. Depuis que nous savons comment fonctionne l’allocation mémoire lors de la déclaration des varia-bles, nous pouvons comprendre pourquoi rajouter des éléments après coup n’est pas possible : les cases mémoire situées après le tableau sont sans doute déjà occupées par d’autres variables.

å Les langages de programmation modernes proposent des tableaux de taille variable, mais c’est hors sujet ici.

Page 71: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0071

Tableaux, structures et types

Reprenons le cours de notre raisonnement : on ne peut pas rajouter d’éléments à un tableau. Sa définition dans le programme est donc gravée dans le marbre. Pour être certain qu’il ne sera jamais trop petit, il faut le prévoir trop grand. Eh oui !

Je m’explique : il vaut mieux avoir trop d’éléments (certains ne seront simplement pas remplis) que pas assez car alors toutes les données ne pourront pas être stockées. Ce n’est absolument pas neuf :

• la mémoire de votre ordinateur se compte en gigaoctets. Vous n’utilisez sans doute pas toute la mémoire constamment. Mais il vaut mieux en avoir beaucoup au cas où ;

• quelle est la capacité de votre disque dur ? Sans doute plusieurs centaines de gigaoctets. Le jour où vous utiliserez toute sa capacité (en pratique même un peu avant), vous le trouverez trop petit ;

• une feuille de calcul Excel contient 65 536 lignes et 256 colonnes soit 16 777 216 cel-lules. J’insiste, il y près de 17 millions de cellules par feuille. Ainsi, si vous aviez peur d’en manquer, vous quitterez ce cours rasséréné. Vous n’aurez sans doute jamais besoin d’autant de cellules, mais bon, il vaut mieux en avoir mille fois trop et ne pas s’en servir que de manquer d’une seule.

Prenons un exemple concret : pour effectuer des statistiques, je souhaite stocker l’âge des élèves des différentes classes d’un lycée. Je définirai donc un tableau par classe. Maintenant, combien d’éléments par tableau ? Le proviseur m’indique que les classes ont de vingt à quarante élèves, quarante étant une limite fixée par le ministère (donc infranchissable).

Je vais donc déclarer des tableaux de quarante éléments. Ainsi, je suis certain de pouvoir stocker n’importe quelle classe dans un tableau. Mais que se passera-t-il dans le tableau si la classe possède moins de quarante élèves ? Comment gérer les éléments du tableau dont je ne me sers pas ? Il ne faut pas que j’utilise les quarante éléments du tableau pour calculer une statistique si seulement vingt sont valides.

Pour résumer, j’ai un tableau de quarante éléments dont seulement certains sont utili-sés donc possèdent une valeur devant être prise en compte. Comment gérer cela ? Très simplement : pour chaque tableau, je définirai une variable entière stockant le nombre d’éléments utilisés.

Comprenez bien le principe ! Si je n’ai besoin que de vingt éléments dans un tableau de quarante, pas question d’utiliser un élément sur deux. On utilisera les vingt premiers, les vingt suivants restant inoccupés. Avec notre mécanisme, un tableau est donc toujours rempli de la gauche vers la droite, la variable associée indiquant le nombre d’éléments réellement utilisés, soit l’indice du dernier élément utilisé.

Ainsi, chaque tableau sera accompagné d’une variable entière indiquant le nombre d’éléments utilisés (soit l’indice du dernier élément utilisé).

Bien entendu, à chaque ajout ou suppression d’éléments dans le tableau, il faut mettre à jour sa variable associée. Chaque boucle (parcours de tableau) ne fera pas référence au nombre maximal d’éléments (taille du tableau), mais à notre variable indiquant le nombre d’éléments réellement utilisés.

Page 72: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0072

Séquence 3

Exercice 37

Définissez deux variables : un tableau de quarante éléments et sa variable indiquant le nombre d’éléments réellement utilisés. Ce tableau servira à stocker les âges de mes étudiants.Écrivez ensuite un sous-programme remplissant trente-cinq éléments de ce tableau (j’ai une classe de trente-cinq élèves). Vous mettrez des valeurs aléatoires entre 18 et 23. Pour cela, uti-lisez la fonction aléatoire(i) renvoyant un nombre entier entre 0 et i-1. À vous de trouver une formule permettant d’obtenir des valeurs entre 18 et 23. (C’est une réponse à ceux qui se pose la question de l’intérêt des mathématiques en BTS informatique.)

Exercice 38

Écrivez maintenant un sous-programme calculant l’âge moyen des étudiants du tableau. Attention, comme un sous-programme est indépendant, vous devez utiliser votre variable stockant le nombre d’éléments du tableau. Plus question de considérer que le tableau con-tient trente-cinq éléments : un autre peut n’en contenir que vingt-sept.

Le moment est venu de poser le problème : dans les deux exercices précédents, nous avons dû systématiquement traîner deux variables : le tableau et la variable associée indiquant le nombre d’éléments de ce tableau.

Les deux variables sont indissociables. J’insiste : elles forment un tout, un concept cohé-rent. Cela ne vous dit rien ? Relisez le paragraphe 3E. Nous allons définir un type struc-turé contenant deux variables : le tableau et le nombre d’éléments de ce tableau.

Exercice 39

Définissez le type puis une variable de ce type. Quelle est la syntaxe pour accéder à un élé-ment du tableau ?

Reprenons la correction :

type TableauÂge = structure Élément : tableau[40] d’entiers NbrÉléments : entier fin structure

var Bts1 : TableauÂge

Page 73: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0073

Tableaux, structures et types

Exercice 40

Question finaude : je vous demande d’accéder à un élément du tableau Élément. Mais ce tableau, il appartient à qui ? Au type TableauÂge ? À Bts1 ? À NbrÉléments ? À une autre variable ? À un autre type ? Hein ?

La question en suspens est : comment accéder à un élément du tableau de Bts1 ? Supposons que je veuille l’élément à l’indice 12. Comment y accéder ? Mes étudiants de BTS ont souvent des difficultés pour répondre. S’ils n’arrivent pas à accéder à un élément, c’est parce qu’ils apprennent mal le courså. En effet, en l’appliquant rigoureusement, il n’y a aucune difficulté. Nous allons progresser par étapes.

1. Pour accéder à un champ champ d’une variable structurée VarStruct, on utilise la syn-taxe VarStruct.champ. Ici, on veut le champ Élément de la variable Bts1, ce qui donne Bts1.Élément (le fait que Élément soit un tableau ne change rien).

2. Comme nous l’avons vu ci-dessus (paragraphe 3D), la syntaxe VarStruct.champ donne accès à une variable de même type que le champ champ. Ici, Bts1.Élément est donc une variable du même type que Élément. En clair, Bts1.Élément est un tableau avec un nom un peu long, mais c’est un tableau quand même.

3. Pour accéder à l’élément situé à l’indice indice d’un tableau tableau, on utilise la syn-taxe tableau[indice].

4. Résumons : Bts1.Élément est un tableau ; pour avoir le ie élément d’un tableau Tab, on écrit Tab[i]. En mixant ces deux écritures, on obtient que le ie élément du tableau Bts1.Élément sera accessible par l’écriture Bts1.Élément[i].

5. Conclusion : pour avoir le 12e élément, cela donne l’écriture Bts1.Élément[12] que vous devez lire ainsi : je veux le douzième élément du tableau Élément de la structure Bts1. (Ou, ce qui revient au même : « dans la structure Bts1, j’accède au champ Élément et je prends son douzième élément. »)

Exercice 41

Réécrivez les deux sous-programmes des exercices 37 et 38 en utilisant la structure Bts1.

Voici la leçon à retenir :

À chaque fois que vous êtes confronté à un tableau ayant un nombre variable d’élé-ments (c’est le cas le plus fréquent), vous devez utiliser une structure (variable struc-turée) contenant deux champs :

• le tableau ;

• une variable entière stockant le nombre d’éléments réellement utilisés. Cette variable doit être mise à jour à chaque évolution du contenu du tableau.

La définition d’un type structuré est alors indispensable pour pouvoir déclarer la structure.

å Apophtegme du bon professeur Février : « On peut apprendre sans comprendre mais on ne peut pas comprendre sans apprendre. » Il veut sans doute dire par là que si les bases ne sont pas appri-ses, les concepts suivants ne seront pas compris. (Et si c’est ce qu’il veut dire, il a raison.)

Page 74: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0074

Séquence 3

4C. Tableau contenant une structure

Un tableau contient des éléments d’un type quelconque. Or, un type structuré est un type. On peut donc sans problème définir un tableau contenant des éléments qui sont des variables structurées.

Ce genre de choses est d’un usage courant. Par exemple, nous pouvons stocker dans un tableau l’ensemble des clients de l’entreprise, les informations décrivant un client étant stockées dans une structure (je reprends celle de l’exercice 34) :

type Client = structure NumClient : entier NomClient : chaîne PrénomClient : chaîne AdrClient : chaîne TélClient : chaîne DateClient : date fin structure

Le tableau est alors :

var TabCl : tableau[50] de Client

Pour accéder au nom du 8e client, la syntaxe sera : TabCl[8].NomClient, à savoir que l’on accède au 8e élément du tableau (c’est un client) et, dans cet élément, on accède au champ NomClient.

Attention, distinguez bien l’indice de tableau et le champ NumClient. Ce sont deux infor-mations différentes.

Exercice 42

Ne perdons pas nos bonnes habitudes : les clients, cela va, cela vient. Autant dire que je peux parfois en avoir 120, d’autres fois 470. On supposera que je n’aurai jamais plus de mille clients. Définissez une structure contenant le tableau de clients et le nombre de clients qu’il contient comme nous l’avons vu dans le paragraphe 4B.

Exercice 43

Écrivez un sous-programme affichant le nom de chaque client (les clients sont stockés dans une variable structurée du type défini dans l’exercice précédent).

Page 75: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0075

Tableaux, structures et types

4D Ce qui est à venirLe cours est terminé : nous avons comme convenu abordé les tableaux de structures et les structures contenant un tableau.

Nous allons maintenant systématiser cela par deux TD. Je veux vous entraîner à manipu-ler ce type de variables (notamment les tableaux de structures) dans tous les sens : tri, recherche d’éléments, parcours… Vous pourrez ensuite vous jeter dans les devoirs.

Page 76: Cours - Matière 3987_83987TGPA0007
Page 77: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0077

Synthèse

Les tableaux

On ne parlera pas de tableau (tout court), mais de tableau de quelque chose, sous-entendu de « tableau contenant des éléments d’un type donné ». Un tableau ne peut contenir que des éléments de même type. Mélanger des entiers et des caractères dans un tableau est donc interdit.

Un tableau est un ensemble de variables de même type. Chacune de ces variables est un élément du tableau. Au sein d’un tableau, chaque élément est identifié par un numéro appelé indice.

On utilisera un tableau pour stocker un grand nombre de variables équivalentes et interchangeables (un ensemble de quelque chose : d’adresses, de notes, de ports réseau…). Le fait que, au sein d’un tableau, chaque élément soit identi-fié par un indice permet des traitements très puissants avec une simple boucle pour.

Syntaxe de déclaration :

var Nom : tableau[dim1, dim2, dim3,… , dimn] de type

Pour accéder à un élément d’un tableau, il faut préciser le tableau dont il est question puis, au sein de ce tableau, les indices de l’élément voulu. La syntaxe est simple : on écrit le nom du tableau, suivi entre crochets des indices séparés par des virgules. Par exemple, pour accéder au troisième élément du tableau Tab (à une dimension), on écrira Tab[3].

Pour accéder à un élément d’un tableau, il faut fournir une valeur pour chaque indice (soit un indice par dimension). Toute autre écriture est incorrecte.

Un élément d’un tableau est strictement équivalent à une variable du même type (un élément d’un tableau d’entiers est un entier, un élément d’un tableau de chaînes est une chaîne…). L’accès à la valeur d’un élément quelconque du tableau est immédiat puisque le compilateur n’a qu’un calcul simple à réaliser pour déterminer l’emplacement de cet élément en mémoire par rapport à la position du tableau.

Pour accéder à un élément d’un tableau, il faut fournir une valeur pour chaque indice (soit un indice par dimension).

Un tableau est un ensemble de variables (éléments) de même type. Ces éléments sont identifiés par des indices permettant au programme d’accéder directement à l’espace mémoire de l’élément requis. Attention à ne pas confondre l’élément d’un tableau avec son indice. Bref, ne confondez pas i et t[i].

Page 78: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0078

Types

Syntaxe de déclaration

type nom = description

Une fois le type défini, on peut l’utiliser comme n’importe quel type de base (de même qu’une fois un sous-programme défini, on peut s’en servir comme s’il avait toujours existé).

Types structurés

Syntaxe de déclaration

type nom = structure variable1 : type1

variable2 : type2

... variablen : typen

fin structure

Les variables constituant le type sont appelées des champs. Toute variable d’un type structuré possède tous les champs définis dans le type. L’ordre des champs dans la structure n’a aucune importance.

Lorsque vous voulez définir une chose décrite par plusieurs valeurs, il faut définir un type structuré. Dit autrement, un concept doit être représenté par une varia-ble. Si une variable simple ne suffit pas, on définit un type structuré contenant toutes les informations nécessaires.

Attention, pour déclarer une variable structurée, il faut avoir préalablement défini le type correspondant.

Accès aux valeurs des variables composées

Quand une variable en contient d’autres (tableau t, structure s), il faut toujours passer par la variable contenant pour accéder à une des variables contenues :

• pour un tableau t, on met l’indice entre crochets : t[i] identifie l’élément i du tableau t ;

• pour une structure s, on utilise l’opérateur « . » pour séparer la variable et ses champs : s.champ identifie le champ champ de la variable structurée s.

Avec un tableau Tab, Tab[i] identifie une variable de même type que les éléments du tableau. Avec une structure Str contenant un champ Ch, la variable Str.Ch est de même type que le champ Ch.

Page 79: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0079

Tableaux définis dans une structure

Chaque tableau sera accompagné d’une variable entière indiquant le nombre d’éléments qu’il contient (soit l’indice du dernier élément utilisé).

À chaque fois que vous êtes confronté à un tableau ayant un nombre variable d’éléments (c’est le cas le plus fréquent), vous devez utiliser une structure (varia-ble structurée) contenant deux champs :

• le tableau ;

• une variable entière stockant le nombre d’éléments réellement utilisés. Cette variable doit être mise à jour à chaque évolution du contenu du tableau.

La définition d’un type structuré est alors indispensable pour pouvoir déclarer la structure.

Les TD qui suivent proposent des éléments de cours : les algorithmes classiques de parcours des tableaux. Si vous les maîtrisez, vous serez au point pour l’étude de cas.

Vous pouvez maintenant réaliser le TD 1.

Page 80: Cours - Matière 3987_83987TGPA0007
Page 81: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0081

Travaux dirigés 1 : un seul tableau

Durée indicative : 4 heures

L’objet des exercices qui suivent est de mettre en œuvre de façon systémati-que les concepts vus dans le cours pour que cela devienne un automatisme. Vous allez donc manipuler tableaux et structures dans tous les sens ! Ces exercices vous permettront également d’étudier les algorithmes classiques de manipulation des tableaux. Plus que de simples exercices d’application, ils sont plutôt un complément de cours. Ne les bradez pas !

Enfin, faites-les dans l’ordre, la difficulté étant croissante.

L’année dernière, vous avez étudié les parcours de tableaux et les tris.Nous allons revoir cela en manipulant un tableau de structures. Comme l’objet de ces exer-cices est de vous rendre à l’aise avec les structures, nous allons reprendre celles de l’exercice 42, à savoir :

• une structure Client ;

• une structure TabClient contenant un tableau et un entier stockant le nombre d’élé-ments dans le tableau.

Je vous rappelle les deux types (je renomme le champ NbrÉléments de TabClient en NbrClients car j’en ai assez de taper le « É ») :

type Client = structure NumClient : entier NomClient : chaîne PrénomClient : chaîne AdrClient : chaîne TélClient : chaîne DateClient : date fin structure

TabClient = structure Élément : tableau[1000] de Client NbrClients : entier fin structure

Le champ NumClient est l’identifiant du client au sens analyse (Merise) du terme. Ses diffé-rentes valeurs sont donc uniques.

Dans tous les exercices, vous devez écrire les sous-programmes les plus généraux possibles. Pour ne pas alourdir les algorithmes, ne prenez néanmoins pas en compte le cas du tableau vide. On supposera que ce sont les programmes appelant qui testeront ce cas.

le corrigé du TD figure dans le fascicule « Autocorrection ».Étudiez-le après chaque exercice.

Page 82: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0082

Exercice 1

Parcours complet d’un tableau

Nous supposons ici que le champ AdrClient contient la ville du client. Écrivez un sous-pro-gramme comptant le nombre de clients habitant la ville d’Aubusson. Modifiez ensuite ce sous-programme pour qu’il retourne le nombre de clients habitant une ville donnée.

Exercice 2

Recherche d’un élément 1/2 (parcours du tableau sous condition)

Écrivez un sous-programme renvoyant le nom d’un client (NomClient) d’un numéro donné. Attention, le numéro de client est stocké dans le champ NumClient, ce n’est pas l’indice!

Vous écrirez deux sous-programmes, un pour chacune des deux hypothèses suivantes :

1. on suppose que le client existe forcément ;

2. on suppose que le client demandé peut ne pas exister.

Si je ne vous avais rien précisé sur l’existence du client, laquelle des deux hypothèses auriez-vous dû prendre?

Exercice 3

Recherche d’un élément 2/2 (parcours du tableau)

Dans l’exercice précédent, vous cherchiez un client précis qui pouvait ou non exister. Je vous demande maintenant d’écrire un sous-programme retournant le client le plus ancien. Vous supposerez que l’on peut comparer les dates avec les opérateurs habituels (<, >…).

Si nous gérons un champ NbrClients indiquant combien de clients sont actuellement stockés dans le tableau, c’est parce que ce nombre peut varier. Nous allons maintenant travailler dans ce cadre, en ajoutant ou en supprimant des éléments du tableau.

Exercice 4

Ajout d’un élément dans un tableau

Écrivez un sous-programme permettant d’ajouter un client au tableau. Faites particulière-ment attention aux paramètres et au choix de l’élément dans lequel ajouter le client.

Exercice 5

Supprimer le dernier élément d’un tableau

Écrivez un sous-programme permettant de supprimer le dernier client de notre tableau.

Page 83: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0083

Exercice 6

Supprimer un élément précis d’un tableau

Écrivez un sous-programme permettant de supprimer un client ayant un numéro NumClient donné. Pour faire cela, vous devrez écrire deux sous-programmes :

• un sous-programme renvoyant l’indice d’un client ayant un numéro donné. Cela per-mettra de trouver quel élément doit être supprimé. Vous supposerez que le client peut ne pas exister ;

• un sous-programme supprimant un client ayant un numéro donné (voir ci-dessous). Ce sous-programme utilisera le précédent.

Voici une petite aide pour se souvenir comment supprimer un élément d’un tableau. Il faut éviter les trous : avant la suppression, on a NbrClients éléments d’indices 1 à NbrClients. Après la suppression, on en aura NbrClients-1, de 1 à NbrClients-1.Ainsi, tous les éléments à droite de l’élément supprimé sont décalés d’un cran vers le début du tableau. Une petite métaphore ? Imaginez une file d’attente. Une des personnes faisant la queue en a assez et s’en va. Que se passe-t-il ? Pour les personnes qui sont devant celle qui est partie, rien. Pour les autres, c’est Noël : ils gagnent une place.Faisons la manipulation sur un tableau d’entiers contenant 6 éléments :

8 5 4 79 6 1

Si je veux supprimer le deuxième, je vais décaler le 3e pour le mettre dans le 2e :

8 4 4 79 6 1

Puis le 4e dans le 3e :

8 4 79 79 6 1

Puis le 5e dans le 4e :

8 4 79 6 6 1

Puis le 6e dans le 5e :

8 4 79 6 1 1

Et c’est fini. Les esprits chagrins me diront que le 6e élément, qui devrait être vide, contient toujours la valeur 1. Certes. Mais maintenant, je n’ai plus 6 éléments, mais 5. Le contenu des 6e, 7e… éléments n’a donc aucune importance. C’est le même principe que pour l’exercice précédent (voir sa correction).

Notez bien que l’on décale les éléments en partant de l’élément suivant celui que l’on sup-prime et en allant jusqu’au dernier. Si l’on allait dans l’autre sens, on effacerait les valeurs donc cela ne fonctionnerait pas.

Au fait, soyons précis avec le vocabulaire : techniquement, on a écrasé l’élément plus qu’on ne l’a supprimé !

Page 84: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0084

Les exercices suivants vont traiter du tri et de la gestion des tableaux triés.

Exercice 7

Expliquez l’intérêt d’un tableau trié par rapport à un tableau non trié.

Exercice 8

Écrivez un sous-programme triant le tableau des clients d’après leur identifiant (NumClient). Vous devez connaître une méthode de tri et savoir l’appliquer (par exemple, le tri à bulles vu en première année). Il existe de nombreuses méthodes de tri, plus ou moins efficaces (en général, plus elle est simple à programmer, moins elle est efficace).

Je vais vous présenter la méthode par insertion que je vous demande de programmer. Elle n’est pas plus efficace que celle à bulles mais cela vous changera !

Le principe est très simple : trier un tableau du plus petit élément au plus grand revient à mettre le plus petit en premier, puis celui juste un peu plus grand, puis le suivant et ainsi de suite :

• nous allons chercher le plus petit élément du tableau et le permuter avec le premier. On sait alors que le premier élément est trié ;

• on cherche alors le plus petit élément sans prendre en compte le premier (on va donc de l’indice 2 à l’indice NbrClients). On trouve alors le 2e plus petit que l’on permute avec le deuxième élément du tableau ;

• on continue. À la ie itération, on cherche le plus petit élément de l’indice i à l’indice NbrClients. On trouve alors le ie plus petit que l’on permute avec le ie élément du tableau ;

• on s’arrête lorsque l’on a trouvé non pas le plus grand élément, mais celui juste avant. On le place alors en avant-dernière position. Le dernier élément est forcément le plus grand.

Nous allons essayer cet algorithme sur un tableau exemple. Pour bien se repérer, les cases encadrées en noir seront triées , les cases encadrées en vert seront non-triées .Prenons l’exemple du tableau suivant, pas trié du tout.

5 8 1 2 6 9 8 10 7

On cherche le plus petit élément (c’est la valeur 1 à l’indice 3) et on le permute avec le pre-mier élément. On permute donc les éléments d’indice 1 et 3.

1 8 5 2 6 9 8 10 7

On cherche maintenant le plus petit élément sans prendre en compte le premier. On va donc travailler sur les huit derniers éléments. Le plus petit est alors la valeur 2 (indice 4). Je per-mute donc les éléments d’indice 2 et 4.

1 2 5 8 6 9 8 10 7

Page 85: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0085

Je continue. Le plus petit élément (les deux premiers étant exclus) est la valeur 5 à l’indice 3. Je le permute donc avec l’élément à l’indice 3, soit avec lui-même. Cela ne change rien (l’élément était déjà bien placé).

1 2 5 8 6 9 8 10 7

Ensuite vient l’élément d’indice 5 que je permute avec celui d’indice 4.

1 2 5 6 8 9 8 10 7

Je permute ensuite l’élément d’indice 9 avec celui d’indice 5.

1 2 5 6 7 9 8 10 8

Il me reste les quatre derniers éléments. Le plus petit de ceux-là est la valeur 8. Ce peut être celui d’indice 7 ou 9 selon mon algorithme de recherche. Cela ne change rien. Je prends celui d’indice 7 et je le permute avec celui d’indice 6.

1 2 5 6 7 8 9 10 8

Dans les trois derniers éléments, le plus petit est mon second 8 que je permute avec l’élément d’indice 7.

1 2 5 6 7 8 8 10 9

Il ne me reste que deux éléments à trier. Ainsi, trouver le plus petit des deux détermine aussi le plus grand. Positionner le plus petit revient donc à positionner le plus grand.Le plus petit est à l’indice 9, je permute donc les deux dernières cases.

1 2 5 6 7 8 8 9 10

J’ai l’assurance que mon tableau est trié. Et, en effet, il l’est.Si nous avions fait encore une itération, on aurait cherché le plus petit élément parmi le dernier et on l’aurait permuté avec lui-même… inutile !

Pour réaliser ce tri, vous aurez besoin des sous-programmes suivants :

• un pour permuter deux éléments (clients) d’indices donnés ;

• un recherchant le plus petit élément d’un tableau (le critère est la valeur du champ NumClient). Attention, il faut fournir en paramètre l’indice de départ à partir duquel on cherche l’élément. On a vu ci-dessus qu’à la première ité-ration, on va du 1er au dernier élément, à l’itération suivante du 2e au dernier puis du 3e au dernier…

• enfin, il faut un sous-programme réalisant le tri proprement dit.

Évidemment, les sous-programmes s’appelleront les uns les autres. Ne perdez pas de vue que mon exemple ci-dessus triait selon les éléments, tandis que l’exercice demande le tri sur NumClient, soit un champ des éléments.

Page 86: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0086

Exercice 9

Écrivez un sous-programme cherchant un client d’un numéro donné. Vous supposerez que le tableau est trié sur les numéros et que le client peut ne pas exister.Comme le tableau est trié, il faut appliquer la recherche dichotomique vue en première année.Voici un petit rappel de cette méthode. Dans un tableau trié, on compare l’élément du milieu avec celui que l’on cherche :

• si les deux sont égaux, on a trouvé notre élément ;

• si l’élément du milieu est plus grand, c’est que l’élément cherché est dans la première moitié du tableau. On recommence donc en prenant en compte le premier demi-tableau (élément du milieu excepté) ;

• s’il est plus petit, c’est que l’élément cherché est dans la deuxième moitié du tableau. On recommence donc en prenant en compte le second demi-tableau (élément du milieu excepté).

Pourquoi ne prend-on jamais l’élément du milieu lors du découpage en demi-tableau ? Car, si l’on découpe le tableau, c’est parce que cet élément du milieu n’est pas celui que l’on cherche. Inutile donc de s’en embarrasser ! De plus, cela permettra d’obtenir une condition d’arrêt si l’on ne trouve pas notre élément.

Vous devez donc gérer deux variables : une indiquant l’indice de début du tableau, l’autre l’indice de fin. Ces variables évolueront en fonction des sous-tableaux retenus. Pensez à gérer une condition d’arrêt dans la boucle pour le cas où l’élément n’est pas présent !

Je ne vous donne pas plus d’indications car la recherche dichotomique, c’est du cours : vous devez savoir la mettre en œuvre quand vous en avez besoin. Si vous avez des difficultés, exécutez l’algorithme à la main sur un petit tableau.

Une remarque néanmoins. Pour déterminer le milieu du tableau, ne divisez pas par 2 avec la division réelle mais utilisez div qui réalise la division entière. Par exemple :

• si vous considérez le sous-tableau qui va de l’indice 10 à l’indice 20, l’élément du milieu sera à l’indice (10+20) div 2 soit 15 ;

• avec un sous-tableau possédant un nombre pair de cases, cela fonctionne également. Si mon sous-tableau va des indices 11 à 18, l’élément du milieu sera à l’indice (11+18) div 2 soit 14. (Nous ne sommes pas parfaitement au milieu, mais c’est impossible avec un nombre pair de cases.)

Ce n’est pas un exercice qui se traite en dix minutes. Prenez votre temps.

Exercice 10

Dernier exercice : je vous demande d’écrire un sous-programme ajoutant un client donné au tableau trié sur le numéro NumClient. Le principe est le suivant :

• il faut localiser dans quel élément du tableau on va ajouter le client. L’objectif est que le tableau soit toujours trié après l’ajout ;

• une fois l’indice trouvé, il faut écarter les éléments de droite (qui sont plus grands) pour faire une petite place à notre élément.

Page 87: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0087

Exemple, voici un tableau dont 8 éléments sont utilisés :

1 2 5 6 7 8 8 9

Je souhaite ajouter la valeur 4. Elle se positionnera entre 2 et 5, donc entre les éléments d’indice 2 et 3. On décale donc vers la droite les éléments des indices 3 à 8 (qui seront donc aux indices 4 à 9) :

1 2 5 5 6 7 8 8 9

J’ajoute maintenant mon 4 dans l’élément d’indice 3 qui vient d’être libéré (en pratique, je remplace la valeur 5 à l’indice 3 par ma nouvelle valeur ; l’ancienne valeur 5 est à l’indice 4 depuis le décalage) :

1 2 4 5 6 7 8 8 9

Mon tableau possède un élément de plus (donc 9 au total).

Ces deux étapes ont un goût de déjà vu :

• la recherche de l’indice où ajouter l’élément sera faite par dichotomie (il faudra adapter l’algorithme de l’exercice 9 en conséquence) ou, moins efficace mais plus simple, par un parcours séquentiel du tableau ;

• le décalage des éléments a été vu dans l’exercice 6, sauf qu’ici on décale dans l’autre sens (on agrandit le tableau au lieu de le rétrécir).

Voici une petite aide pour la recherche de l’indice où l’on doit insérer le client. Il y a deux techniques :

• la plus simple mais la moins efficace consiste à parcourir le tableau jusqu’à trouver la case où insérer notre client. Faites attention aux cas extrêmes (début et fin de tableau) ;

• on peut aussi exploiter le fait que le tableau est trié et chercher l’indice par dichotomie en faisant semblant de chercher l’élément que l’on veut ajouter (on sait qu’il n’existe pas puisque la valeur de chaque champ NumClient est unique mais on obtient plus ou moins sa position théorique).

La seconde technique est très efficace mais est assez difficile à mettre en œuvre car il faut extraire de nombreux cas particuliers. Je vous en dispense donc.

Finalement, je veux trois sous-programmes : le premier cherchera l’indice où ajouter l’élé-ment, le deuxième décalera les éléments et le troisième utilisera les précédents pour réaliser l’insertion proprement dite.

Passez suffisamment de temps sur cet exercice. Il est plus complexe que les autres. Vous devez absolument essayer à la main les techniques proposées pour être certain de les comprendre. Il vous faudra ensuite vérifier soigneusement vos algorithmes et notamment les cas extrêmes. Si vous arrivez à faire correctement cet exercice, vous pourrez vous estimer parfaitement au point sur la manipulation des tableaux et des structures.

Vous pouvez, dés maintenant, faire et envoyer à la correction le devoir 2 (voir fasci-cule « Devoirs » réf 3987 DG).

Page 88: Cours - Matière 3987_83987TGPA0007
Page 89: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0089

Travaux dirigés 2 : deux tableaux

Durée indicative : 8 heures

Le premier TD vous avait rafraîchi la mémoire concernant tout ce que l’on peut faire avec un tableau.

Ce second TD met en œuvre deux tableaux liés afin de faire des recherches de l’un dans l’autre. Ce genre de choses est régulièrement demandé à l’examen. En effet, c’est relativement court à rédiger mais il faut de bonnes connaissan-ces pour savoir le faire.

Présentation du TDEn fait, nous allons plus ou moins reprogrammer le langage SQL (soyons modestes, plutôt moins que plus).Déjà, dans le TD précédent, lorsque nous recherchions un client donné ou le client le plus ancien… si le tableau avait été table, nous aurions retrouvé des instructions SQL classiques.Pour garder le lien entre tableaux et base de données, le premier TD utilisait un seul tableau (table). Ce dernier TD va en utiliser plusieurs. Nous allons donc réaliser l’équivalent de join-tures.Je m’en voudrais de vous sembler vieillot. Pourquoi, vous demandez-vous peut-être, mani-puler des tableaux pour émuler une base de données et réaliser des traitements assez com-plexes alors qu’une requête SQL fait cela mieux que nous ? Certes, une base de données est faite pour cela. Mais est-il vraiment utile de déranger (voire d’acheter) un tel outil complexe pour réaliser un traitement somme toute restreint ? On ne réalise plus de grosse application de gestion sans base de données : le faire avec des fichiers et des tableaux serait de l’escro-querie. Mais pour de toutes petites applications… les tableaux sont au poil.

Le corrigé du TD figure dans le fascicule d’autocorrection.Étudiez-le après chaque exercice.

Page 90: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0090

Exercice 1

Définissons nos types

Comme nous allons manipuler plusieurs tableaux, j’en profite pour vous faire définir les différentes données.

Voici le sujet sur lequel nous allons travailler. Il s’agit de réaliser des statistiques sur les horai-res d’arrivée des différents trains dans une gare donnée.

Un train est caractérisé par son type : TGV, TER…, son numéro, les nombre de places en 1re et en 2nde et sa ville d’origine (on ne stocke pas la ville d’arrivée puisque c’est systématiquement celle de la gare que l’on informatise). Tous les trains sont stockés dans un tableau.

Pour chaque train arrivant en gare, on relève son numéro et le décalage à l’arrivée en minu-tes par rapport à l’horaire prévu (0 pour un train à l’heure, -5 pour un train en avance de 5 minutes, 75 pour un retard d’une heure quinze). Ces deux informations sont stockées dans un tableau.

Pour changer un peu par rapport au TD précédent, on supposera que 95 trains s’arrêtent dans cette gare et sont donc concernés. J’insiste : c’est 95, ni 94 ou 96. Ce nombre est cons-tant.

En revanche, on stocke les arrivées des trains au fur et à mesure. Un historique trop impor-tant n’étant pas utile, vous dimensionnerez le tableau pour qu’il puisse contenir 10 000 arri-vées. Lorsque le tableau est plein et que vous ajoutez une nouvelle arrivée, la plus ancienne (contenue dans le premier élément) doit être supprimée.

Je vous demande de définir les types de données nécessaires au stockage de ces informations.

Exercice 2

Parcours d’un seul tableau (début)

Écrivez deux sous-programmes :

• un calculant la moyenne des décalages à l’arrivée de tous les trains ;

• un autre calculant le plus petit, le plus grand et le décalage moyen d’un train donné (identifié par son numéro). Attention, ce n’est pas un algorithme si simple qu’il y paraît : il ne faut prendre en compte que les arrivées concernant notre train et non toutes cel-les du tableau. Pour simplifier (temporairement), vous supposerez que le train existe forcément.

Exercice 3

Parcours d’un seul tableau (fin)

Réécrivez le dernier sous-programme calculant le plus petit, le plus grand et le décalage moyen d’un train donné. Vous prendrez maintenant le cas général du train qui n’existe pas forcément.

Page 91: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0091

Exercice 4

Parcours des deux tableaux

Écrivez un sous-programme déterminant combien d’arrivées de trains de type TGV ont eu lieu en retard. (Un même train TGV arrivant trois fois en retard sera comptabilisé trois fois.)

Exercice 5

Recherche

Écrivez la fonction de recherche indiquée à la fin de la correction de l’exercice précédent (on cherche l’indice d’un train dont le numéro est donné) puis réécrivez NbrTGVRetard en utilisant cette fonction.

Exercice 6

Écrivez un algorithme complet (et donc plus seulement un sous-programme) permettant de savoir si un train donné s’arrête dans notre gare (donc s’il fait partie ou non de notre tableau des trains).

Vous supposerez que la procédure InitTrains (var Trains : tableau[95] de Train) est fournie (vous pouvez donc vous en servir !). Elle permet de remplir le tableau des trains.

Pour les quatre derniers exercices, nous supposons que le tableau des arrivées est trié par numéro de train. Ainsi, si nous avons les trains numéro 15, 87 et 665, le tableau contient d’abord toutes les arrivées du train 15 puis celles du train 87 et enfin celles du 665.

Exercice 7

Il n’y a qu’un train en provenance de Limoges. Combien d’arrivées a-t-il effectué ? (On sup-posera qu’il est arrivé au moins une fois.) Faites des sous-programmes !

En première approche, pour trouver les arrivées d’un train donné, nous n’allons exploiter que partiellement le fait que le tableau des arrivées est trié par train : parcourez séquentiel-lement le tableau jusqu’à trouver les arrivées de votre train. Ensuite, vous avancerez dans le tableau jusqu’à ce que les arrivées ne concernent plus votre train. Comme le tableau est trié par train, vous êtes assurés de n’avoir loupé aucune des arrivées cherchées.

Exercice 8

Modifiez l’algorithme précédent en supposant que le train de Limoges peut n’être encore jamais arrivé dans notre gare.

Page 92: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0092

Exercice 9

C’est toujours le même sujet : combien d’arrivées du train de Limoges ont été enregistrées ? Nous allons maintenant exploiter pleinement la potentialité de notre tableau d’arrivées trié. Vous allez faire une recherche dichotomique pour trouver une arrivée du train cherché. Cela vous donnera une arrivée quelconque de ce train. Vous devrez donc reculer puis avancer dans le tableau pour trouver toutes les arrivées.

Voici une illustration. Prenons le tableau d’entiers suivants :

1 2 4 4 4 4 6 6 7

Si je cherche les éléments de valeur 4, la première étape de la dichotomie me renvoie immé-diatement (j’ai eu de la chance, dans le cas général, il faudra réaliser des itérations avec les sous-tableaux) l’élément d’indice 5 contenant effectivement un 4. Il faut ensuite que je recule (éléments d’indices 4 et 3) puis que j’avance (élément d’indice 6) pour obtenir toutes les occurrences de mes valeurs 4.

Bien entendu, nous nous situons toujours dans le cas général où le train peut n’être jamais arrivé. Quand vous reculez pour chercher les premières arrivées, attention à ne pas sortir du tableau ; et quand vous avancez… idem.

Exercice 10

Pour terminer cette séquence de TD, je vous demande d’écrire un algorithme complet (comme dans l’exercice 6) permettant d’indiquer à l’utilisateur combien de voyageurs sont arrivés dans notre gare en provenance d’une ville donnée. La SNCF vous indique que statisti-quement, 65 % des places de première et 79 % des places de seconde sont occupées.

Vous avez évidemment toujours à votre disposition la fonction InitTrains (var Trains : tableau[95] de Train). De même, vous supposerez que tout ce dont vous avez besoin concer-nant l’initialisation du tableau des arrivées est fourni.

Cet exercice n’est qu’une extension des précédents : il faut reprendre des bouts de code à gauche à droite et les compléter.

Je vous propose de commencer l’exercice dès maintenant. Si vraiment vous n’y arrivez pas ou, mieux encore, une fois votre algorithme écrit, lisez ce qui suit et vérifiez votre algorithme à la lumière des indications. Corrigez alors si besoin est votre travail avant d’étudier la correction.

Voici quelques indications pour réaliser l’exercice. Partez de l’exercice 9. Ce qui change, c’est :

• on ne cherche plus un seul train venant de Limoges, mais de 0 à n trains venant d’une ville précise. Tout le traitement de l’exercice 9 doit donc être encadré par une boucle cherchant ces trains en parcourant le tableau des trains ;

• ensuite, c’est trivial mais il faut penser à le faire, le nombre d’arrivées d’un train donné ne suf-fit plus, il faut réaliser une opération arithmétique avec ce nombre et la capacité du train.

Vous pouvez réaliser le TD 3.

Page 93: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0093

Travaux dirigés 3 : sur machine

Durée indicative : autant de temps que possible

Le développement, c'est sur machine !

Savoir raisonner sur le papier, c’est une chose. Mais le développement d’applications doit conduire à une application !

Vous devez donc reprendre les algorithmes du cours et les coder dans un langage de pro-grammation (par exemple WinDev déjà utilisé l’année dernière).

Utilisez le débogueur, modifiez les algorithmes et voyez ce qui se passe. C’est comme cela que vous comprendrez exactement pourquoi une boucle doit s’arrêter à 1 et pas à 0, pour-quoi un test est nécessaire…

Page 94: Cours - Matière 3987_83987TGPA0007
Page 95: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0095

Devoir autocorrectif

L’objet de ce devoir « autocorrectif »å est l’écriture de sous-programmes manipulant des liens entre tableaux. L’accent sera mis sur les différents par-cours et accès.

Cela veut-il dire que l’on peut mettre n’importe quel type de sous-programme avec un passage par valeur ou par adresse aléatoire ? Certainement pas !

Exercice 1

Nous voulons gérer les notes des différents étudiants d’une classe. Pour cela, nous disposons de trois tableaux :

• un contenant les étudiants ;

• un contenant les différentes matières. Notez que chacune est affectée d’un coefficient ;

• un troisième contenant une note (et l’étudiant et discipline concernés).

Voici les types concernant les étudiants :

type Étudiant = structure Num : entier // Numéro de l’étudiant (identifiant) Nom : chaîne Prénom : chaîne fin structure

type Classe = structure t : tableau [50] d’Etudiant NbrÉtudiants : entier fin structure

Je définis un type Classe contenant un tableau d’étudiants avec une variable NbrÉtudiants indiquant combien d’éléments du tableau sont effectivement initialisés (soit combien j’ai d’élèves dans ma classe).

å Il ne faut pas envoyer ce devoir à la correction. Le corrigé se trouve dans le fascicule « Autocorrection ».

Page 96: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0096

Voici les types concernant les matières :

type Matière = structure Num : entier // Numéro de la matière (identifiant) Libellé : chaîne Coefficient : entier fin structure

type TabMatières = structure t : tableau [20] de Matière NbrMatières : entier fin structure

Voici les types concernant les notes :

type Note = structure NumÉtudiant : entier NumMatière : entier Note : entier // je suppose que les notes sont arrondies fin structure

type TabNotes = structure t : tableau [1000] de Note NbrNotes : entier fin structure

Soyons sûrs de bien comprendre.

Si je veux les notes de l’étudiant Nina TECKEL dans la matière Programmation, comment faire ? Il y a trois étapes :

1. Je vais chercher dans le tableau des étudiants le numéro de l’étudiant Nina TECKEL. Imaginons que ce soit 4.

2. Je vais chercher dans le tableau des matières le numéro de la matière Programmation. Imaginons que ce soit 13.

3. Dans le tableau des notes, je cherche celle de l’étudiant 4 dans la matière 13.

On supposera qu’un étudiant possède exactement une note par matière et que le tableau des notes est trié par matières (on a donc toutes les notes de la matière 1 puis toutes celles de la matière 2…).

Page 97: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0097

Travail à faire

1. Écrivez un sous-programme renvoyant la moyenne des notes pour une matière donnée (identifiée par son libellé). Vous supposerez que la matière existe.

Je vous rappelle que la moyenne d’un étudiant se calcule en additionnant le produit de cha-que note par son coefficient et en divisant le tout par la somme des coefficients.Par exemple, si j’ai 15 en étude de cas (coefficient 5), 12 en épreuve pratique (coefficient 3) et 19 en note de synthèse (coefficient 4), ma moyenne sera :

15 x 5 + 12 x 3 + 19 x 4 = 187 = 15,58 5 + 3 + 4 12

2. Écrivez un sous-programme renvoyant la moyenne d’un étudiant dont on vous fournit le nom et le prénom. Attention, cet étudiant peut ne pas exister (on supposera néanmoins qu’il n’y a pas d’homonyme). Vous veillerez à prendre en compte les coefficients des dif-férentes matières.

Exercice 2

Vous avez enfin trouvé enfin l'opérateur téléphonique qui est vraiment, vraiment moins cher que tous les autres.

Le problème, c’est que sa grille de tarification est la plus complexe du marché :

• tout appel abouti (le correspondant décroche) est facturé d’un frais fixe (0,046 €) ;

• les 0,046 € de frais de connexion donnent droit à des secondes gratuites. Combien ? Cela dépend de la tranche horaire ;

• le prix des secondes supplémentaires (au-delà des secondes gratuites) dépend égale-ment de l’heure d’appel.

On retrouve les vingt-quatre tranches d’une heure formant une journée et numérotées de 1 à 24. Elles possèdent deux propriétés : le nombre de secondes gratuites par appel et le prix de la seconde.

Par exemple :

• la tranche horaire 0 h – 1 h possède le numéro 1. Les 60 premières secondes sont gra-tuites (incluses dans les 0,046 €) et les secondes suivantes sont facturées 0,0002 €. (C’est bien peu ? Certes, mais une seconde, c’est bien court !) ;

• la tranche horaire 14 h – 15 h possède le numéro 15. Un appel passé à cette heure dispose de 28 secondes gratuites (incluses dans les 0,046 €) puis chaque seconde coûte 0,0005 €.

Très fortement paranoïaque, vous êtes convaincu que les factures que vous recevez sont erro-nées. Vous bricolez un circuit électronique relié à votre PC remplissant un fichier contenant, pour chaque tranche horaire, la liste de tous les appels avec leur duréeå .

En effet, connaître le nombre d’appels et leur durée totale n’est plus suffisant à cause des premières secondes gratuites : il faut impérativement connaître la durée de chaque appel.Vous allez ensuite écrire un programme exploitant ces données.

å La réalisation de ce boîtier n’est pas demandée.

Page 98: Cours - Matière 3987_83987TGPA0007

8 3987 TG PA 0098

Pour informatiser cela, vous définissez un type structuré Tarif définissant, pour une tranche horaire, le nombre de secondes gratuites et le tarif de la seconde supplémentaire :

type Tarif = structure NbrSecGratuites : entier TarifSeconde : réel fin structure

var Tab Tarifs : tableau [24] de Tarif

Nous avons donc un tableau de vingt-quatre tarifs, l’indice déterminant la tranche horaire.Par exemple pour les appels payés de 14 à 15 h :

• TabTarifs[15].NbrSecGratuites = 28 ;

• TabTarifs[15].TarifSeconde = 0,0005.

Pour suivre vos communications, vous récupérez les informations que votre boîtier électroni-que (à ne pas réaliser) a stocké dans des fichiers.Pour cela, vous avez les types suivants :

type Appel = structure TrancheHoraire : entier Durée : entier fin structure

type TypeTabAppels = structure t : tableau [10000] de Appel NbrAppels : entier fin structure

On retrouve nos tableaux contenant un nombre variable d’éléments. En effet, je peux passer un seul appel, vingt, trois cents… Chaque élément du champ t correspond à un appel. Je mémorise la tranche horaire (t[i].TrancheHoraire) correspondante et la durée de l’appel (t[i].Durée).

Remarque importante, on considère que les appels du tableau t contenu dans la structure TabAppels sont triés sur la tranche horaire. Cela signifie que l’on aura d’abord tous les appels pas-sés entre minuit et une heure (tranche horaire 1), puis tous les appels dans la tranche horaire 2…

Assurez-vous de bien avoir compris le contenu des différents tableaux et structures, sinon vous ne pourrez pas écrire l’algorithme demandé. Je ne vous cache pas que l’essentiel de la difficulté réside là. C’est d’ailleurs pourquoi je ne vous donne pas d’exemple de contenu de tableau.

Travail à faire

Écrivez un sous-programme prenant en paramètre une structure de type TabAppels et un tableau de tarifs et calculant le total à payer.