Vous êtes sur la page 1sur 40

TP Développement C++

© 2012 tv <tvaira@free.fr> - v.1.0

Sommaire
Première partie : Le premier programme 3
Hello world . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Explications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Compilation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
Édition des liens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
Environnement de programmation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
Manipulations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
Objectifs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
Étape n°1 : création de votre espace de travail . . . . . . . . . . . . . . . . . . . . . . . . 8
Étape n°2 : édition du programme source . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
Étape n°3 : vérification du bon fonctionnement du compilateur . . . . . . . . . . . . . . . 9
Étape n°4 : placement dans le bon répertoire . . . . . . . . . . . . . . . . . . . . . . . . . 9
Étape n°5 : fabrication (enfin !) du premier programme . . . . . . . . . . . . . . . . . . . 10
Questions de révision . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Exercice 1 : corriger des erreurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
Exercice 2 : faire évoluer un programme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
Bilan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

Deuxième partie : Structure d’un programme informatique simple 14


Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
Objets, types et valeurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
Entrée . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
Opérations et opérateurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
Règles de codage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
Affectation et initialisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Questions de révision . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
Exercice 3 : calcul de notes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
Bilan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

1
SOMMAIRE

Troisième partie : Programmation modulaire 24


Objectifs et outils . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
Expressions et instructions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
Constantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
Références et pointeurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
Fonctions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
Règles de codage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
Questions de révision . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
Exercice 4 : programmation modulaire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
Exercice 5 : passage par adresse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
Exercice 6 : passage par référence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
Bilan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

Les objectifs de ce tp sont de comprendre et mettre en oeuvre la fabrication d’un pro-


gramme simple. Beaucoup de conseils sont issus du livre de référence de Bjarne Stroustrup
(www.programmation.stroustrup.pearson.fr).
Remarque : les tp ont pour but d’établir ou de renforcer vos compétences pratiques. Vous pouvez penser
que vous comprenez tout ce que vous lisez ou tout ce que vous a dit votre enseignant mais la répétition et
la pratique sont nécessaires pour développer des compétences en programmation. Ceci est comparable au
sport ou à la musique ou à tout autre métier demandant un long entraînement pour acquérir l’habileté
nécessaire. Imaginez quelqu’un qui voudrait disputer une compététion dans l’un de ces domaines sans
pratique régulière. Vous savez bien quel serait le résultat.

TP Développement C++ 2 / 40 © 2012 tv <tvaira@free.fr>


PREMIÈRE PARTIE : LE PREMIER PROGRAMME

Première partie : Le premier programme

Hello world
Voici une version du premier programme que l’on étudie habituellement. Il affiche “Hello world !” à
l’écran :
// Ce programme affiche le message "Hello world !" à l’écran

#include <iostream>

using namespace std;

int main()
{
cout << "Hello world !\n"; // Affiche "Hello world !"

return 0;
}
Hello world (version 1) en C++

Explications
Tout programme C/C++ doit posséder une fonction nommée main (principale) pour indiquer où
commencer l’exécution. Une fonction est essentiellement une suite d’instructions que l’ordinateur
exécutera dans l’ordre où elles sont écrites.

Une fonction comprend quatre parties :


– un type de retour : ici int (pour integer ou entier) qui spécifie le genre de résultat que la fonction
retournera lors de son exécution. En C/C++, le mot int est un mot réservé (un mot-clé) : il ne peut
donc pas être utilisé pour nommer autre chose.
– un nom : ici main
– une liste de paramètres entre parenthèses (que l’on verra plus tard) : ici la liste de paramètres est
vide
– un corps de fonction entre accolades qui énumère les instructions que la fonction doit exécuter
Remarque : la plupart des instructions C/C++ se terminent par un point-virgule (;).

En C/C++, les chaînes de caractères sont délimitées par des guillements anglais ("). "Hello
world !\n" est donc une chaîne de caractères. Le code \n est un “caractère spécial” indiquant le
passage à une nouvelle ligne (newline).

Le nom cout (character output stream) désigne le flux de sortie standard (l’écran par défaut). Les
caractères “placés dans cout” au moyen de l’opérateur de sortie « aparaîtront à l’écran.

// Affiche "Hello world !" placé en fin de ligne est un commentaire. Tout ce qui est écrit après
// sera ignoré par le compilateur (la machine). Ce commentaire rend le code plus lisible pour les
programmeurs. On écrit des commentaires pour décrire ce que le programme est supposé faire et, d’une
manière générale, pour fournir des informations utiles impossibles à exprimer directement dans le code.

TP Développement C++ 3 / 40 © 2012 tv <tvaira@free.fr>


PREMIÈRE PARTIE : LE PREMIER PROGRAMME

La première ligne du programme est un commentaire classique : il indique simplement ce que le programme
est censé faire (et pas ce que nous avons voulu qu’il fasse !). Prenez donc l’habitude de mettre ce type de
commentaire au début d’un programme.

La fonction main de ce programme retourne la valeur 0 (return 0;) à celui qui l’a appelée. Comme
main() est appelée par le “système”, il recevra cette valeur. Sur certains systèmes (Unix/Linux), elle peut
servir à vérifier si le programme s’est exécuté correctement. Un zéro (0) indique alors que le programme
s’est terminé avec succès (c’est une convention UNIX). Évidemment, une valeur différente de 0 indiquera
que le programme a rencontré une erreur. Et sa valeur précisera alors le type de l’erreur.

En C/C++, une ligne qui commence par un # fait référence à une directive du préprocesseur (ou de pré-
compilation). Le préprocesseur ne traite pas des instructions C/C++ (donc pas de ";"). Ici, la directive
#include <iostream> demande à l’ordinateur de rendre accessible (d’“inclure”) les fonctionnalités
contenues dans un fichier nommé iostream. Ce fichier est fourni avec le compilateur et nous permet
d’utiliser cout et l’opérateur de sortie « dans notre programme.
Un fichier inclus au moyen de #include porte généralement l’extension .h. On l’appelle en-tête (header)
ou fichier d’en-tête.
Remarque : En C++, il est maintenant inutile d’ajouter l’extension .h pour les fichiers d’en-tête standard.

La ligne using namespace std; indique que l’on va utiliser l’espace de nom std par défaut.
Remarque : cout (et cin) existe dans cet espace de nom mais pourrait exister dans d’autres espaces de
noms. Le nom complet pour y accéder est normalement std::cout. L’opérateur :: permet la résolution de
portée en C++ (un peu comme le / dans un chemin !).
Pour éviter de donner systématiquement le nom complet, on peut écrire le code ci-dessous. Comme on
utilise quasiment tout le temps des fonctions de la bibliothèque standard, on utilise presque tout le temps
" using namespace std; " pour se simplifier la vie !

TP Développement C++ 4 / 40 © 2012 tv <tvaira@free.fr>


PREMIÈRE PARTIE : LE PREMIER PROGRAMME

Compilation
C++ (ou C) est un langage compilé. Cela signifie que, pour pouvoir exécuter un programme, vous devez
d’abord traduire sa forme lisible par un être humain en quelque chose qu’une machine peut “comprendre”.
Cette traduction est effectuée par un programme appelé compilateur.
Ce que le programmeur écrit est le code source (ou programme source) et ce que l’ordinateur exécute
s’appelle exécutable, code objet ou code machine.

Vous allez constater que le compilateur est plutôt pointilleux sur la syntaxe ! Des manipulations de ce
TP sont consacrées à découvrir cette syntaxe du langage C++. Comme tous les programmeurs, vous
passerez beacoup de temps à chercher des erreurs dans du code source. Et la plupart de temps, le code
contient des erreurs ! Lorsque vous coderez, le compilateur risque parfois de vous agacer. Toutefois, il a
généralement raison car vous avez certainement écrit quelque chose qui n’est pas défini précisément par
la norme C++ et qu’il empêche de produire du code objet.
Remarque : Le compilateur est dénué de bon sens et d’intelligence (il n’est pas humain) et il est donc très
pointilleux. Prenez en compte les messages d’erreur et analysez les bien car souvenez-vous en bien le
compilateur est “votre ami”, et peut-être le meilleur que vous ayez lorsque vous programmez.

TP Développement C++ 5 / 40 © 2012 tv <tvaira@free.fr>


PREMIÈRE PARTIE : LE PREMIER PROGRAMME

Édition des liens


