Vous êtes sur la page 1sur 98

BTS Informatique de gestion 2e 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).
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érentiel
Le 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.

3
8 3987 TG PA 00
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 formation
En 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.

4
8 3987 TG PA 00
Organisation
Le 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 !

Contenu
Les 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.

Notations
Pour 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 .

5
8 3987 TG PA 00
Quelques conseils
Le 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

6
8 3987 TG PA 00
Séquence 1
Variables et instructions
Duré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

Contenu
1. 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

7
8 3987 TG PA 00
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 !)

8
8 3987 TG PA 00
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
instructions
fin

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 ? ».

9
8 3987 TG PA 00
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.

10
8 3987 TG PA 00
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.

11
8 3987 TG PA 00
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éorie
Tout 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).

12
8 3987 TG PA 00
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.

13
8 3987 TG PA 00
Séquence 1

3C. Gestion de la mémoire


J’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 ?

14
8 3987 TG PA 00
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émoire
VARIABLE TYPE ADRESSE DÉBUT A B C D E F G H I J
1 22 2 246 235 222 109 20 170 225 55
2 24 240 41 108 85 45 230 15 145 55
3 73 213 65 214 206 56 245 41 92 96
4 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émoire
VARIABLE 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 55
Nom 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 96
4 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.

15
8 3987 TG PA 00
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 := 51554
Nom := «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émoire
VARIABLE 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 55
Nom 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 96
4 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émoire
VARIABLE 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 55
Nom 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 96
4 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.

16
8 3987 TG PA 00
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émoire
VARIABLE 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 55
Nom 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 96
4 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émoire
VARIABLE 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 55
Nom chaîne [7] B4 2 24 240 41 51556 45 230 15 145 55
3 73 213 65 214 206 56 245 41 92 96
4 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/2
Nom := Majuscule(Nom)

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

17
8 3987 TG PA 00
Séquence 1

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


Tableau des variables État de la mémoire
VARIABLE 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 55
Nom chaîne [7] B4 2 24 240 41 25778 45 230 15 145 55
3 73 213 65 214 206 56 245 41 92 96
4 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émoire
VARIABLE TYPE ADRESSE DÉBUT A B C D E F G H I J
1 22 2 246 235 222 109 20 170 225 55
2 24 240 41 178 100 45 230 15 145 55
3 73 213 65 214 206 56 245 41 92 96
4 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).

18
8 3987 TG PA 00
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 :", NomPersonne


saisir "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.
19
8 3987 TG PA 00
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.

20
8 3987 TG PA 00
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 si booléen
alors alors
instructions_oui instructions_oui
sinon fin si
instructions_non
fin 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.

21
8 3987 TG PA 00
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_1 instr_1
instr_2 instr_2
instr_3 instr_3
si booléen évaluation booléen
alors booléen booléen
instr_o_1 vrai faux
instr_o_2
instr_o_3 instr_o_1 instr_n_1
sinon instr_o_2 instr_n_2
instr_n_1 instr_o_3
instr_n_2
fin si
instr_4 instr_4
instr_5 instr_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.

22
8 3987 TG PA 00
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… faire


Je 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


instructions
fin 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.

23
8 3987 TG PA 00
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_1
instr_2 instr_1
instr_3 instr_2
instr_3
tant que booléen faire
évaluation booléen
instr_tq_1
instr_tq_2 booléen booléen
vrai faux
instr_tq_3
fin tant que instr_tq_1
instr_4 instr_tq_2
instr_5 instr_tq_3

instr_4
instr_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" afficher "avant boucle"


tant que (2 = 3) faire i := 1
afficher "dans boucle" tant que (i > 0) faire
x := x/0 afficher "dans boucle"
fin tant que i := i+1
afficher "après boucle" fin tant que
afficher "après boucle"

3e bloc 4e bloc

afficher "avant boucle" afficher "avant boucle"


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

24
8 3987 TG PA 00
Variables et instructions

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


Dans la vraie vie :
tu mangeras
jusqu’à ce que ton assiette soit vide.
Voici la syntaxe :

répéter
instructions
jusqu’à 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_1
instr_1
instr_2
instr_2
répéter
instr_rjq_1 instr_rjq_1
instr_rjq_2 instr_rjq_2
instr_rjq_3 instr_rjq_3
jusqu’à booléen évaluation booléen
instr_3 booléen booléen
vrai faux
instr_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 répéter
saisir "Entrez l’âge : ", âge x := x/0
jusqu’à (âge > 0) et (âge < 120) jusqu’à x = x

25
8 3987 TG PA 00
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
… avant après
la 1re exécution de la boucle
nombre d’itérations (d’exécu-
tions des instructions de la 0à∞ 1à∞
boucle)
on sort de la boucle quand le
faux vrai
booléen est…
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


