TP Programmation 2 Algorithmie
TP Programmation 2 Algorithmie
Sommaire
Un programme informatique 3
Objectifs du programmeur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Entrées et sorties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Objets, types et valeurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
Nommer une variable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
Expressions et instructions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
Conditionner une action . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
Les variables booléennes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Itérer une action . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
Bilan 25
1
SOMMAIRE
Les objectifs de ce tp sont de comprendre et mettre en oeuvre des variables dans les struc-
tures fondamentales d’algorithmie : enchaînements, alternatives, itérations. Les exercices
sont extraits du site [Link].
Les critères d’évaluation de ce tp sont :
– le minimum attendu : on doit pouvoir fabriquer un exécutable et le lancer !
– le programme doit énoncer ce qu’il fait et faire ce qu’il énonce !
– le respect des noms de fichiers
– le nommage des variables ainsi que leurs types, l’utilisation de constantes
– le code est fonctionnel
– la simplicité du code
Un programme informatique
Un programme informatique (simple) est souvent structuré de la manière suivante :
Un traitement est tout simplement l’action de produire des sorties à partir d’entrées : les
vrais programmes ont donc tendance à produire des résultats en fonction de l’entrée qu’on leur fournit.
Objectifs du programmeur
Le métier de programmeur consiste à écrire des programmes qui :
– donnent des résultats corrects
– sont simples
– sont efficaces
L’ordre donné ici est très important : peu importe qu’un programme soit rapide si ses résultats sont faux.
De même, un programme correct et efficace peut être si compliqué et mal écrit qu’il faudra le jeter ou le
récrire complètement pour en produire une nouvelle version. N’oubliez pas que les programmes utiles
seront toujours modifiés pour répondre à de nouveaux besoins.
Un programme (ou une fonction) doit s’acquitter de sa tâche de façon aussi simple que
possible.
Nous acceptons ces principes quand nous décidons de devenir des professionnels. En termes pratiques, cela
signifie que nous ne pouvons pas nous contenter d’aligner du code jusqu’à ce qu’il ait l’air de fonctionner :
nous devons nous soucier de sa structure. Paradoxalement, le fait de s’intéresser à la structure et à la
“qualité du code” est souvent le moyen le plus facile de faire fonctionner un programme.
En programmation, les principes à respecter s’expriment par des règles de codage et des bonnes
pratiques.
+ Exemples :
– Règle de codage : les valeurs (comme 10 pour un maximum ou 3.14 pour pi) dans une expression
doivent être déclarées comme des constantes. En C, les constantes s’écrivent en majuscules.
– Bonne pratique : Un programme (ou une fonction) ne doit pas dépasser 15 lignes de C/C++ (accolades
exclues).
Entrées et sorties
Pour l’instant, nos programmes se limiteront à lire des entrées en provenance du clavier de l’utilisateur
et de produire des résultats en sortie sur l’écran de celui-ci.
Pour lire des entrées saisies au clavier (l’entrée standard stdin), on utilisera :
– scanf() en C ou C++
– cin en C++
- Ne vous préoccuper pas des saisies invalides. Pour écrire des programmes qui fonctionnent et qui sont
simples, il est plus prudent pour l’instant de supposer que l’on fait face à un utilisateur « parfait ». Par
exemple, lorsque on souhaite lire un entier, l’utilisateur « parfait » saisit un entier !
Pour afficher des résultats à l’écran (la sortie standard stdout), on utilisera :
– printf() en C ou C++
– cout en C++
- Evidemment, il existe d’autres types d’entrées/sorties (fichier, réseau, base de données, ...) pour les
programmes. Nous les verrons plus tard.
+ Exemples :
– les entiers sont stockés dans des objets de type int
– les réels sont stockés dans des objets de type float ou double
– les chaînes de caractères sont stockées dans des objets de type string en C++
– etc ..
Vous pouvez vous représenter un objet comme “une boite” (une case) dans laquelle vous pouvez mettre
une valeur du type de l’objet :
Représentation d’objets
- Il est fondamentalement impossible de faire quoi que ce soit avec un ordinateur sans stocker des
données en mémoire (on parle ici de la RAM).
Une instruction qui définit une variable est ... une définition !
- Une déclaration est l’action de nommer quelque chose et une définition de la faire exister. Les
déclarations (situées dans des fichiers .h) sont utilisées pendant la phase de compilation et les définitions
(dans les fichiers .cpp) sont indispensables à l’édition des liens pour fabriquer un exécutable.
Une définition peut (et généralement doit) fournir une valeur initiale. Trop de programmes informatiques
ont connu des bugs dûs à des oublis d’initialisation de variables. On vous obligera donc à le faire
systématiquement. On appelle cela “respecter une règle de codage“. Il en existe beaucoup d’autres.
int nombreDeTours = 100; // Correct 100 est une valeur entière
string prenom = "Robert"; // Correct "Robert" est une chaîne de caractères
// Mais :
int nombreDeTours = "Robert"; // Erreur : "Robert" n’est pas une valeur entière
string prenom = 100; // Erreur : 100 n’est pas une chaîne de caractères (il manque les
guillemets)
Initialisation de variables
- Le compilateur se souvient du type de chaque variable et s’assure que vous l’utilisez comme il est
spécifié dans sa définition.
Le C++ dispose de nombreux types. Toutefois, vous pouvez écrire la plupart des programmes en n’en
utilisant que cinq :
int nombreDeTours = 100; // int pour les entiers
double tempsDeVol = 3.5; // double pour les nombres en virgule flottante (double précision)
string prenom = "Robert"; // string pour les chaînes de caractères
char pointDecimal = ’.’; // char pour les caractères individuels ou pour des variables
entières sur 8 bits (un octet)
bool ouvert = true; // bool pour les variables logiques (booléenes)
Les types usuels en C++
- Le type string n’existe pas en langage C. Pour manipuler des chaînes de caractères en C, il faudra
utiliser des tableaux que l’on verra plus tard.
#include <stdbool.h> /* pour le type bool en C */
Expressions et instructions
La brique de base la plus élémentaire d’un programme est une expression. Une expression calcule une
valeur à partir d’un certain nombre d’opérandes. Cela peut être une valeur littérale comme 10, ’a’,
3.14, "rouge" ou le nom d’une variable.
- Il existe de nombreux opérateurs : les opérateurs arithmétiques (+, -, *, / et %), les opérateurs
relationnels (<, <=, >, >=, == et !=), les opérateurs logiques && (et), || (ou) et ! (non), les opérateurs
bits à bits & (et), | (ou) et ~ (non) ...
Cela se traduit en C :
int age;
scanf("%d", &age);
if (age < 10)
{
printf("Accès interdit\n");
}
On écrit donc le mot-clef if, la traduction en anglais de « si », puis on met entre parenthèses la condition
à tester, à savoir age < 10. On n’oublie pas de de mettre des accolades.
Ainsi, l’accès est interdit à un enfant de 8 ans :
$ ./tester-age
8
Accès interdit
Pour exprimer la condition du « si » dans le programme, on a utilisé le symbole <, qui est l’opérateur de
comparaison strictement inférieur. De manière symétrique, l’opérateur > permet de tester si un nombre
est strictement supérieur à un autre. Lorsqu’on veut tester si un nombre est inférieur ou égal à un
autre, on utilise le symbole <=. De manière symétrique, le symbole >= permet de tester si un nombre est
supérieur ou égal à un autre.
Par exemple, le code suivant permet de tester si la température de l’eau a atteint 100 degrés :
int temperature;
scanf("%d", &temperature);
if (temperature >= 100)
{
printf("L’eau bout !");
}
if (unite == ’i’)
cout << longueur << " in == " << conversion*longueur << " cm\n";
else
cout << longueur << " cm == " << longueur/conversion << " in\n";
}
Conversion cm/inch (version 1)
En fait cet exemple semble seulement fonctionner comme annoncé. Ce programme dit que si ce n’est pas
une conversion en inch c’est forcément une conversion en cm. Il y a ici une dérive sur le comportement
de ce programme si l’utilisateur tape ’f’ car il convertira des cm en inches ce qui n’est probablement pas
ce que l’utilisateur désirait. Un programme doit se comporter de manière sensée même si les utilisateurs
ne le sont pas.
Voici une version améliorée en utilisant une instruction if imbriquée dans une instruction if :
if (unite == ’i’)
cout << longueur << " in == " << conversion*longueur << " cm\n";
else if (unite == ’c’)
cout << longueur << " cm == " << longueur/conversion << " in\n";
else
cout << "Désolé, je ne connais pas cette unité " << unite << endl;
Conversion cm/inch (version 2)
De cette manière, vous serez tenter d’écrire des tests complexes en associant une instruction if à chaque
condition. Mais, rappelez-vous, le but est d’écrire du code simple et non complexe.
En réalité, la comparaison d’unité à ’i’ et à ’c’ est un exemple de la forme de sélection la plus courante :
une sélection basée sur la comparaison d’une valeur avec plusieurs constantes. Le C/C++ fournit pour
cela l’instruction switch.
int main()
{
const double conversion = 2.54; // nombre de cm pour un pouce (inch)
int longueur = 1; // longueur (en cm ou en in)
char unite = 0; // ’c’ pour cm ou ’i’ pour inch
cout << "Donnez une longueur suivi de l’unité (c ou i):\n";
cin >> longueur >> unite;
switch (unite)
{
case ’i’:
cout << longueur << " in == " << conversion*longueur << " cm\n";
break;
case ’c’:
cout << longueur << " cm == " << longueur/conversion << " in\n";
break;
default:
cout << "Désolé, je ne connais pas cette unité " << unite << endl;
break;
}
}
Conversion cm/inch (version 3)
L’instruction switch utilisée ici sera toujours plus claire que des instructions if imbriquées, surtout si
l’on doit comparer à de nombreuses constantes.
Vous devez garder en mémoire ces particularités quand vous utilisez un switch :
– la valeur utilisée pour le switch() doit être un entier, un char ou une énumération (on verra cela plus
tard). Vous ne pourrez pas utiliser un string par exemple.
– les valeurs des étiquettes utilisées dans les case doivent être des expressions constantes (voir plus loin).
Vous ne pouvez pas utiliser de variables.
– vous ne pouvez pas utiliser la même valeur dans deux case
– vous pouvez utiliser plusieurs case menant à la même instruction
– l’erreur la plus fréquente dans un switch est l’oubli d’un break pour terminer un case. Comme ce
n’est pas une obligation, le compilateur ne détectera pas ce type d’erreur.
if (estPasCher)
{
printf("Pas cher");
}
La variable estPasCher est appelée une variable booléenne ou un booléen de type bool car elle ne
peut être que vraie ou fausse, ce qui correspond en C/C++ aux valeurs true (pour vrai) et false (pour
faux).
- N’oublier pas que le type bool est natif en C++. Par contre en C, il faut inclure stdbool.h pour
pouvoir l’utiliser.
Une maladresse classique avec les booléens est de faire quelque chose comme ceci :
int prix;
scanf("%d", &prix);
bool estCher = (prix > 100)
if (estCher == true)
{
printf("C’est cher !");
}
Le code est correct mais on n’a pas besoin de tester si quelque chose est égal à true ou false , si ce
quelque chose est lui même déjà true ou false !
On écrira donc :
if (estCher)
{
printf("C’est cher !");
}
// ou :
if (!estCher)
{
printf("Pas cher"));
}
Il est bien sûr possible d’utiliser des opérateurs booléens (les opérateurs && et ||) pour combiner des
conditions et les valeurs booléennes sont également utilisables.
Voici quelques extraits de code à titre d’exemple :
bool estSenior = (age >= 60);
bool estJeune = (age <= 25) && (age >= 12);
bool reductionPossible = (estSenior || estJeune);
if (reductionPossible)
{
printf("Réduction!");
}
Ce premier programme n’a pas été écrit en C/C++ mais le code devait ressembler à ceci :
// Calcule et affiche une liste de carrés
#include <iostream>
int main()
{
int i = 0; // commencer à 0
// tant que i est inférieur strict à 100 : on s’arrête quand i a atteint la valeur 100
while (i < 100)
{
cout << i << ’\t’ << i * i << ’\n’; // affiche i et son carré séparés par une
tabulation
++i; // on incrémente le nombre et on recommence
}
return 0;
}
Le premier programme jamais écrit (version while)
Les accolades ({}) délimitent le corps de la boucle : c’est-à-dire le bloc d’instructions à répéter. La
condition pour la répétition est exprimée directement dans le while. La boucle while s’exécutera 0 ou
n fois.
Il existe aussi une boucle do ... while à la différence près que cette boucle sera exécutée au moins
une fois.
// faire
do
{
cout << i << ’\t’ << i * i << ’\n’; // affiche i et son carré séparés par une tabulation
++i; // on incrémente le nombre et on recommence
}
while (i < 100); // tant que i est inférieur strict à 100
La boucle do .. while
Donc écrire une boucle est simple. Mais cela peut s’avérer dangereux :
– Que se passerait-il si i n’était pas initialisé à 0 ? Voilà une première raison qui démontre que les
variables non initialisées sont une source d’erreurs courante.
– Que se passerait-il si on oubliait l’instruction ++i ? On obtient une boucle infinie (un programme qui
ne “répond” plus). Il faut éviter au maximum d’écrire des boucles infinies. Il est conseillé d’éviter ce
type de boucle : while(1) ou while(true) qui sont des boucles infinies.
Itérer sur une suite de nombres est si courant en C/C++ que l’on dispose d’une instruction spéciale pour
le faire. C’est l’instruction for qui très semblable à while sauf que la gestion de la variable de contrôle
de boucle est concentrée sur une seule ligne plus facile à lire et à comprendre. Il existe toujours une
instruction while équivalente à une instruction for.
L’instruction for concentre : une zone d’initialisation, une zone de condition et une zone
d’opération d’incrémentation. N’utilisez while que lorsque ce n’est pas le cas.
// Calcule et affiche une liste de carrés
#include <iostream>
#include <cmath>
int main()
{
// exécute le bloc d’instructions de la boucle :
// avec i commençant à 0 (initialisation)
// tant que i est inférieur strict à 100 (condition)
// en incrémentant i après chaque exécution du bloc d’instruction (opération d’
incrémentation)
for (int i = 0; i < 100; i++)
cout << i << ’\t’ << pow(i, 2) << ’\n’; // affiche i et son carré séparés par une
tabulation
return 0;
}
Le premier programme jamais écrit (version for)
+ Ici, on utilise la fonction puissance (pow) de la bibliothèque mathématique. Pour cela, il faut inclure
math.h en C ou cmath en C++ puis effectuer l’édition des liens avec l’option -lm (ce qui est fait par
défaut maintenant).
- Les langages C/C++ nécessitent un certain niveau de maîtrise. Il faut donc pratiquer intensément. Il
n’est pas rare que les débutants commettent quelques erreurs évitables ... L’Annexe n°1 (page 27) recense
les erreurs fréquentes des débutants.
Dans le fourré
Sur l’étroit chemin qui mène à la montagne, vous parvenez à une énorme porte qui vous empêche de
passer. Les villageois vous ont prévenu que la clé de cette porte demeure dans le fourré qui se trouve juste
à côté. Celui-ci est organisé selon des cases avec des pièges et des buissons, dont on vous a fourni le plan.
Cependant, vous n’êtes pas sûr que le plan soit tout à fait exact et vous redoutez les pièges du fourré.
Vous décidez donc d’envoyer votre robot chercher la clé pour vous.
+ Ce que doit faire votre programme :
Votre programme doit diriger votre robot dans la grille suivante :
Le robot se trouve initialement à l’entrée du fourré et doit atteindre la case où se trouve la clé sans passer
par les cases où se trouvent des buissons infranchissables ni celles qui contiennent un piège . Vous n’avez
pas besoin de programmer le chemin retour.
Afin que vous puissiez manipuler le robot, nous avons créé un module appelé « robot ».
Pour disposer de ces instructions en C, vous devez inclure la ligne suivante en haut de votre programme :
#include "robot.h"
Pour déplacer le robot dans le fourré, nous proposons les quatre instructions suivantes :
– Aller en haut ;
– Aller en bas ;
– Aller à gauche ;
– Aller à droite.
Chacune demandant au robot de se déplacer d’une case dans une direction sur la grille.
En C, vous devrez les écrire comme suit :
allerHaut();
allerBas();
allerGauche();
allerDroite();
- Notez bien que le robot ne tourne pas : il se déplace de case en case sur la grille, vers le haut, le bas,
la gauche ou la droite.
+ Exemple :
Pour vous aider, voici en guise d’exemple un programme qui envoie le robot dans un buisson en trois
déplacements.
#include <stdio.h>
#include "robot.h"
int main()
{
allerHaut();
allerHaut();
allerGauche();
}
main.c
$ make
$ ./fourre
1234567
1 BBBBBBB
2 B P B
3 B B BPB
4 BRP C B
5 B BBBBB
1234567
1 BBBBBBB
2 B P B
3 BRB BPB
4 B P C B
5 B BBBBB
Vendanges
Par cette belle journée d’automne, vous accompagnez les villageois qui partent faire les vendanges. Vous
remarquez tout de suite un homme faisant de manière répétitive des allers-retours entre les cueilleurs
et la charrette qui doit être remplie à ras bord avant la fin de la journée. Cet homme tombe de fatigue
et vous lui proposez de terminer le chargement. Bien évidemment, vous allez utiliser votre robot pour
effectuer cette tâche à votre place.
+ Ce que doit faire votre programme :
Le champ est représenté ci-dessous :
Le robot est initialement tout à gauche, là où se trouve le grand tas de raisins. Il devra, 20 fois :
– ramasser des raisins pour remplir la hotte de ramassage ;
– se rendre à la charrette ;
– déposer le contenu de la hotte ;
– revenir au point de départ.
A chaque fois que du raisin a été déposé quelque part, la fonction deposer() affiche l’état de la situation :
T 1 2 3 4 5 6 7 8 9 10 11 12 13 14 C
19 . . . . . . . . . . . . . . 01
Vous disposez aussi de la fonction afficherJeu() qui affichera à tout moment l’état de la situation :
T 1 2 3 4 5 6 7 8 9 10 11 12 13 14 C
20 . . . . . . . . . . . . . . 00
+ Exemples :
#include <stdio.h>
#include "robot.h"
int main()
{
lirePosition();
afficherJeu();
return 0;
}
main.c
$ ./vendanges
position : 0 (T)
T 1 2 3 4 5 6 7 8 9 10 11 12 13 14 C
20 . . . . . . . . . . . . . . 00
-1 00 01 02 03 04 05 06 07 08 09 10
00 D 1 1 1 1 1 1 1 1 1 1
A chaque fois qu’un anneau a été déposé quelque part, la fonction deposerAnneau() affiche l’état de la
situation :
01 D 0 1 1 1 1 1 1 1 1 1
Vous disposez aussi de la fonction afficherJeu() qui affichera à tout moment l’état de la situation.
+ Exemples :
#include <stdio.h>
#include "robot.h"
int main()
{
lirePosition();
afficherJeu();
return 0;
}
main.c
$ ./anneaux
position : 0 (D)
R D 01 02 03 04 05 06 07 08 09 10
00 D 1 1 1 1 1 1 1 1 1 1
Bornes kilométriques
Le long de la route sur laquelle vous marchez se trouvent des bornes. Sur chaque borne est écrit le nombre
de kilomètres séparant la position actuelle du bout de la route. Si la borne numéro zéro se trouve derrière
vous, alors les numéros augmentent au fur et à mesure que vous avancez. Au contraire, si la borne zéro
se trouve devant vous, alors les numéros diminuent au fur et à mesure que vous avancez.
Vous avez noté le numéro de la borne de laquelle vous êtes parti(e) ce matin, ainsi que le numéro de la
borne à laquelle vous êtes arrivé(e) ce soir. Vous souhaitez calculer la distance que vous avez parcourue
dans la journée.
+ Ce que doit faire votre programme :
Votre programme doit lire deux entiers, correspondant à deux numéros de bornes kilométriques, et il doit
afficher la distance séparant ces deux bornes. Notez que le résultat doit être un nombre positif ou nul.
+ Exemples :
$ gcc bornes-kilometriques.c -o bornes-kilometriques
Ou :
$ ./bornes-kilometriques
152
189
Distance : 37
$ ./bornes-kilometriques
814
786
Distance : 28
Question 5. Tester les 2 exemples donnés ci-dessus. Quel est le test qui manque pour valider le
programme ?
Bagarre générale
À peine arrivé dans le village, voilà qu’une bagarre générale est sur le point d’éclater ! Tout en vous
mettant à l’abri, vous tâchez de savoir ce qui se passe. On vous explique que le village est principalement
composé de deux grandes familles rivales qui ne se supportent pas. Tout sujet étant une source de
discorde possible, ils avaient décidé que les superficies de leurs champs respectifs ne devaient pas être
trop différentes afin de ne pas attiser la jalousie de la famille opposée. Mais voilà que le patriarche des
Arignon suspecte qu’un des champs des Evaran est trop grand ! Vous décidez de les aider ; mais la tâche
ne sera pas facile, chacun gardant jalousement secrète la superficie réelle de ses champs.
+ Ce que doit faire votre programme :
Votre programme devra lire deux entiers, la superficie d’un champ des Arignon et la superficie d’un
champ des Evaran. Si l’un des champs est plus grand d’au moins 10 m2 (strictement) que l’autre champ,
alors il faudra afficher le texte « La famille X a un champ trop grand », « X » devant bien sûr être
remplacé par « Arignon » ou « Evaran » selon le cas.
+ Exemples :
$ ./bagarre-generale
42
54
La famille Evaran a un champ trop grand
$ ./bagarre-generale
10
20
Question 7. Tester les 2 exemples donnés ci-dessus. Quel est le test qui manque pour valider le
programme ?
Calculatrice
Vous êtes dans la boutique du plus grand marchand de la ville, à la recherche d’un certain nombre
d’ingrédients. Malheureusement pour vous, c’est la période du grand inventaire et cela peut durer très
longtemps ! Vous décidez de les aider en programmant une calculatrice.
+ Ce que doit faire votre programme :
Votre programme devra lire deux nombres entiers et une lettre représentant l’opération à effectuer (’s’
pour la somme et ’p’ pour le produit) et affichera le résultat de l’opération.
+ Exemples :
$ ./calculatrice
5 4 s
9
$ ./calculatrice
2 3 p
6
Graduation de thermomètres
Les habitants du village utilisent beaucoup de thermomètres différents : certains pour l’été, d’autres pour
l’hiver, d’autres pour la température de l’eau, etc. Ce qui change d’un thermomètre à l’autre, ce sont
les valeurs de la température minimale et de la température maximale lisibles sur la graduation. Les
habitants aimeraient pouvoir imprimer facilement la graduation des températures possibles pour chaque
thermomètre.
+ Ce que doit faire votre programme :
Étant données deux températures entières tempMin et tempMax, votre programme doit afficher toutes les
températures comprises entre les deux bornes incluses.
+ Attention, la saisie des deux bornes peut être inversée. Vous devez vous assurer que tempMin et
tempMax soient valides avant d’afficher les graduations.
+ Exemples :
$ ./graduation-thermometres
9
14
9
10
11
12
13
14
$ ./graduation-thermometres
14
9
9
10
11
12
13
14
Question 10. Écrire le programme calcul-denivelees.c en utilisant une boucle Tant Que (while).
Tester et valider.
poids est celui d’un joueur de la 1re équipe, le deuxième poids celui d’un joueur de la 2e équipe, le
troisième la 1re équipe, le quatrième la 2e équipe, etc.
Après avoir calculé le poids total de chaque équipe, vous devrez afficher le texte « L’équipe X a un
avantage » (en remplaçant X par la valeur 1 ou 2), en considérant qu’une équipe est avantagée si elle a
un poids total supérieur à celui de l’autre ou « Aucune équipe a un avantage » si les deux totaux sont
identiques.
Vous afficherez ensuite le texte « Poids total pour l’équipe 1 : » suivi du poids de l’équipe 1, puis « Poids
total pour l’équipe 2 : » suivi du poids de l’équipe 2 (voir l’exemple ci-dessous).
+ Exemples :
$ ./tir-corde
3
40
80
50
50
60
10
L’équipe 1 a un avantage
Poids total pour l’équipe 1 : 150
Poids total pour l’équipe 2 : 140
- Chaque équipe est composée de trois joueurs. Ceux de la première pèsent 40, 50 et 60 kg, tandis que
ceux de la seconde font 80, 50 et 10 kg. Cela fait 150 kg opposés à 140 kg.
Question 11. Écrire le programme tir-corde.c en utilisant une boucle Pour (for).
Type d’arbres
Alors que vous traversez une forêt vous ne pouvez vous empêcher d’admirer la végétation autour de vous
et notamment les nombreuses espèces d’arbres. Malgré votre intérêt, vous êtes très mauvais botaniste et
avez beaucoup de mal à identifier les différents arbres. Une personne que vous croisez vous donne quelques
indications et vous décidez d’écrire un programme qui vous donnera le nom de l’arbre en fonction de ses
caractéristiques.
+ Ce que doit faire votre programme :
Il existe 4 types d’arbres :
– le "Tinuviel" fait moins de 5 mètres de haut et ses feuilles sont composées de plus de 8 folioles
– le "Calaelen" fait plus de 10 mètres de haut et ses feuilles sont composées de plus 10 folioles
– le "Falarion" fait moins de 8 mètres de haut et ses feuilles sont composées de moins de 5 folioles
– le "Dorthonion" fait plus de 12 mètres de haut et ses feuilles sont composées de moins de 7 folioles
Votre programme lira deux entiers, la hauteur et le nombre de folioles de l’arbre, et affichera le nom de
l’arbre correspondant.
- Toutes les inégalités sont à prendre au sens large, c’est-à-dire que "moins" signifie "moins ou égal" ou
et "plus" signifie "plus ou égal".
+ Exemples :
$ ./type-arbre
12
12
Calaelen
$ ./type-arbre
4
9
Tinuviel
$ ./type-arbre
9
4
Inconnu
- Lorsque cela n’est pas précisé explicitement, les inégalités sont au sens large (c’est-à-dire que "moins"
signifie "moins ou égal" ou et "plus" signifie "plus ou égal").
Vous devez vérifier tous les critères. S’ils sont vérifiés tous les 5, vous devez afficher « Très probable ». Si
seulement 3 ou 4 sont vérifiés, vous devez afficher « Probable ». Si aucun n’est vérifié, vous devez afficher
« Impossible », et dans les autres cas, vous devez afficher « Peu probable ».
+ Exemples :
$ ./espion-demasque
180
40
65
0
1
Très probable
- Vous utiliserez des variables booléenes. Vous pouvez aussi faire preuve de simplicité dans le calcul des
critères.
Question 15. Écrire le programme espion-demasque.c. Tester et valider.
- Il est fortement conseillé de lire l’Annexe n°2 (page 29) qui évoque les problèmes concernant les
nombres réels (float et double).
Question 16. Écrire le programme moyenne-notes.c.
Tarif du bateau
Vous venez d’apprendre qu’un concert de rock va se dérouler ce soir dans la ville située de l’autre côté du
lac. Vous avez tout juste le temps d’y arriver en prenant le bateau. Cependant, vous n’êtes pas le seul à
vouloir y aller, et il y a une longue queue pour acheter les tickets pour la traversée.
Comme il n’y a qu’une seule personne qui s’occupe à la fois de vendre les tickets et d’aider les gens à
monter sur le bateau, vous vous rendez vite compte que vous allez certainement arriver en retard. La
vente des tickets est particulièrement lente car il faut demander aux gens leur âge pour savoir s’ils doivent
payer plein tarif ou bien tarif réduit.
Vous proposez que votre robot s’occupe de la vente des billets et que la personne responsable du bateau
s’occupe uniquement d’aider les gens à monter sur le bateau. Il va donc vous falloir programmer votre
robot assez vite pour arriver à temps au concert.
+ Ce que doit faire votre programme :
Votre programme doit lire l’âge d’une personne et le nombre de traversées indiqué sur sa carte et afficher
soit « Tarif réduit », soit « Tarif plein », soit « Gratuit » suivant les cas. Vous avez le droit à une
réduction si vous avez entre 12 et 25 ans (pour les jeunes) ou si vous avez plus de 60 ans (pour lres
seniors) mais, si vous n’avez pas de réduction et que vous faites plus de 50 traversées par an, alors vous
avez le droit à une traversée gratuite.
+ Exemples :
$ ./tarif-bateau
27
10
Tarif plein
$ ./tarif-bateau
22
15
Tarif réduit
La roue de la fortune
Lors de l’examen final à la fin de leurs études, la tradition veut que les élèves tirent un sujet au hasard.
Tirer un numéro dans un chapeau n’étant pas très amusant, ils utilisent une roue comme celle-ci, qu’ils
peuvent faire tourner dans un sens ou dans l’autre.
Les enseignants, par expérience, arrivent toujours à estimer de combien de "zones" la roue va tourner et
peuvent aller chercher le sujet et revenir, pendant que la roue tourne encore. Il faut, pour cela, calculer
rapidement sur quelle zone le curseur va s’arrêter, en fonction du nombre de zones dont la roue va tourner.
A vous de jouer !
$ ./roue-fortune
25
1
$ ./roue-fortune
-50
22
- L’opérateur modulo % retourne le reste de la division euclidienne. Cet opérateur est très utilisé en
informatique (nombre pair ou impair, multiple d’un nombre, intervalle, tampon circulaire ...). Le modulo
sur 6 donnera toujours un nombre compris entre 0 et 5 par exemple :
0 % 6 retourne 0
1 % 6 retourne 1
2 % 6 retourne 2
6 % 6 retourne 0
7 % 6 retourne 1
8 % 6 retourne 2
...
Bilan
Vous avez découvert les notions suivantes :
– lire des entrées et produire des résultats en sortie
– enchaîner des suites d’instructions
– répéter (itérer) des instructions
– manipuler des variables et faire des opérations avec
– effectuer des actions sous conditions
Conclusion : parce qu’il est clair que vous débutez à peine votre carrière de programmeur, n’oubliez pas
qu’écrire de bons programmes, c’est écrire des programmes corrects, simples et efficaces.
Rob Pike (ancien chercheur des Laboratoires Bell et maintenant ingénieur chez Google) :
« Règle n°4 : Les algorithmes élégants comportent plus d’erreurs que ceux qui sont plus
simples, et ils sont plus difficiles à appliquer. Utilisez des algorithmes simples ainsi que des
structures de données simples. »
Cette règle n°4 est une des instances de la philosophie de conception KISS (Keep it Simple, Stupid dans
le sens de « Ne complique pas les choses ») ou Principe KISS, dont la ligne directrice de conception
préconise de rechercher la simplicité dans la conception et que toute complexité non nécessaire devrait
être évitée.
printf("Hello World!\n");
// ce qui correspond à :
fprintf(stdout, "Hello World!\n");
// ou :
write(1, "Hello World!\n"); // 1 étant le descripteur de stdout
Pour afficher une erreur, il est recommandé de la faire dans le flux stderr :
// En C/C++
fprintf(stderr, "Ceci est une erreur.\n");
// En C++
cerr << "Ceci est une erreur.\n";
- Si on ne désire pas l’affichage des messages d’erreur d’un programme, il suffit de rediriger son flux
stderr vers le périphérique null. Par exemple pour le programme [Link] :
$ ./[Link] 2> /dev/null
La fonction printf() (ainsi que sprintf() et fprintf() prend en argument une chaîne de format qui
spécifie les types de ses autres arguments. La chaîne de format de printf() est riche en possibilités, car
elle indique non seulement le type des arguments mais aussi la manière de les écrire. Par exemple, vous
pouvez choisir le nombre de chiffres après la virgule d’un nombre flottant. Chaque code spécial de la
chaîne de format est précédé d’un %. Si vous voulez écrire le symbole %, vous devez l’écrire deux fois : %%.
printf("%d\n", 42); // affichage d’un entier
printf("Taux de réussite : %.1f %%\n", 92.9); // Taux de réussite : 92.9 %
Chaque code spécial est découpé en plusieurs sections (les sections entre crochets sont facultatives) :
%[largeur][.precision]type
1. Largeur : nombre minimum de caractères à écrire, printf() complètera si nécessaire par des espaces
sur la gauche, la valeur est alignée à droite.
2. Précision : nombre de chiffres après la virgule à écrire pour les nombres flottants, précédé d’un point.
La valeur est automatiquement arrondie : le dernier chiffre est incrémenté s’il y a besoin.
3. Type : le type de la valeur à écrire
Voici les différents types utilisables :
- La lecture des chaînes de caractère (avec scanf() en C/C++ ou cin en C++) se termine sur ce
qu’on appelle un espace blanc (whitespace), c’est-à-dire le caractère espace, une tabulation ou un caractère
de retour à la ligne (généralement la touche Enter ). Notez que les espaces sont ignorés par défaut. Sous
Linux, vous pouvez indiquer la fin d’un flux (EOF) en combinant les touches Ctrl + d qui indiquera
qu’il n’y a plus de saisie. Vous pouvez aussi stopper le programme avec Ctrl + z ou l’interrompre
avec Ctrl + c .
La chaîne de format se compose de codes spéciaux indiquant le type des valeurs à lire. Chaque code est
précédé du symbole %. Il en existe une multitude, nous nous contenterons ici de ceux dont vous aurez
besoin :
%d int entier 32 bits en décimal
%lld long long int entier 64 bits
%f float nombre réel simple précision
%lf double nombre réel double précision
%c char caractère unique
%s char* chaîne de caractères
+ man 3 scanf
int a = 0;
if(a != 0); /* c’est sûrement un bug involantaire */
printf("bug : a n’est pas nul et pourtant a = %d !\n", a);
, Le bug du débutant n°2 : il ne faut pas confondre l’opérateur ’=’ (d’affectation) avec l’opérateur ’==’
(comparaison d’égalité). Cela risque de donner un comportement défectueux :
int a = 0;
if(a = 0) /* c’est sûrement un bug involantaire */
printf("a = %d : a est égal à 0\n", a);
else printf("bug : a n’est pas égal à 0 et pourtant a = %d !\n", a);
, Le bug du débutant n° 3 : il faut penser à déterminer si un break est nécessaire dans un case. Le code
suivant le prouve :
a = 1;
switch (a)
{
case 1: printf("a = %d : a est égal à 1\n", a); /* ne faudrait-il pas un break ? */
case 2: printf("bug : a = %d : a est égal à 2 !\n", a);
}
Vous obtiendrez :
a = 1 : a est égal à 1
bug : a = 1 : a est égal à 2 !
, Le bug du débutant n°4 : il ne faut pas mettre un ; après le while car cela exécuterait une instruction
nulle si expression est vraie. Le code suivant sera dans une boucle infinie sans afficher aucun message
"Salut" :
long compteur = 15;
while (compteur > 0); // c’est sûrement un bug involantaire
{
printf("Salut\n");
compteur = compteur - 1;
} // ici non plus ne pas mettre de ;
, Le bug du débutant n°5 : il ne faut pas mettre un ; à la fin d’un for car cela exécuterait une instruction
nulle si expression est vraie. Le code suivant n’affichera qu’un seul message "Salut" car le for boucle
dans le vide :
long compteur;
for (compteur = 0; compteur < 15; compteur++); // c’est sûrement un bug involantaire
{
printf("Salut\n");
}
, Le bug du débutant n°6 : il ne faut pas oublier les accolades {} à la suite d’une instruction conditionnelle
if/else ou itérative for/while sinon vous n’exécuterez qu’un seule instruction au lieu de plusieurs. Par
exemple :
Si temperature >= 100
Écrire "L’eau bout !"
Écrire "Préparons le thé"
if (x == 2.)
printf("OK\n");
On s’attend à voir ce programme écrire OK. Il n’en est rien ! À cause des pertes de précision, x n’est
pas égal à 2 à la fin du programme, mais à un nombre presque égal à 2 et différent de 2, comme
2.0000000000000004.
On ne peut donc pas tester l’égalité entre deux nombres réels.
Pour déterminer si deux nombres réels sont égaux, il faut commencer par calculer la différence des deux
nombres. Si la différence est très proche de zéro, c’est-à-dire si la valeur absolue de la différence est très
petite, alors on considère que les nombres sont égaux.
Concrètement, pour comparer x et y, on commence par définir une constante très petite que l’on nomme
traditionnellement epsilon, et on teste si |x - y| < epsilon. La fonction fabs() calcule la valeur absolue.
#include <math.h>
double x = 2.;
x = sqrt(x); // Racine carrée de x
x = x * x; // x^2