Un programme contient généralement plusieurs parties distinctes, souvent développées par des personnes
différentes. Par exemple, le programme “Hello world !” est constitué de la partie que nous avons écrite,
plus d’autres qui proviennent de la bibliothèque standard de C++ (cout par exemple).
Ces parties distinctes doivent être liées ensemble pour former un programme exécutable. Le programme
qui lie ces parties distinctes s’appelle un éditeur de liens (linker).

Remarque : notez que le code objet et les exécutables ne sont pas portables entre systèmes. Par exemple,
si vous compilez pour une machine Windows, vous obtiendrez un code objet qui ne fonctionnera pas sur
une machine Linux.

Une bibliothèque n’est rien d’autre que du code (qui ne contient pas de fonction main évidemment)
auquel nous accédons au moyen de déclarations se trouvant dans un fichier d’en-tête. Une déclaration
est une suite d’instruction qui indique comment une portion de code (qui se trouve dans une bibliothèque)
peut être utilisée. Le débutant a tendance à confondre bibliothèques et fichiers d’en-tête.

Remarque : Une bibliothèque dynamique est une bibliothèque qui contient du code qui sera intégré au
moment de l’exécution du programme. Les avantages sont que le programme est de taille plus petite et
qu’il sera à jour vis-à-vis de la mise à jour des bibliothèques. L’inconvénient est que l’exécution dépend
de l’existence de la bibliothèque sur le système cible. Une bibliothèque dynamique, Dynamic Link Library
(.dll) pour Windows et shared object (.so) sous UNIX/Linux, est un fichier de bibliothèque logicielle
utilisé par un programme exécutable, mais n’en faisant pas partie.
Les erreurs détectées par le compilateur sont des erreurs de compilation, celles que trouvent l’éditeur de
liens sont des erreurs de liaisons (ou erreurs d’édition de liens). Et celles qui se produiront à l’exécution
seront des erreurs d’exécutions ou de “logique” (communément appelées bugs). Généralement, les erreurs
de compilation sont plus faciles à comprendre et à corriger que les erreurs de liaison, et les erreurs de
liaison sont plus faciles à comprendre et à corriger que les erreurs d’exécution et les erreurs de logique.

TP Développement C++ 6 / 40 © 2012 tv <tvaira@free.fr>


PREMIÈRE PARTIE : LE PREMIER PROGRAMME

Environnement de programmation
Pour programmer, nous utilisons un langage de programmation. Nous utilisons aussi un compilateur pour
traduire le code source en code objet et un éditeur de liens pour lier les différentes portions de code objet
et en faire un programme exécutable. De plus, il nous faut un programme pour saisir le texte du code
source (un éditeur de texte à ne pas confondre avec un traitement de texte) et le modifier si nécessaire.
Ce sont là les premiers éléments essentiels de ce qui constitue la boîte à outils du programmeur que
l’on appelle aussi environnement de développement.
Si vous travaillez dans une fenêtre en mode ligne de commande (appelée parfois “mode console”), comme
c’est le cas de nombreux programmeurs professionnels, vous devez taper vous-mêmes les différentes
commandes pour produire un exécutable et le lancer.

Figure 1 – Exemple de développement sur la console

Si vous travaillez dans un Environnement de Développement Intégré ou EDI, comme c’est aussi le cas
de nombreux programmeurs professionnels, un simple clic sur le bouton approprié suffira.

Figure 2 – Exemple de développement avec l’EDI Qt Creator

Un EDI (ou IDE pour Integrated Development Environment) peut contenir de nombreux outils comme :
la documentation en ligne, la gestion de version et surtout un débogueur (debugger) qui permet de
trouver des erreurs et de les éléminer.
Remarque : Il existe de nombreux EDI (ou IDE) pour le langage C/C++ et on en utilisera certains
notamment en projet. On peut citer : Visual C++, Builder, Qt Creator, Code::Blocks, devcpp, eclipse, etc
... Ils peuvent être très pratique mais ce ne sont que des outils et l’apprentissage du C/C++ ne nécessite
pas forcément d’utiliser un EDI. Ils améliorent surtout la productivité dans un cadre professionnel.

TP Développement C++ 7 / 40 © 2012 tv <tvaira@free.fr>


PREMIÈRE PARTIE : LE PREMIER PROGRAMME

Manipulations
Objectifs

L’objectif de cette partie est la mise en oeuvre de la chaîne de fabrication g++ sous Linux.

Étape n°1 : création de votre espace de travail

Dans votre répertoire personnel, créez un répertoire "C-C++" où vous stockerez l’ensemble des vos tp.
Entrez dans ce répertoire et faites un nouveau répertoire dedans nommé "tp1" où vous stockerez vos
travaux pour ce premier TP.
Pour réaliser cela, vous pouvez soit utiliser l’interface graphique avec l’explorateur de fichiers
(dolphin par exemple) soit utiliser une console (Menu → − Outils →
− Konsole pour une environnement
KDE) en tapant simplement les commandes suivantes :
$ mkdir C-C++
$ cd C-C++
$ mkdir tp1
$ cd tp1

Étape n°2 : édition du programme source

À l’aide d’un éditeur de texte (vi, vim, emacs, kwrite, kate, gedit sous Linux ou Notepad, Notepad++,
UltraEdit sous Windows, ...), tapez (à la main, pas de copier/coller, histoire de bien le lire et de s’habituer
à la syntaxe !) le programme suivant dans un fichier que vous nommerez "helloWorld.cpp" :
#include <iostream>

using namespace std;

int main()
{
int decompte = 5;

while(decompte > 0)
{
cout << "Hello ";
cout << "world" << " !" << endl;
decompte = decompte - 1;
}

return 0;
}
Hello world (version 2) en C++

TP Développement C++ 8 / 40 © 2012 tv <tvaira@free.fr>


PREMIÈRE PARTIE : LE PREMIER PROGRAMME

Étape n°3 : vérification du bon fonctionnement du compilateur

Tout d’abord ouvrez une console (Menu →− Outils →− Konsole pour une environnement KDE) et vérifiez
que le compilateur C++ est bien installé en tapant simplement la commande suivante :
$ g++

Si cela vous répond "g++: no input file" ou "g++: pas de fichier à l’entrée", alors tout va bien :
votre GNU/Linux a bien réussi à exécuter le compilateur... et ce dernier se plaint juste que vous ne lui
avez pas dit quoi compiler.
L’installation du compilateur est bien faite et vous pourrez continuer.
Si au contraire GNU/Linux vous répond (en français par exemple) "bash: g++ : commande introuvable".
Alors c’est que l’interpréteur de commandes (bash) n’est pas en mesure de trouver le compilateur installé
ou qu’il n’est pas du tout installé. Demandez alors l’aide de l’enseignant.

Étape n°4 : placement dans le bon répertoire

Avant de pouvoir compiler notre premier programme, il nous faut tout d’abord nous déplacer dans la
console (la fenêtre noire) pour nous placer dans le repertoire où se trouve le fichier "helloWorld.cpp" créé
précédemment. Pour cela, appprendre quelques commandes bash est nécessaire. La première commande
à connaitre est "ls" ("ls -l" pour avoir plus de détails) qui affiche le contenu du répertoire dans lequel
vous vous trouvez actuellement. Essayez ! La commande équivalente sous Windows est "dir".
La deuxième commande à connaitre est "cd" qui change le répertoire où vous vous trouvez : par exemple,
pour aller dans le sous-répertoire "C-C++", vous allez taper :
$ cd C-C++

Remarquez que l’invite de commande (en anglais on appelle ça le "prompt", c’est-à-dire le message qui
précède le curseur sur la dernière ligne qui vient d’apparaitre) contient toujours le nom du répertoire
courrant.
Recommencer l’opération cette fois pour rentrer dans le sous-répertoire "tp1" :
$ cd tp1

Si vous voulez remonter d’un niveau, rien de plus simple car le nom de répertoire ".." indique, où que
vous soyez, le répertoire qui se trouve immédiatement au dessus. On l’appelle le répertoire parent.
$ cd ..

Un autre nom de répertoire particulier est "." : c’est le répertoire dans lequel vous êtes actuellement. On
l’appelle le répertoire courant.
Souvenez-vous que sous Linux, il faut taper "./helloWorld" pour lancer l’exécution du programme
"helloWorld" (sous Linux, les fichiers exécutables n’ont pas d’extension particulière contrairement à
Windows qui possède l’extension ".exe" pour les programmes). En fait cela signifie : "dans le répertoire
courant" / "le fichier helloWorld".
A votre avis, quel est le résultat de la commande suivante :
$ cd .