instructions
fin 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à.

26
8 3987 TG PA 00
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.

27
8 3987 TG PA 00
Séquence 1

ALGORITHME TRACE DE L’EXÉCUTION


instr_1
instr_1
instr_2
instr_2
indice := e1
pour indice de e1 à e2 pas e3 indice ≤ e2 ?
instr_p_1
non oui
instr_p_2
instr_p_3 instr_p_1
fin pour instr_p_2
instr_4 instr_p_3
instr_5 indice := indice + e3

instr_4
instr_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 pour i de 10 à 1 pas -1


afficher i pour j de 10 à 1 pas -1
fin pour afficher i, "x", j, "=", i*j
fin pour
fin 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.
28
8 3987 TG PA 00
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 boucles


Concernant 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é.

29
8 3987 TG PA 00
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 !

30
8 3987 TG PA 00
Synthèse

Syntaxe générale d’un algorithme


(Sans sous-programme pour le moment.)

Algo nom de l’algorithme


var
déclaration des variables
début
instructions
fin
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).

31
8 3987 TG PA 00
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 si booléen
alors alors
instructions_oui instructions_oui
sinon fin si
instructions_non
fin 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


instructions
fin 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.

32
8 3987 TG PA 00
Tant que le booléen est vrai, on exécute les instructions dans la boucle.
Répéter

répéter
instructions
jusqu’à 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
… avant après
la 1re exécution de la boucle
nombre d’itérations (d’exécutions
0à∞ 1à∞
des instructions de la boucle)
on sort de la boucle quand le
faux vrai
booléen est…
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


instructions
fin 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.

33
8 3987 TG PA 00
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.

34
8 3987 TG PA 00
Séquence 2
Procédures et fonctions
Duré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

Ñ Contenu
1. 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

35
8 3987 TG PA 00
Séquence 2

1. Introduction

1A. Définition
Les 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-programme


Un 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êt
La 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éfinitions
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.
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.

36
8 3987 TG PA 00
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. Introduction
Ré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.

37
8 3987 TG PA 00
Séquence 2

2B3. La fonction
Voici la syntaxe :
fonction nom (paramètres) : type

var
déclarations

début
instruction1
...
instructionn
Retourner (expression) // Retour du résultat
fin
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 Nom2 (paramètres) : type

fonction Nom1 (paramètres) : type début


si booléen
début alors
instruction1 instruction1
... sinon
instructionn instruction2
Retourner (expression) fin si
fin Retourner (expression)
fin

38
8 3987 TG PA 00
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 Nom4 (paramètres) : type


02
03 début
01 fonction Nom3 (paramètres) : type 04 si booléen
02 05 alors
03 début 06 si booléen2
04 si booléen 07 alors
05 alors 08 instruction1
06 instruction1 09 Retourner (expression)
07 sinon 10 sinon
08 instruction2 11 Retourner (expression)
09 Retourner (expression) 12 fin si
10 fin si 13 sinon
11 fin 14 instruction2
15 fin si
16 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.

39
8 3987 TG PA 00
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-programmes


Je 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. Syntaxe
Nous 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) fonction Maximum (n1 : réel, n2 : réel) : réel
début début
… …
fin 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.

40
8 3987 TG PA 00
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 passage


On 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.

41
8 3987 TG PA 00
Séquence 2

Exercice 17
Envisageons l’algorithme suivant :
Algorithme échange

var
e1, e2 : entier

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


var
c : entier
début
c := a
a := b
b := c
fin

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 ", e2
fin
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’exercice


Pour 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émoire
VARIABLE TYPE ADRESSE DÉBUT A B C D E F G H I J
1
2
3
4
J’insiste : du point de vue du programme qui s’exécute, la mémoire est vide.