Pourquoi ?
Vous avez peut être remarqué qu’une barre oblique sépare les répertoires, et qu’elle n’est pas dans le
même sens sous Windows "\" et Linux "/" ? Apprennez à les repérer et à ne pas vous tromper ! Au lieu
de faire deux fois la commande "cd", on aurait pu aller directement au bon endroit en une seule fois :

TP Développement C++ 9 / 40 © 2012 tv <tvaira@free.fr>


PREMIÈRE PARTIE : LE PREMIER PROGRAMME

cd C-C++\tp1 Sous Windows


cd C-C++/tp1 Sous Linux

Faites maintenant "ls -l" (ou "dir" et vérifiez que votre fichier "helloWorld.cpp" apparait bien dans
la liste. Si ce n’est pas le cas, appelez l’enseignant à l’aide.

Étape n°5 : fabrication (enfin !) du premier programme

Cela se fait comme vu en cours avec les deux commandes suivantes :


g++ -Wall -c helloWorld.cpp

qui réalise le "pré-processing", la compilation et l’assemblage du fichier source "helloWorld.cpp"


en un fichier objet "helloWorld.o" dans le même repertoire. Faites "ls -l" pour vérifier que le fichier
"helloWorld.o" a bien été créé.
Vous devez ensuite faire :
g++ -Wall -o helloWorld helloWorld.o

qui réalise l’édition des liens entre les divers fichiers ".o" (unification des variables et des fonctions
contenues dans ces différents fichiers), puis qui produit le fichier exécutable "helloWorld".
Vous pouvez maintenant démarer le programme, sous Linux, souvenez-vous qu’il faut indiquer que le
programme se trouve dans le repertoire courant en tapant : "./helloWorld" pour le démarrer.
./helloWorld

Questions de révision
L’idée de base des questions de révision est de vous donner une chance de voir si vous avez identifié et
compris les points clés de cette partie.

Question 1. Quel est le but du programme “Hello world !” ?

Question 2. Quelles sont les quatre parties d’une fonction ?

Question 3. Citez une fonction qui doit apparaître dans tout programme C ou C++.

Question 4. Dans le programme “Hello world !”, à quoi sert la ligne return 0; ?

Question 5. Quel est le rôle du compilateur ?

Question 6. À quoi sert la directive #include ?

Question 7. Que signifie l’extension .h à la fin d’un nom de fichier en C/C++ ?

Question 8. Que fait l’éditeur de liens pour votre programme ?

Question 9. Quelle est la différence entre un fichier source et un fichier objet ?

Question 10. Qu’est-ce qu’un environnement de développement intégré (EDI) et que fait-il pour vous ?

TP Développement C++ 10 / 40 © 2012 tv <tvaira@free.fr>


PREMIÈRE PARTIE : LE PREMIER PROGRAMME

Exercice 1 : corriger des erreurs


L’objectif de cet exercice est d’apprendre à interpréter les erreurs signalées par le compilateur.
Question 11. Éditez le fichier "aCorriger.cpp" ci-dessous et tentez de le compiler. Quel compilateur
faut-il utiliser ? g++ ou gcc ?

/Qu’est censé faire ce programme ?

// auteur : e. remy

include <iostrem>

using namespace std;

integer main()
{
/* Vous devez corriger ce programme et arriver à le compiler puis l’exécuter.
cout << ’Le programme marche !’ << end;
int valeur = 10
cout << "valeur =" << valeur << end;
// Attention : la division de deux entiers est une division euclidienne,
// c’est-à-dire une division ***ENTIERE*** !
int quotient = 10 / 3
cout << "quotient=" << quotient << end;
int reste = 10 % 3
cout << "reste=" << reste << end;
// Si vous voulez faire une division réelle, il faut convertir un des
// arguments en réel :
out << "quotient reel =" << valeur / 3.0 <<end; // Cette fois-ci 3.0 est réel
out << "Fin du programme;
return 0;
}
Un fichier source truffé d’erreurs !

La compilation échoue car le fichier est truffé d’erreurs !


Question 12. Corrigez-les jusqu’à obtenir le bon fonctionnement du programme. Puis, ajouter le
commentaire classique au début du programme source.

Quelques consignes importantes :


– Vous pouvez commencer par tenter de corriger toutes les erreurs que vous trouver par vous-même en
lisant le programme... mais au delà, c’est au compilateur de vous dire où sont les erreurs de syntaxe.
– Ne considérez que la première erreur signalée par le compilateur : les suivantes peuvent être une
conséquence de la mauvaise compréhension de la suite du programme par le compilateur à cause de
cette première erreur... il faut donc la corriger en premier ! Une fois qu’elle est corrigée, essayez de
recompiler pour voir si vous avez bien corrigé, et s’il reste d’autres erreurs... La programmation est
une véritable école de patience !
– Utilisez le numéro de ligne indiqué par le compilateur. Soit l’erreur se trouve à la ligne indiquée... soit
un peu avant : le numéro de ligne indiqué est l’endroit où il devient manifeste pour le compilateur que
le programme est erroné... mais des fois vous avez pu écrire une bêtise qui n’est pas litéralement fausse
et donc que le compilateur accepte pendant quelques lignes... jusqu’à ce que cela devienne clair qu’il y
a un problème !

TP Développement C++ 11 / 40 © 2012 tv <tvaira@free.fr>


PREMIÈRE PARTIE : LE PREMIER PROGRAMME

– Si vous ne trouvez pas la source de l’erreur, n’hésitez pas à appeler à l’aide ! L’enseignant est là pour
vous aider. Plus tard, quand vous rédigerez vos propres programmes, sachez également que quand on a
"le nez dedans", on ne voit pas toujours ses propres fautes, mais qu’elles sont souvent évidentes pour
quelqu’un qui a un regard neuf sur votre programme : demander une relecture à un de vos camarades
s’avère donc souvent très efficace.

Exercice 2 : faire évoluer un programme


L’objectif de cet exercice est de faire évoluer un programme existant.
Question 13. Testez le programme ci-dessous. Que permet de faire cin ? Proposez une définition pour
cin ?

// Affiche à l’écran un entier saisi par l’utilisateur

#include <iostream>

int main (int argc, char **argv)


{
int n;

std::cout << "Donnez un entier : " << std::endl;


std::cin >> n;
std::cout << "Vous avez donné l’entier : " << n << std::endl;

return 0;
}
Une saisie clavier avec cin

Question 14. Modifiez le programme “Hello world (version 2)” pour qu’il puisse afficher n fois le
message "Hello world !" (n étant une valeur saisie par l’utilisateur). Que se passe-t-il si l’utilisateur saisi
une valeur négative ?

Bilan
Qu’y a-t-il de si important à propos du programme “Hello world !” ? Son objectif était de vous familiariser
avec les outils de base utilisés en programmation.

Retenez cette règle : il faut toujours prendre un exemple extrêmement simple (comme “Hello world”) à
chaque fois que l’on découvre un nouvel outil. Cela permet de diviser l’apprentissage en deux parties : on
commence par apprendre le fonctionnement de base de nos outils avec un programme élémentaire puis
on peut passer à des programmes plus compliqués sans être distraits par ces outils. Découvrir les outils
et le langage simultanément est beaucoup plus difficile que de le faire un après l’autre.

Conclusion : cette approche consistant à simplifier l’apprentissage d’une tâche complexe en la décom-
posant en une suite d’étapes plus petites (et donc plus faicles à gérer) ne s’applique pas uniquement à la
programmation et aux ordinateurs. Elle est courante et utile dans la plupart des domaines de l’existence,
notamment dans ceux qui impliquent une compétence pratique.

Descartes (mathématicien, physicien et philosophe français) dans le Discours de la méthode :

TP Développement C++ 12 / 40 © 2012 tv <tvaira@free.fr>


PREMIÈRE PARTIE : LE PREMIER PROGRAMME

« diviser chacune des difficultés que j’examinerais, en autant de parcelles qu’il se pourrait,
et qu’il serait requis pour les mieux résoudre. »
« conduire par ordre mes pensées, en commençant par les objets les plus simples et les
plus aisés à connaître, pour monter peu à peu comme par degrés jusques à la connaissance
des plus composés ... »

TP Développement C++ 13 / 40 © 2012 tv <tvaira@free.fr>


DEUXIÈME PARTIE : STRUCTURE D’UN PROGRAMME INFORMATIQUE SIMPLE

Deuxième partie : Structure d’un programme informatique sim-


ple

Structure
Un programme informatique est souvent structuré de la manière suivante :

Les vrais programmes ont donc tendance à produire des résultats en fonction de l’entrée qu’on leur fournit.
Pour pouvoir lire quelque chose, il faut dire où le placer ensuite. Autrement dit, il faut un “endroit” dans
la mémoire de l’ordinateur où placer les données lues. On appelle cet “endroit” un objet.

Objets, types et valeurs


Un objet est une région de la mémoire, dotée d’un type qui spécifie quelle sorte d’information on peut y
placer. Un objet nommé s’appelle une variable.

Exemples :
– les chaînes de caractères sont stockées dans des variables de type string
– les entiers sont stockés dans des variables de type int
– les réels sont stockés dans des variables de type float
– 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

TP Développement C++ 14 / 40 © 2012 tv <tvaira@free.fr>


DEUXIÈME PARTIE : STRUCTURE D’UN PROGRAMME INFORMATIQUE SIMPLE

// Réalise la saisie de l’age et du nom de l’utilisateur

#include <iostream>

int main ()
{
int age; // Réserve une région de la mémoire destinée à contenir un entier et lui
attribue le nom age
string nom; // Réserve une région de la mémoire destinée à contenir une chaîne de
caractères et lui attribue le nom nom

std::cout << "Donnez votre âge : " << std::endl; // Affiche un message d’invite demandant
à l’utilisateur d’exécuter une action
std::cin >> age; // Lit la valeur saisie et la place dans age
std::cout << "Donnez votre nom : " << std::endl;
std::cin >> nom; // Lit les caractères et les place dans nom

// ...

return 0;
}
Saisies clavier avec cin

Remarque : 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é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

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

TP Développement C++ 15 / 40 © 2012 tv <tvaira@free.fr>


DEUXIÈME PARTIE : STRUCTURE D’UN PROGRAMME INFORMATIQUE SIMPLE

Le C++ dispose de nombreux types (voir le support de cours). 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++

Remarque : les types string et bool n’existent pas en langage C.

Entrée
Rappel : Une instruction qui introduit un nouveau nom dans un programme et réserve de la mémoire
pour une variable s’appelle une définition.
Le nom cin (character input stream) désigne le flux d’entrée standard (le clavier par défaut), dans la
bibiothèque standard. L’opérateur » (qui signifie “obtenir depuis“) spécifie où va l’entrée. On remarque
dans cet exemple que l’opérateur » est sensible au type : autrement dit, la lecture dépend du type de la
variable de destination. C’est la même chose pour l’opérateur «.
// Réalise la saisie de l’age et du nom de l’utilisateur

#include <iostream>

int main ()
{
int age; // Réserve une région de la mémoire destinée à contenir un entier et lui
attribue le nom age
string nom; // Réserve une région de la mémoire destinée à contenir une chaîne de
caractères et lui attribue le nom nom
std::cout << "Donnez votre nom et votre âge : " << std::endl; // Affiche un message d’
invite demandant à l’utilisateur d’exécuter une action
std::cin >> nom; // Lit les caractères et les place dans nom
std::cin >> age; // Lit la valeur saisie et la place dans age
// ...
return 0;
}
Saisies clavier avec cin

Pourquoi cin ne place-t-il pas tout ce qui est saisi dans la variable nom ? Parce que, par convention, la
lecture des chaînes de caractère 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. Notez que les espaces sont ignorés
par défaut.
Question 15. Inversez la saisie (cin) de nom et age. Que constatez-vous ? Expliquez.
Question 16. Réaliser un programme qui saisit et affiche le nom et le prénom de l’utilisateur dans le
message "Bienvenue non prénom !".

Remarque : si votre nom comporte des espaces, il vous faudra alors utilisé la fonction getline
(www.cplusplus.com/reference/string/string/getline/).

TP Développement C++ 16 / 40 © 2012 tv <tvaira@free.fr>


DEUXIÈME PARTIE : STRUCTURE D’UN PROGRAMME INFORMATIQUE SIMPLE

Opérations et opérateurs
Outre le fait qu’il spécifie quelles valeurs on peut stocker dans une variable, le type de celle-ci détermine
quelles opérations on peut lui appliquer et ce qu’elles signifient.
Remarque : la liste des opérateurs est fournie dans le cours.
// Réalise des opérations sur l’age et le nom de l’utilisateur

#include <iostream>

using namespace std;

int main ()
{
int age;
string nom;

cin >> nom; // Lit les caractères et les place dans nom
cin >> age; // Lit la valeur saisie et la place dans age

string fils = nom + " Junior"; // Ajoute (concatène) des caractères à la fin
int ageApres = age + 1; // Additionne des entiers

string s = nom - " Junior"; // Erreur : cet opérateur n’est pas défini pour string !
int ageAvant = age - 1; // Soustrait des entiers

return 0;
}
Opérations sur les types

Par "Erreur", nous entendons que le compilateur rejettera la ligne où on essaye de soustraire des chaînes
car le compilateur sait exactement quelles opérations peuvent être appliquées à chaque type de variable.
erreur: no match for ’operator-’ in ’nom - " Junior"’

Par contre, le compilateur acceptera sans broncher des opérations légales qui générèrent des résultats
absurdes :
int age = -100;

Remarque : L’impossibilité d’avoir un âge négatif peut vous sembler évidente (n’est-ce pas ?), mais
personne ne l’a dit au compilateur : il produira donc du code pour cette définition et vous aurez (peut-être)
un bug à gérer par la suite.

La comparaison de chaînes est particulièrement utile et permet de montrer un traitement réalisable par
un ordinateur :
// Lit et compare deux noms

#include <iostream>

using namespace std;

int main()

TP Développement C++ 17 / 40 © 2012 tv <tvaira@free.fr>


DEUXIÈME PARTIE : STRUCTURE D’UN PROGRAMME INFORMATIQUE SIMPLE

{
cout << "Donnez deux noms :\n";
string premier;
string second;
cin >> premier >> second; // Lit deux chaînes

if (premier == second) cout << "Les deux noms sont identiques.\n";


if (premier < second)
cout << premier << " est avant " << second <<’\n’;
if (premier > second)
cout << premier << " est après " << second <<’\n’;

return 0;
}
Comparaison de chaînes

Remarque : Nous utilisons ici une instruction if pour sélectionner des actions soumises à des conditions.
Vous pouvez consulter le cours pour obtenir plus d’informations sur les instructions conditionnelles.
On obtient ceci à l’exécution :
$ ./a.out
Donnez deux noms :
titi toto
titi est avant toto

$ ./a.out
Donnez deux noms :
tata tata
Les deux noms sont identiques.

$ ./a.out
Donnez deux noms :
toto Toto
toto est après Toto

Question 17. Quelle est alors la définition d’"identique" dans le programme précédent ?

Règles de codage
Un nom de variable est un nom principal (surtout pas un verbe) suffisamment éloquent, éventuellement
complété par :
– une caractéristique d’organisation ou d’usage
– un qualificatif ou d’autres noms

On utilisera la convention suivante : un nom de variable commence par une lettre minuscule puis
les différents mots sont repérés en mettant en majuscule la première lettre d’un nouveau
mot.

Certaines abréviations sont admises quand elles sont d’usage courant : nbre, max, min, ...

TP Développement C++ 18 / 40 © 2012 tv <tvaira@free.fr>


DEUXIÈME PARTIE : STRUCTURE D’UN PROGRAMME INFORMATIQUE SIMPLE

Les lettres i, j, k utilisées seules sont usuellement admises pour les indices de boucles.

Exemples : distance, distanceMax, consigneCourante, etatBoutonGaucheSouris, nbreDEssais, ...

Un nom de variable doit être uniquement composé de lettres, de chiffres et de "souligné" (_). Les noms
débutant par le caractère "souligné" (_) sont réservés au système, et à la bibliothèque C. Les noms
débutant par un double "souligné" (__) sont réservés aux constantes symboliques (#define ...) privées
dans les fichiers d’en-tête (.h).

Il est déconseillé de différencier deux identificateurs uniquement par le type de lettre (minuscule/majus-
cule). Les identificateurs doivent se distinguer par au moins deux caractères, parmi les 12 premiers, car
pour la plupart des compilateurs seuls les 12 premiers symboles d’un nom sont discriminants.

Les mots clés du langage sont interdits comme noms.

Remarque : l’objectif de respecter des règles de codage est d’augmenter la lisibilité des programmes en se
rapprochant le plus possible d’expressions en langage naturel.

Affectation et initialisation
L’opérateur le plus intéressant (et le plus important à maîtriser) est l’opérateur d’affectation, représenté
par =. Il attribue une nouvelle valeur à une variable.
L’opérateur = permet deux opérations similaires mais intellectuellement distinctes :
– l’initialisation qui donne une valeur initiale à une variable
– l’affectation qui donne une nouvelle valeur à une variable

Exemple d’initialisations et d’affectations sur des entiers

TP Développement C++ 19 / 40 © 2012 tv <tvaira@free.fr>


DEUXIÈME PARTIE : STRUCTURE D’UN PROGRAMME INFORMATIQUE SIMPLE

Question 18. Compléter en indiquant les valeurs de a et b pour l’exemple ci-dessous.

Exemple d’initialisations et d’affectations sur des chaînes

L’affectation est nécessaire lorsqu’on veut placer une nouvelle valeur dans un objet. Elle trouve toute son
utilité dès qu’on fera quelque chose plusieurs fois car il faudra une affectation pour refaire quelque chose
avec une valeur différente (c’est le concept de variable).

Voici un programme qui illustre cela : un détecteur de mots répétés adjacents dans une suite de mots. Ce
genre de code fait partie de la plupart des vérificateurs de grammaire :
// Détecte des mots répétés adjacents
#include <iostream>
using namespace std;

int main()
{
string motPrecedent = " "; // signifiera "pas un mot" !
string motCourant;
while (cin >> motCourant) // Lit un flux de mots
{
if (motPrecedent == motCourant) // Teste si le mot est le même que le précédent
cout << "mot répété : " << motCourant << ’\n’;
motPrecedent = motCourant;
}
return 0;
}
Détecteur de mots répétés

TP Développement C++ 20 / 40 © 2012 tv <tvaira@free.fr>


DEUXIÈME PARTIE : STRUCTURE D’UN PROGRAMME INFORMATIQUE SIMPLE

On lit donc un mot dans motCourant avec cin, puis on le compare au mot précédent (motPrecedent).
S’ils sont ”identiques“, on l’affiche à l’écran. Puis on doit se préparer pour le mot suivant : on place donc
dans motPrecedent le mot contenu dans motCourant.
Mais que doit faire ce programme pour le premier mot quand il n’y a pas encore de mot précédent à
comparer ? Il faut initialiser motPrecedent lors de sa définition de telle manière qu’il indique qu’il ne
contient pas (encore) un mot. Il contiendra qu’un seul caractère (le caractère espace) car on sait que
l’opérateur » les ignore. Il sera donc impossible à cin de lire cette valeur et lors du premier passage dans
la boucle while, le test if échouera (c’est ce que nous voulions).
On obtient ceci à l’exécution :
$ ./a.out
toto et titi titi vont à la la plage avec titi
mot répété : titi
mot répété : la
Ctrl-d

Remarque : sous Linux, vous pouvez mettre fin au programme en combinant les touches Ctrl et 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.
Conseil : Une façon de comprendre ce programme est de ”jouer à l’ordinateur“, autrement dit de suivre
le programme ligne par ligne en faisant ce qu’il spécifie. Dessinez des boîtes sur une feuille de papier et
inscrivez-y leurs valeurs. Puis changez les valeurs comme indiqué par le programme. Les programmeurs
expérimentés font cela alors pourquoi pas vous ?
Question 19. Quelle est la signification exacte de ”mots répétés” dans ce programme ?
Question 20. Modifiez ce programme afin qu’il compte et affiche à la fin le nombre de mots répétés
adjacents dans une suite de mots. Pour vous aider, répondez aux questions suivantes qui sont celles qu’un
programmeur doit se poser :
a) Proposez un nom et un type pour la variable qui doit compter le nombre de mots répétés.
b) Donnez sa valeur initiale.
c) Écrivez alors sa définition.
d) Repérez l’endroit où la détection d’un mot répété est réalisée.
e) Écrivez alors l’incrémentation du compteur. Si vous devez écrire plusieurs instructions pour un if,
il vous faudra alors mettre un bloc avec des accolades ({}).

Remarque : pour incrémenter une variable, on peut soit utiliser l’opérateur d’incrémentation (++)
soit l’addition (+).
int a = 4;

// Au choix :
++a; // après cette instruction, a aura pour valeur 5 (écriture conseillée)
a++; // après cette instruction, a aura pour valeur 6
a = a + 1; // après cette instruction, a aura pour valeur 7
a += 1; // après cette instruction, a aura pour valeur 8
Incrémentation d’un entier

Remarque : assurer un affichage grammaticalement correct, "Aucun mot répété adjacent dans cette suite",
"un seul mot répété adjacent dans cette suite" ou "Il y a n mots répétés adjacents dans cette suite" (où n
est une valeur strictement supérieure à 1).

TP Développement C++ 21 / 40 © 2012 tv <tvaira@free.fr>


DEUXIÈME PARTIE : STRUCTURE D’UN PROGRAMME INFORMATIQUE SIMPLE

Questions de révision
L’idée de base des questions de révision est de vous donner une chance de voir si vous avez identifié et
compris les points clés de cette partie.

Question 21. Qu’entend-on par le terme “invite” ?

Question 22. Quel opérateur utilise-t-on pour lire une valeur et la placer dans une variable ?

Question 23. Qu’est-ce qu’une variable ?

Question 24. Quelle est la différence entre = et == ?

Question 25. Qu’est-ce qu’une définition ?

Question 26. Qu’est-ce qu’une initialisation et en quoi diffère-t-elle d’une affectation ?

Question 27. Qu’est-ce qu’une concaténation de chaînes ? Quel est l’opérateur qui permet cela en
C++ ?

Question 28. Qu’est-ce qui termine l’entrée d’une chaîne ? d’un entier ?

Question 29. Quelles sont les bonnes règles pour choisir un nom de variable ?

Question 30. Qu’est-ce qu’une incrémentation ?

Exercice 3 : calcul de notes


L’objectif de cet exercice est de montrer la décomposition en itérations d’un problème simple.

Question 31. Calculer la somme d’un certain nombre de valeurs tapées par l’utilisateur

Créez un programme qui lit une série de valeurs numériques réelles tapées successivement par
l’utilisateur. La saisie de la valeur -1 comme note signale au programme qu’il n’y a plus de note à
taper ; cette note -1 ne doit pas être considérée comme une vraie note pour la suite du problème, c’est
uniquement une marque de fin. Au fur et à mesure que les nombres sont lus l’un après l’autre dans
une variable (que vous pouvez nommer note), additionnez-les dans une variable que vous nommerez
accumulateur et que vous initialiserez précédemment à la valeur 0 (élément neutre de l’addition).

Question 32. Compter les valeurs en même temps.

Modifiez votre source pour que le programme, en plus de toujours calculer la somme des valeurs tapées,
calcule aussi automatiquement leur nombre... sans compter le -1 !

Question 33. Calculer la moyenne des valeurs.

Ajoutez l’affichage de la moyenne des valeurs, c’est-à-dire tout simplement le total divisé par le nombre
de valeurs. Attention : il y a un petit piège dans cette question ! Si, pris par l’habitude (pourtant pas bien
vieille) de toujours créer des variables de type int, vous avez jusqu’ici commis l’erreur de choisir int comme
type pour la note saisie ou pour l’accumulateur, vous allez obtenir un autre calcul que celui que vous
désirez ! En effet, diviser un entier par un autre entier conduit à calculer le quotient entier de la division
euclidienne. Si vous voulez calculer une division réelle, il suffit qu’un des arguments (ici, choisissons
l’accumulateur) soit un réel (comme indiqué à la première question de l’exercice). Si l’accumulateur est
un réel, je vous signale qu’il serait logique que la variable note le soit aussi.

TP Développement C++ 22 / 40 © 2012 tv <tvaira@free.fr>


DEUXIÈME PARTIE : STRUCTURE D’UN PROGRAMME INFORMATIQUE SIMPLE

Question 34. Contrôler les saisies.

Améliorez l’opération de lecture pour que le programme vérifie que la valeur tapée est bien comprise
entre 0 et 20 inclus, ou qu’il s’agit de -1. Si la valeur n’est pas acceptable, alors l’utilisateur voit un
message qui lui demande de recommencer sa saisie de cette valeur jusqu’à ce qu’elle soit correcte.

Question 35. Déterminer le minimum et le maximum.

Trouvez comment modifier votre algorithme pour que le programme calcule (et affiche, à sa fin) la valeur
du minimum et du maximum de la série de note en plus du calcul de moyenne déjà obtenu aux questions
précédentes.

Question 36. Écrire un programme robuste.

Que donne votre programme si on ne saisit aucune note, c’est-à-dire si on tape -1 dès le début ? Un bon
programme ne doit pas planter, et donc prévoir ce cas.

Bilan
Notez comment ces premiers programmes se sont construits : progressivement en modifiant un peu le
code pour atteindre un nouvel objectif. C’est une technique très courante : en présence d’un problème
à résoudre, on recherche un problème similaire et on utilise sa solution en ajoutant les modifications
appropriées. Ne partez donc pas de zéro à moins d’y être vraiment obligé. Se baser sur une version
précédente économise beaucoup de temps et permet de tirer profit des efforts investis dans le programme
d’origine. Mais, pour pouvoir être réutilisé, un programme doit donc être proprement écrit : clair, lisible,
fonctionnel, robuste et documenté.
Conclusion : Les types sont au centre de la plupart des notions de programmes corrects, et certaines des
techniques de construction de programmes les plus efficaces reposent sur la conception et l’utilisation des
types.

Rob Pike (ancien chercheur des Laboratoires Bell et maintenant ingénieur chez Google) :
« Règle n°5 : Les données prévalent sur le code. Si vous avez conçu la structure des données
appropriée et bien organisé le tout, les algorithmes viendront d’eux-mêmes. La structure des
données est le coeur de la programmation, et non pas les algorithmes. »
Cette règle n°5 est souvent résumée par « Écrivez du code stupide qui utilise des données futées ! »

TP Développement C++ 23 / 40 © 2012 tv <tvaira@free.fr>


TROISIÈME PARTIE : PROGRAMMATION MODULAIRE

Troisième partie : Programmation modulaire


D’un certain point de vue, un programme informatique ne fait jamais rien d’autre que traiter des
données. Comme on l’a déjà vu dans la deuxième partie, un programme accepte des entrées et produit
des sorties :

La majeure partie du travail d’un programmeur est : comment exprimer un programme sous la forme
d’un ensemble de parties qui coopèrent et comment peuvent-elles partager et échanger des données ?

Les E/S signifie évidemment “entrées/sorties” : la sortie d’une partie de code est l’entrée de la suivante.
Un traitement est tout simplement l’action de produire des sorties à partir d’entrées. Les
entrées dans une partie de programme sont souvent appelées des arguments (ou parfois paramètres) et
les sorties d’une partie de programme des résultats.
Par parties de programme (ou de code), on entend des entités comme une fonction produisant un
résultat à partir d’un ensemble d’arguments en entrée.
Exemple : un traitement comme produire le résultat (sortie) 49 à partir de l’argument (entrée) 7 au
moyen de la fonction racineCarree.

TP Développement C++ 24 / 40 © 2012 tv <tvaira@free.fr>


TROISIÈME PARTIE : PROGRAMMATION MODULAIRE

Objectifs et outils
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 progremmes 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.
Notre principal outil pour organiser un programme est de décomposer un traitement de grande taille en
plusieurs parties plus petites jusqu’à obtenir quelque chose de suffisamment simple à comprendre et à
résoudre (cf. Descartes et son discours de la méthode).

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.

TP Développement C++ 25 / 40 © 2012 tv <tvaira@free.fr>


TROISIÈME PARTIE : PROGRAMMATION MODULAIRE

// calcule une aire


int longueur = 40;
int largeur = 20;
int aire = longueur * largeur;

Il y a plusieurs sortes d’instructions : les déclarations, les instructions d’expression, les instructions
conditionnelles, les instructions itératives (boucles), etc ...
Une instruction d’expression est une expression suivie d’un point-virgule (;). Le point-virgule (;) est
un élément syntaxique permettant au compilateur de “comprendre” ce que l’on veut faire dans le code
(comme la ponctuation dans la langue française).
Dans les programmes comme dans la vie, il faut souvent choisir entre plusieurs possibilités. Le C/C++
propose plusieurs instructions conditionnelles : l’instruction if ou l’instruction switch.
Une instruction if choisit entre deux possibilités : si la condition est vraie, la première instruction (ou
bloc d’instructions) est exécutée sinon c’est la seconde.
if(feuxPieton == vert) traverser();
else attendre();

La notion de base est donc simple mais il est également facile d’utiliser if de façon trop simpliste.
Voici un exemple simple de programme de conversion cm/inch qui utilise une instruction if :
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;

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 :
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;

if (unite == ’i’)

TP Développement C++ 26 / 40 © 2012 tv <tvaira@free.fr>


TROISIÈME PARTIE : PROGRAMMATION MODULAIRE

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.

TP Développement C++ 27 / 40 © 2012 tv <tvaira@free.fr>


TROISIÈME PARTIE : PROGRAMMATION MODULAIRE

Il est rare de faire quelque chose une seule fois. C’est pour cela que tous les langages de programmation
fournissent des moyens pratiques de faire quelque chose plusieurs fois (on parle de traitement itératif).
On appelle cela une boucle ou une itération.
Le C/C++ offrent plusieurs instructions itératives : la boucle while (et sa variante do ... while)
et la boucle for.
Le premier programme jamais exécuté sur un ordinateur à programme stocké en mémoire (l’EDSAC)
est un exemple d’itération. Il a été écrit et exécuté par David Wheeler au laboratoire informatique de
Cambridge le 6 mai 1949 pour calculer et afficher une simple liste de carrés comme ceci :
0 0
1 1
2 4
3 9
4 16
...
98 9604
99 9801

Ce premier programme n’a pas été écrit en C/C++ mais le code devait ressembler à ceci :
// Calcule et affiche le carré d’un nombre

#include <iostream>
#include <cmath>

using namespace std;

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’ << pow(i, 2) << ’\n’; // affiche i et son carré séparés par une
tabulation
++i; // on incrémente le nombre et on recommence
}
}
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.
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é de ne pas
coder 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

TP Développement C++ 28 / 40 © 2012 tv <tvaira@free.fr>


TROISIÈME PARTIE : PROGRAMMATION MODULAIRE

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 le carré d’un nombre

#include <iostream>
#include <cmath>

using namespace std;

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
}
Le premier programme jamais écrit (version for)

Constantes
Les programmes utilisent généralement beaucoup de constantes. Elles sont très importantes pour conserver
un code lisible.
Le C++ offre la notion de constante symbolique, c’est-à-dire un objet nommé auquel on ne peut pas
donner de nouvelle valeur une fois qu’il a été initialisé.
const double pi = 3.14159;
pi = 22/7; // Erreur : affectation d’une constante
const double v = 7*pi/r; // Ok : on ne fait que de lire la constante pi

Règle de codage : on évite d’utiliser des valeurs littérales dans le code et on s’oblige le plus possible à
utiliser des constantes portant des noms significatifs.
Remarque : les valeurs littérales dans le code (en dehors des définitions const) sont qualifiées par dérision
de constantes magiques.
Il est aussi possible de créer des constantes en utilisant l’instruction du préprocesseur #define :
#define PI 3.14159

C’est simplement une substitution de texte (une sorte de copier/coller) qui est réalisée avant la
compilation. Il n’y a aucun typage de la constante.
L’utilisation de #define améliore surtout la lisibilité du code source. La convention usuelle est d’utiliser
des MAJUSCULES (pour les distinguer des variables).

TP Développement C++ 29 / 40 © 2012 tv <tvaira@free.fr>


TROISIÈME PARTIE : PROGRAMMATION MODULAIRE

Références et pointeurs
Les pointeurs sont des variables spéciales permettant de stocker une adresse (pour la manipuler ensuite).
L’adresse représente généralement l’emplacement mémoire d’une variable (ou d’une autre adresse).
Comme la variable adressée a un type, le pointeur qui stockera son adresse doit être du même type pour
la manipuler convenablement.

// On utilise l’étoile (*) définir un pointeur


int *ptrInt; // ptrInt est un pointeur sur un entier (int)

// On utilise le & devant une variable pour avoir la valeur de son adresse et s’en servir
pour initialiser ou affecter un pointeur
int i = 2;
ptrInt = &i; // affectation de ptrInt avec l’adresse de la variable i (&i)

// On utilise l’étoile devant le pointeur (*) pour accéder à l’adresse stockée


*ptrInt = 3; // indirection ("on pointe sur le contenu de i")

// et maintenant la variable i contient 3


Utilisation d’un pointeur

Les pointeurs peuvent être incrémentés, décrémentés, additionnés ou soustraits. Dans ce cas, leur nouvelle
valeur d’adresse dépend du type sur lequel ils “pointent”. Cela peut servir par exemple pour se déplacer
sur la prochaine variable du même type dans la mémoire.

Règle de codage : on peut préfixer ses variables pointeurs avec ptr.


Remarque : les pointeurs font parties des variables les plus délicates à manipuler car, si on les utilise mal,
on peut provoquer des erreurs d’accès mémoire. Certains langages (comme le Java) les ont interdits.

TP Développement C++ 30 / 40 © 2012 tv <tvaira@free.fr>


TROISIÈME PARTIE : PROGRAMMATION MODULAIRE

En C++ (pas en C), il est possible d’utiliser des références sur des objets (variables) définis ailleurs :
cela permet de créer un nouveau nom qui sera un synonyme de cette variable (comme un alias ou un
raccourci). On pourra donc modifier le contenu de la variable en utilisant une référence sur celle-ci. Une
référence ne peut être initialisée qu’une seule fois. Elle ne peut donc référencer qu’une seule variable tout
au long de sa durée de vie.
int i = 2; // i est un entier valant 2

// On indique le & devant le nom de la variable et cela signifie ‘‘référence’’


int &j = i; // j est une référence sur un entier et cet entier est i

//&j = x; // Erreur : illégal (on ne peut pas affecter une nouvelle variable à une référence
déjà initialisée)
//int &k = 44; // Erreur : illégal (on ne peut pas créer une référence sur une valeur)

// A partir d’ici j est ‘‘synonyme‘‘ de i, ainsi :


j = j + 1; // est équivalent à i = i + 1 !

// et maintenant la variable i contient 3


Utilisation d’une référence

Remarque : les références sont très utilisées en C++, notamment dans le passage des arguments d’une
fonction. On peut aussi ajouter le mot clé const pour interdire la fonction de modifier (accidentellement)
la variable passée en argument.

TP Développement C++ 31 / 40 © 2012 tv <tvaira@free.fr>


TROISIÈME PARTIE : PROGRAMMATION MODULAIRE

Fonctions
Dans le programme précédent (celui qui calcule et affiche le carré d’un nombre), pow(i, 2) est un appel
de fonction. Plus précisément, c’est un appel à une fonction nommée pow avec les arguments i et 2
et qui retourne le résultat de i élévé à la puissance 2.

L’appel pow(i, 2)

La bibliothèque standard fournit beaucoup de fonctions utiles, comme la fonction pow(). Toutefois,
nous écrirons de nombreuses fonctions nous-mêmes.
Voici une définition plausible d’une fonction carre :
// Calcule et affiche le carré d’un nombre

#include <iostream>

using namespace std;

int carre(int x)
{
return x*x;
}

int main()
{
for (int i = 0; i < 100; i++)
cout << i << ’\t’ << carre(i) << ’\n’; // affiche i et son carré séparés par une
tabulation

return 0;
}
Notre fonction carre()

TP Développement C++ 32 / 40 © 2012 tv <tvaira@free.fr>


TROISIÈME PARTIE : PROGRAMMATION MODULAIRE

Le programmeur professionnel se doit de maîtriser les concepts sur les fonctions :

Déclaration de fonction

Définition de fonction

Remarque : une fonction qui ne retourne pas de résultat (donc void) se nomme une procédure.

TP Développement C++ 33 / 40 © 2012 tv <tvaira@free.fr>


TROISIÈME PARTIE : PROGRAMMATION MODULAIRE

// Un appel à une fonction signifie qu’on lui demande d’exécuter son traitement
// Il doit correspondre à la déclaration faite au compilateur qui vérifie :
// on n’est pas obligé d’utiliser le résultat de retour mais on doit donner à la fonction
exactement les arguments qu’elle exige

carre(2); // Probablement une erreur car la valeur retour n’est pas utilisée

int c1 = carre(); // Erreur : argument manquant

int c2 = carre; // Erreur : parenthèses manquantes

int c3 = carre(1, 2); // Erreur : trop d’arguments

int c4 = carre("deux"); // Erreur : mauvais type d’argument car int attendu

int c5 = carre(2); // Ok : enfin !


L’appel à notre fonction carre()

On définit une fonction lorsqu’on souhaite un traitement distinct et nommé parce que procéder ainsi :
– rend le traitement distinct du point de vue logique
– rend le programme plus clair et plus lisible
– permet d’utiliser la fonction à plusieurs endroits dans un programme (à chaque fois qu’on en a besoin)
– facilite les tests (on simule des entrées et on compare le résultat obtenu à celui attendu)

Les programmes sont généralement plus facile à écrire, à comprendre et à maintenir lorsque chaque fonc-
tion réalise une SEULE ACTION logique et bien évidemment celle qui correspond à son nom.

Règle de codage : on évite d’utiliser des saisies et des affichages dans les fonctions pour permettre
notamment leur ré-utilisation. Les fonctions se concentrent sur le traitement et n’ont pas à réaliser la
saisie de leurs entrées et l’affichage de leur sortie. C’est une “bonne pratique” à respecter dès maintenant.
Voici ce qu’il ne faut pas faire :
// Calcule et affiche le carré d’un nombre saisi par l’utilisateur

#include <iostream>
using namespace std;

void saisirUnNombreEtAfficherSonCarre()
{
int x;
cout << "Donnez un nombre : ";
cin >> x;
cout << "Le carré de ce nombre est " << x*x;
}

int main()
{
saisirUnNombreEtAfficherSonCarre();

return 0;
}
Mauvais exemple

TP Développement C++ 34 / 40 © 2012 tv <tvaira@free.fr>


TROISIÈME PARTIE : PROGRAMMATION MODULAIRE

Remarque : les fonctions qui ne recoivent aucune entrée et qui ne produisent aucun résultat en retour
sont intellectuellement suspicieuses (des “trous noirs” de code !). Il faut les éviter au maximum.
Voici un code d’utilisation de la fonction carre() bien mieux structuré :
// Calcule et affiche le carré d’un nombre saisi par l’utilisateur

#include <iostream>

using namespace std;

int carre(int x)
{
return x*x;
}

int main()
{
int x;
cout << "Donnez un nombre : ";
cin >> x;
cout << "Le carré de ce nombre est " << carre(x);
return 0;
}
Bon exemple

Passage par valeur

Remarque : bien évidemment, on peut passer les arguments à une fonction par référence ou par pointeur
(voir cours et exercices plus loin).

TP Développement C++ 35 / 40 © 2012 tv <tvaira@free.fr>


TROISIÈME PARTIE : PROGRAMMATION MODULAIRE

Règles de codage
Un nom de fonction est construit à l’aide d’un verbe (pas un nom), et éventuellement d’éléments
supplémentaires :
– une quantité
– un complément d’objet
– un adjectif représentatif d’un état

On utilisera la convention suivante : un nom de fonction commence par une lettre minuscule
puis les différents mots sont repérés en mettant en majuscule la première lettre d’un nou-
veau mot. Un nom de fonction peut commencer par une majuscule dans des cas que l’on
précisera plus tard.

Le verbe peut être au présent de l’indicatif ou à l’infinitif. L’adjectif représentatif d’un état concerne
surtout les fonctions booléennes. La quantité peut, le cas échéant, enrichir le sens du complément.

Exemples : void ajouter(), void sauverValeur(), estPresent(), estVide(),


viderAMoitieLeReservoir(), ...

Rappel : Les mots clés du langage sont interdits comme noms.

Les noms des paramètres d’une fonction sont construits comme les noms de variables : ils commencent,
notamment par une minuscule. L’ordre de définition des paramètres doit respecter la règle suivante :

nomFonction(parametrePrincipal, listeParametres)

où parametrePrincipal est la donnée principale sur laquelle porte la fonction, la listeParametres ne


comportant que des données secondaires, nécessaires à la réalisation du traitement réalisé par la fonction.
Exemple : ajouter(EnsembleMesures mesures, float uneMesure)
La sémantique de cette fonction est d’ajouter une mesure à un ensemble de mesures (qui est la donnée
principale) et non, d’ajouter un ensemble de mesures à une mesure.
Remarque : l’objectif de respecter des règles de codage est d’augmenter la lisibilité des programmes en se
rapprochant le plus possible d’expressions en langage naturel.

Pour finir, on va s’ajouter une règle qui couvre l’objectif majeur suivant : décomposer un traitement de
grande taille en plusieurs parties plus petites jusqu’à obtenir quelque chose de suffisamment simple à
comprendre et à résoudre (cf. Descartes et son discours de la méthode). Pour cela, on va s’obliger à
limiter la taille des fonctions à une valeur comprise entre 10 à 15 lignes maximum.

TP Développement C++ 36 / 40 © 2012 tv <tvaira@free.fr>


TROISIÈME PARTIE : PROGRAMMATION MODULAIRE

Questions de révision
L’idée de base des questions de révision est de vous donner une chance de voir si vous avez identifié et
compris les points clés de cette partie.

Question 37. Qu’est-ce qu’un traitement ?

Question 38. Qu’entend-on par entrées et sorties d’un traitement. Donnez des exemples.

Question 39. Quelles sont les trois exigences qu’un programmeur doit garder présentes à l’esprit en
exprimant des traitements ?

Question 40. Qu’est-ce qu’une lvalue ?

Question 41. Qu’est-ce qu’une constante symbolique et pourquoi les utilise-t-on ?

Question 42. Quand un programmeur préférerait-il une instruction switch à une instruction if ?

Question 43. Quelle est la fonction de chaque partie de la ligne d’en-tête d’une boucle for et dans quel
ordre sont-elles exécutées ?

Question 44. Quand doit-on utiliser la boucle for et quand doit-on utiliser la boucle while ?

Question 45. Étes-vous capable de faire la différence entre une déclaration et une définition de fonction ?

Question 46. Décrivez ce que la ligne char foo(int x, int y) signifie dans une définition de fonction.

TP Développement C++ 37 / 40 © 2012 tv <tvaira@free.fr>


TROISIÈME PARTIE : PROGRAMMATION MODULAIRE

Exercice 4 : programmation modulaire


Éditez le programme somme1-100.cpp dans votre répertoire de travail, étudiez son fonctionnement, puis
fabriquez-le, et testez l’exécutable ainsi produit.
#include <iostream> // Inclusion des déclarations de cout, endl, etc.

using namespace std;

//-------------------------------------------------------------//
// somme1-100.cpp //
// //
// Calcul de la somme des entiers de 1 à 100 //
//-------------------------------------------------------------//
int main ()
{
unsigned int i=0U; // variable de contrôle, valeur initiale : zéro.
unsigned int accumulateur=0U; // la somme des entiers, initialement zéro.

// calcul : utilise un traitement itératif : la boucle


// while est l’une des instructions possibles en C/C++
while (i <= 100U)
{
accumulateur = accumulateur + i;
i = i + 1; // Avec un peu d’habitude, on écrira plutot ++i
}

// affichage du résultat
cout << "La somme des entiers de 1 a 100 est " << accumulateur << endl;

return 0;
} // fin du main()

Question 47. Remplacez la valeur "100" utilisée par le programme par une constante nommée MAXIMUM
définie avant le début du programme et de même valeur. Une habitude courante des programmeurs
C/C++, est de nommer les constantes en majuscules ; pensez à respecter cette règle vous aussi.
Question 48. Modifiez de nouveau le programme pour ne plus utiliser une constante mais une variable
nommée désormais maximum (en minuscule puisque ce n’est plus une constante !). Modifiez le programme
pour qu’il demande à l’utilisateur de donner une valeur pour le maximum avant le début du calcul de
somme.
Question 49. En déplaçant la portion de programme qui réalise la somme (et uniquement le calcul),
réalisez une fonction unsigned int somme1aN(unsigned int n) qui calcule la somme des entiers de 1
à la valeur n fournie en paramètre et qui renvoie cette somme comme valeur de retour de la fonction.
Définissez cette fonction au dessus de votre main(). Pour que votre réponse au problème soit la bonne, il
faut que vous n’ayez pas ni de cin ni de cout dans votre fonction : elle ne fait que le calcul. Modifiez
votre programme principal (c’est-à-dire la fonction int main()) pour que la fonction somme1aN() soit
maintenant appelée et que le main() affiche son résultat.
Question 50. Rendez votre programme modulaire : déplacez la fonction unsigned int somme1aN(unsigned
int n) dans un nouveau fichier somme.cpp ; créez un fichier somme.h contenant la déclaration de cette
même fonction ; incluez ce fichier somme.h dans le fichier qui contient int main(). Compilez séparemment
chaque fichier .cpp en un fichier .o, puis faites l’éditions de liens de tous les .o en un exécutable. Testez.

TP Développement C++ 38 / 40 © 2012 tv <tvaira@free.fr>


TROISIÈME PARTIE : PROGRAMMATION MODULAIRE

Question 51. En vous inspirant de la fonction unsigned int somme1aN(unsigned int n), créez
maintenant une fonction unsigned int factorielle(unsigned int n).
On rapelle que somme1aN(n)=0+0+1+2+3+...+(n-1)+n et que factorielle(n)=1*1*2*3*...*(n-1)*n.
On remarquera que les termes soulignés sont les valeurs initiales de l’accumulateur. Les termes qui suivent
cette valeur initiale correspondent chacun à un tour de boucle. Pensez bien que la valeur de n peut être
0, et qu’on peut très bien ne faire aucun tour de la boucle !

Question 52. Rajoutez une boucle for() dans le main() qui appelle la fonction factorielle() pour
toutes les valeurs comprises entre 1 et celle choisie par l’utilisateur et affiche la valeur de la factorielle
pour chaque valeur de n. Éditez ces modifications dans un fichier main.cpp. Fabriquez et testez. Que se
passe t’il quand n dépasse la valeur 20 ? Pourquoi ?

Les étapes de fabrication peuvent être automatisées en utilisant l’outil make et en écrivant les règles à
appliquer dans un fichier Makefile :
main: main.o somme.o
g++ -o main main.o somme.o
main.o: main.cpp somme.h
g++ -c main.cpp
somme.o: somme.cpp somme.h
g++ -c somme.cpp

Attention : il faut une TABULATION et non des espaces devant les lignes commençant par g++.

Question 53. Modifiez un après l’autre les fichiers concernés (main.cpp, somme.h et somme.cpp) et
exécutez make à chaque fois pour observer ses actions. Quel est l’intérêt de make dans la programmation
modulaire ?

Exercice 5 : passage par adresse


On définit trois nombres réels a, b, c simple précision (float) dans le main(), et on leur attribue des
valeurs arbitraires, par exemple a=11.5, b=-2.1 et c=0.0. On désire écrire une fonction ordonne3() qui
puisse manipuler ces trois variables passées en paramètre, et faire en sorte qu’après son appel, on ait
toujours la situation a ≤ b ≤ c, éventuellement en permutant leurs valeurs.

Question 54. Afin de simplifier le problème, on décide d’écrire d’abord une fonction echange2ParAdresse()
qui effectue le tri sur seulement deux paramètres à la fois. Ecrivez cette fonction puis testez la sur deux
variables du main().

Question 55. Une fois que echange2ParAdresse() est au point, écrivez ordonne3() de telle façon
qu’elle fasse un certain nombre d’appels à echange2ParAdresse(). Combien faut-il d’appel (au minimum)
à echange2ParAdresse() pour être certain de la justesse du résultat ?

Pour être certain que votre solution soit juste, il faut que vous ne fassiez les affichages que dans le main().
Ce qui interdit en particulier d’en faire dans echange2ParAdresse() ou dans ordonne3().

Exercice 6 : passage par référence


Question 56. On reprend la même question qu’à l’exercice 2, mais on décide d’utiliser des passages par
référence à la place des passages par adresse. Laquelle des deux solutions vous parait la plus simple ?
Peut-on utiliser des références en C ?

TP Développement C++ 39 / 40 © 2012 tv <tvaira@free.fr>


TROISIÈME PARTIE : PROGRAMMATION MODULAIRE

Bilan
D’un point de vue théorique, vous pouvez désormais faire tout ce qu’on peut faire avec un ordinateur, le
reste n’est que détails ! Cela montre tout de même la valeur des “détails” et l’importance des compétences
pratiques parce qu’il est clair que vous débutez à peine votre carrière de programmeur.
Conclusion : Il reste “seulement” à apprendre à écrire de bons programmes, c’est-à-dire 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.

TP Développement C++ 40 / 40 © 2012 tv <tvaira@free.fr>

Vous aimerez peut-être aussi