42
8 3987 TG PA 00
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émoire
VARIABLE TYPE ADRESSE DÉBUT A B C D E F G H I J
e1 entier a1 1 ??? ???
e2 entier f1 2
3
4
(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émoire
VARIABLE TYPE ADRESSE DÉBUT A B C D E F G H I J
e1 entier a1 1 50 100
e2 entier f1 2
3
4
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 J
e1 entier a1 1 50 100
e2 entier f1 2
a entier c3 3 50
b 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 J
e1 entier a1 1 50 100
e2 entier f1 2
a entier c3 3 50
b entier g4 4 100
c entier c5 5 ???

43
8 3987 TG PA 00
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 J
e1 entier a1 1 50 100
e2 entier f1 2
a entier c3 3 50
b entier g4 4 100
c 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 J
e1 entier a1 1 50 100
e2 entier f1 2
a entier c3 3 100
b entier g4 4 100
c 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 J
e1 entier a1 1 50 100
e2 entier f1 2
a entier c3 3 100
b entier g4 4 50
c 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émoire
VARIABLE TYPE ADRESSE DÉBUT A B C D E F G H I J
e1 entier a1 1 50 100
e2 entier f1 2
3
4
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 adresse


procé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.

44
8 3987 TG PA 00
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 J
e1 entier a1 1 50 100
e2 entier f1 2
a entier a1 3
b 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 J
e1 entier a1 1 50 100
e2 entier f1 2
a entier a1 3 ???
b entier f1 4
c 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 J
e1 entier a1 1 50 100
e2 entier f1 2
a entier a1 3 50
b entier f1 4
c 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 J
e1 entier a1 1 100 100
e2 entier f1 2
a entier a1 3 50
b entier f1 4
c 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 J
e1 entier a1 1 100 50
e2 entier f1 2
a entier a1 3 50
b entier f1 4
c entier c3 5

45
8 3987 TG PA 00
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émoire
VARIABLE TYPE ADRESSE DÉBUT A B C D E F G H I J
e1 entier a1 1 100 50
e2 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’ignore


J’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.

46
8 3987 TG PA 00
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) : type
var
déclarations
dé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éclarations
début
instruction1
...
instructionn
Résultat := expression
fin

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).

47
8 3987 TG PA 00
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éclarations
début
instruction1
...
instructionn
fin

49
8 3987 TG PA 00
Séquence 2

Fonction
Syntaxe de la fonction

fonction nom (paramètres) : type


var
déclarations
dé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ètres
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.
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 :
• le 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 ;
• le 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.

Remarque
Je 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.

50
8 3987 TG PA 00
Séquence 3
Tableaux, structures et types
Duré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 attendues
Savoir définir ses propres types de données

Ñ Contenu
1. 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

51
8 3987 TG PA 00
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 ?)

52
8 3987 TG PA 00
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.

53
8 3987 TG PA 00
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.

54
8 3987 TG PA 00
Tableaux, structures et types

Exemples :
Moyennes : tableau[20] d’entiers
Je 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éels
J’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éels
J’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.

55
8 3987 TG PA 00
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.

56
8 3987 TG PA 00
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 !

57
8 3987 TG PA 00
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’indice


Je 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.

58
8 3987 TG PA 00
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’entiers
Pour 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.

59
8 3987 TG PA 00
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).

60
8 3987 TG PA 00
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émoire


Cette 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.

61
8 3987 TG PA 00
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
62
8 3987 TG PA 00
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.

63
8 3987 TG PA 00
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 t t[1,1] t[1,2] t[1,3] … t[4,3] t[i,j]
(notation à deux dimensions)
Tableau t t[1] t[2] t[3] … t[12] t[(i-1)*3+j] t[i]
(notation à une dimension)
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éral
Prenons 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.

64
8 3987 TG PA 00
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. Introduction
Vous 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.

65
8 3987 TG PA 00
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 :

CLIENT
date_client représente la date de la première commande du client
num_client
(ce qui permet, par différence avec la date du jour, de connaître
nom_client l’ancienneté du client).
prénom_client
adr_client
tél_client
date_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.

66
8 3987 TG PA 00
Tableaux, structures et types

3B. Syntaxe de la définition


Comme 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 type


Voyons 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és


Un 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.

67
8 3987 TG PA 00
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 ».

68
8 3987 TG PA 00
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.

69
8 3987 TG PA 00
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. Introduction
Une 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.

70
8 3987 TG PA 00
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.

71
8 3987 TG PA 00
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

72
8 3987 TG PA 00
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.)

73
8 3987 TG PA 00
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).

74
8 3987 TG PA 00
Tableaux, structures et types

4D Ce qui est à venir


Le 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.

75
8 3987 TG PA 00
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].

77
8 3987 TG PA 00
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.

78
8 3987 TG PA 00
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.

79
8 3987 TG PA 00
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.

81
8 3987 TG PA 00
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.

82
8 3987 TG PA 00
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é !

83
8 3987 TG PA 00
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

84
8 3987 TG PA 00
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.

85
8 3987 TG PA 00
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.

86
8 3987 TG PA 00
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).

87
8 3987 TG PA 00
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 TD
En 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.

89
8 3987 TG PA 00
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.

90
8 3987 TG PA 00
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.

91
8 3987 TG PA 00
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.

92
8 3987 TG PA 00
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…

93
8 3987 TG PA 00
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 ».

95
8 3987 TG PA 00
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…).

96
8 3987 TG PA 00
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.

97
8 3987 TG PA 00
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.

98
8 3987 TG PA 00

Vous aimerez peut-être aussi