Chapitre 1
Introduction
Un ordinateur est une machine capable d'effectuer très rapidement des opérations arithmétiques
ou logiques sur des données afin de fournir des résultats. Cependant, cette machine ne dispose
d'aucune connaissance : pour obtenir qu'elle réalise les opérations souhaitées, il faut lui fournir des
instructions qu'elle suivra ensuite à la lettre.
En général, l'utilisateur dispose d'un «algorithme» qui décrit en détail comment on passe, au
moyen d'un nombre fini d'opérations, des données aux résultats. L'algorithme (recette) est rédigé
en langage humain, bien trop riche et complexe pour être compris par la machine. L'utilisateur
devra tout d'abord transposer son algorithme en un jargon simpliste (un «langage de programmation
») pour obtenir un «programme» (on dit aussi «code» ou «code source») équivalent. Ce
programme utilisateur sera traduit à son tour en une suite d'instructions primitives effectivement
compréhensibles et exécutables par l'ordinateur. Cette traduction est réalisée par un programme
spécialisé, le «compilateur». A chaque langage de programmation correspond un compilateur, qui
porte le même nom ; ainsi ce cours est une introduction à la programmation en C++, qui utilisera
pour les exercices pratiques un compilateur C++. Le programme source est rangé dans un fichier
situé sur le disque dur ou sur une disquette. Sauf instructions spéciales, le compilateur ne connaît
que le contenu de ce fichier source. Le programme en langage machine (repéré par le suffixe .exe
sous DOS et Windows) est lui aussi rangé dans un fichier (en général dans le même dossier que le
source). Il peut alors être exécuté autant de fois que l'utilisateur le souhaite.
Pour certains langages de programmation (Basic à ses début, Maple, Scilab par exemple), on
utilise un «interprèteur» qui traduit le «code utilisateur» en langage machine ligne par ligne à
chaque exécution.
Il est important de se persuader du fait que l'ordinateur traite, selon les instructions qu'il a
reçues, des chiffres binaires (des bits ou des octets) sans se préoccuper le moins du monde de
leur signification ; les données peuvent être des résultats d'une mesure physique, un morceau de
musique numérisé, une carte météo fournie par un satellite, un texte à traduire en anglais, c'est tout
pareil pour la machine. C'est à l'utilisateur (à travers son programme) d'assurer l'interprétation
des données et des résultats.
1
Chapitre 2
Les éléments du langage
Les langages de programmation, comme toute langue, utilisent des lettres pour former des
mots ; ces mots peuvent être groupés en petites phrases simples que l'on appelle en général des
instructions. Ces phrases doivent être construites en respectant un certain nombre de règles de
syntaxe. Je présente dans ce chapitre les constituants de base des langages C/C++.
2.1 lettres et mots
Le langage C++ utilise 52 lettres : a, b, . . ., z et A, B, . . ., Z. Le compilateur C++ distingue
parfaitement les majuscules des minuscules. On emploie également les signes usuels : =, +, -, *,
/,(,),[,],{,},
> , < , &,%, ?,|, n , la virgule, le point, deux points :, le point-virgule, l'apostrophe (') et
les guillemets anglais(", sous le 3). Certains de ces symboles peuvent être combinés entre eux.
Le langage C est formé de « mots réservés» ou «mots clés», qui doivent être utilisés comme
prévu par la définition du langage ; en voici la liste :
auto double int struct
break else long switch
case enum register typedef
char extern return union
const float short unsigned
continue for signed void
default goto sizeof volatile
do if static while
Le langage C++ ajoute les mots clés suivants
asm bool catch class const_cast
delete dynamic_cast explicit false friend
inline mutable namespace new operator
private protected public reinterpret_cast
static_cast template this throw true
try typeid typename using virtual
wchar_t
La signification de certains de ces mots sera expliquée par la suite. On emploie bien d'autres
mots (toutes les fonctions mathématiques par exemple). Ces mots (identificateurs) ne font pas
partie de la norme C/C++ et peuvent
en principe être redéfinis par l'utilisateur.
2
Intro_C
3
Dans le temps imparti pour ce cours, il ne sera pas possible d'examiner (et encore moins
d'assimiler) tous les aspects du langage C++ : il s'agira plutôt d'une version simplifiée du langage,
proche du C, du C+ en quelque sorte, ou encore du C amélioré.
2.2 Premiers exemples
Je présente maintenant quelques programmes très simples en C++, avec leurs équivalents en
C pur (et en Pascal pour ceux qui connaissent ce langage). Ce premier exemple n'utilise aucune
donnée et a pour seul résultat un affichage à l'écran.
2.2.1 le programme traditionnel
1
// bonjour_2 . cpp : a f f i c h e un message t r a d i t i o n n e l
2
#include <ios t ream>
3
#include <c s t d l i b >
4
int main ( void ){
5 s td : : cout << "Bonjour tout l e monde ! " << s td : : endl ;
6 system( " pause " ) ;
7
return 0 ;
8 }
La numérotation des lignes ne fait PAS partie du programme ou du langage, elle est là pour
faciliter les explications.
Sur la ligne 1, j'ai placé un commentaire (une ligne commençant par //) : c'est du texte
destiné aux utilisateurs, la machine n'en tient aucun compte. Un commentaire expliquant le rôle
de chaque élément d'un programme permet au lecteur de comprendre sans trop de mal ce que fait
ce programme. Le compilateur considère comme un commentaire tout ce qui se trouve entre // et
une fin de ligne.
Sur la ligne 2 (et aussi 3), on rencontre une « directive du préprocesseur » laquelle commence
toujours par # et dont le rôle est de réclamer l'utilisation d'une partie de la «librairie standard».
Le préprocesseur agit comme une avant-garde du compilateur ; il va insérer (inclure) dans mon
programme un fichier entête contenant des renseignements concernant les fonctions responsables
de la lecture des données et de l'écriture des résultats (
i nput- o utput). Celles-ci ne font pas partie
du langage C++ de base. Même chose pour la ligne 3, qui rend possible l'accès à la fonction
system
(communication avec le système d'exploitation).
La ligne 4 contient le nom de l'élément principal (
main ) du programme. Ce nom doit figurer
une fois et une seule
. Les parenthèses indiquent au compilateur qu'il s'agit en fait d'une fonction ;
elles contiennent le mot-clé
void parce que la fonction main ne demande ici aucun argument.
Un programme plus complexe pourrait comporter de nombreuses fonctions, dont une seule devra
s'appeler
main . D'autre part, on indique au système d'exploitation (DOS, Windows,Linux,. . .), par
le mot réservé
int , que main « fournit un résultat», sous la forme d'un entier. Cet entier permet
en principe de vérifier la bonne fin du programme.
La ligne 4 contient une accolade ouvrante, qui marque le début de la fonction proprement dite ;
cette fonction se termine par l'accolade fermante de la ligne 8. Tout ce qui se trouve entre les deux
accolades s'appelle le «corps» de la fonction. On dit aussi que les instructions situées entre une
paire d'accolades forment un «bloc».
La ligne 5 contient la seule instruction effective du programme. Elle demande l'insertion, dans
le «flot de sortie», de la «chaîne de caractères»
Bonjour tout le monde ! , suivie d'une instruction
destinée au terminal (ici l'écran),
endl (à la ligne) ce qui, en français, signifie que l'on veut afficher
à l'écran la phrase
Bonjour tout le monde ! et positionner le curseur au début de la ligne suivante.
Intro_C
4
Comme l'opérateur
cout et le caractère endl font partie de la librairie standard, on préfixe ces
deux noms par
std:: .
La ligne 6 demande au système de marquer un temps d'arrêt, pour que l'on puisse lire le
résultat. Le déroulement normal (retour à l'écran d'accueil) reprend dès que l'on appuie sur une
touche. On voit que la fonction
system admet un argument, la «chaîne de caractères» pause .
On a enfin (ligne 7) une instruction
return 0 , qui envoie au système d'exploitation le «résultat
» de la fonction main, la valeur 0, indiquant que tout s'est bien passé (un résultat non nul
indiquerait une erreur). Le compilateur vérifiera que la déclaration de la fonction (
int main() ),
de type entier, est bien cohérente avec la nature du résultat (l'entier 0).
On remarque que chaque instruction se termine (et DOIT se terminer) par un point-virgule.
Les éléments précédents sont toujours, ou presque toujours, présents dans un programme en C++.
Le programme qui vient d'être présenté respecte strictement la norme C++; les compilateurs
dont vous pourrez disposer respecte plus ou moins strictement cette norme. Il se peut que votre
compilateur admette ou préfère l'entête
#include <iostream.h> . Il est aussi possible (c'est le cas
du compilateur que j'utilise) qu'il ne soit pas regardant sur la présence des préfixes
std:: et que
main()
le satisfasse. Ce comportement voisin de l'anarchie est du à ce que la norme qui définit le
C++ est récente et continue d'évoluer.
2.2.2 le même en C
Voici maintenant le même programme en C pur ; il sera parfaitement compris par un compilateur
C++, mais l'inverse n'est pas vrai : les mots-clés du C++ ne sont pas connus du C.
1 /*bonjour.c: Affiche un message traditionnel*/
2 #include <stdio.h>
3 int main()
4 {
5 printf("Bonjour tout le monde!\n");
6 return 0;
7 }
Examinons les quelques différences qui existent, à ce niveau, entre les deux dialectes.
Sur la ligne 1, un commentaire est indiqué par une ligne commençant par /* et finissant par
*/.
Sur la ligne 2, la « directive du préprocesseur» se réfère à un fichier entête (
h eader) dont le
nom est différent des précédents mais dont le rôle identique : insérer des renseignements concernant
les fonctions responsables de la lecture des données et de l'écriture des résultats (
i nput- o utput).
Celles-ci ne font pas partie, en toute rigueur, du langage C.
La ligne 3 appelle la fonction principale, et les parenthèses vides indiquent que cette fonction
n'attend pas d'arguments.
La ligne 5 est une instruction d'écriture (appel à la fonction
printf ). Cette fonction reçoit un
argument, la chaine de caractères (entre guillemets anglais)
Bonjour tout le monde ! . Cette chaîne
est suivie d'un «caractère d'échappement»
\n , qui demande de passer à la ligne.
2.2.3 le même en Pascal
Voici maintenant un programme équivalent, rédigé en Pascal.
Intro_C
5
1 {Premier programme}
2
3 PROGRAM bonjour;
4 BEGIN
5 WRITELN('Bonjour tout le monde!');
6
7 END.
Le mot clé
program doit être présent. Le corps du programme est compris entre BEGIN et
END. Les chaines de caractères sont écrites entre apostrophes et non entre guillemets. Bien que
Pascal ne distingue pas entre majuscule et minuscule, il est commode de choisir l'une des casses
pour les mots du langage, l'autre pour les variables définies par l'utilisateur.
2.2.4 format libre
Dans les trois langages, la position des mots sur la ligne est indifférente (on parle de format
libre). Le compilateur s'y retrouve grace aux mots réservés (ou aux accolades) et aux points-virgules
qui terminent chaque instruction (sauf les instructions destinées au préprocesseur, qui doivent être
chacune seule sur sa ligne, peut-être avec un commentaire). Ainsi, j'aurais pu écrire le programme
C++ précédent sous la forme
1 /*Premier programme horrible*/ #include <iostream>
2 int main(void){std::cout<<"Bonjour tout le monde!"<<std::endl;return 0;}
La lisibilité n'est pas la même. Il y a grand intérêt à mettre en évidence la logique du programme
à l'aide de la mise en page du texte. Chaque programmeur doit doit créer sa propre présentation,
combinant clarté, lisibilité et économie de place.
2.2.5 saisir des nombres au clavier
Le programme suivant (addition de deux entiers), va nous permettre de découvrir quelques
caractéristiques supplémentaires du langage.
1
//somme . cpp : l i t deux e n t i e r s e t a f f i c h e l e u r somme
2
#include <ios t ream>
3
#include <c s t d l i b >
4
using s td : : c in ; using s td : : cout ; using s td : : endl ;
5
int main ( void ){
6
int nb1 , nb2 , somme ;
7 cout << "donner l e premier e n t i e r : \n" ;
8 c in >> nb1 ;
9 cout << "donner l e deuxième e n t i e r : " << endl ;
10 c in >> nb2 ;
11 somme = nb1 + nb2 ;
12 cout << " l a somme vaut : " << somme << "\n" ;
13 system( " pause " ) ;
14
return 0 ;
15 }
Intro_C
6
J'ai fait savoir au compilateur (ligne 4) que j'allais utiliser les opérateurs standard
cin, cout
et
endl , ce qui va me dispenser de réécrire std:: à chaque utilisation.
Sur la ligne 6, j'ai déclaré les variables que je veux utiliser. Beaucoup de recettes de cuisine
sont rédigées de cette façon : on énonce les ingrédients requis avant de donner les instructions pour
confectionner le plat. Ces variables sont toutes des entiers. Un nom de variable (un «identificateur»)
ne doit pas commencer par un chiffre et peut comporter jusqu'à 31 lettres ou chiffres (le seul autre
caractère permis est le souligné _). Le nom d'une variable doit être significatif et aider à la
compréhension du programme (par exemple prix_par_kilo plutôt que pk3). En C, les déclarations
sont placées avant toute instruction exécutable. Le C++ admet que les variables soient déclarées
juste avant leur utilisation ; cela diminue les risques d'erreurs dans les grands programmes.
Les lignes 7 et 9 contiennent deux instructions (injection dans le flot de sortie) d'écriture à
l'écran, comme précédemment. J'ai mélangé diverses forme du retour à la ligne, toutes compréhensibles
par C++.
Les lignes 8 et 10 réalisent l'opération inverse : l'extraction de deux valeurs à partir du « flot
d'entrée», ou encore la lecture de deux entiers tapés au clavier.
À l'exécution des lignes 7 et 8 par exemple, l'écran affichera
donner le premier entier:
et l'ordinateur attendra (curseur à gauche de l'écran) que l'utilisateur tape la valeur de nb1, suivie
de
< return > ou < entrée > .
L'instruction de la ligne 10 est une «affectation» : la variable
somme reçoit une valeur, laquelle
est justement la somme nb1+nb2.
On se rend compte que les variables (ou les identificateurs) n'ont pas d'existence réelle ; ce sont
des noms (plutôt des pseudonymes) pour des emplacements dans la mémoire de la machine. La
déclaration d'une variable sert d'une part à réserver un emplacement, d'autre part à définir sa
taille, car toutes les variables n'ont pas le même encombrement.
2.2.6 la version C
Je présente maintenant et pour la dernière fois, la version C pure.
1 /*somme.c: lecture de deux entiers et affichage de leur somme*/
2 #include <stdio.h>
3 #include <stdlib.h>
4 int main(void)
5 {
6 int nb1, nb2, somme;
7 printf("donner le premier entier: \n");
8 scanf("%d", &nb1);
9 printf("donner le deuxième entier: \n");
10 scanf("%d", &nb2);
11 somme = nb1 + nb2;
12 printf("la somme vaut %d\n", somme);
13 system("pause");
14 return 0;
15 }
Sur la ligne 6, j'ai déclaré les variables que je veux utiliser.
Les lignes 7 et 9 contiennent deux instructions (appels de la fonction printf) d'écriture à l'écran,
comme précédemment.
Intro_C
7
À la ligne 8 (de même qu'en 10), on voit apparaître une nouvelle fonction,
scanf , chargée de
capter ce que l'utilisateur tape au clavier. Cette fonction utilise deux arguments. Le premier est
une chaine de caractères qui décrit la nature de l'information à saisir, ici un entier (le symbole
%d ).
Le deuxième est le nom de la variable qui doit recueillir l'information, l'un des identificateurs
nb1
ou
nb2 , précédé du symbole magique & (la raison de cette construction deviendra claire plus tard).
Cette complication est une source assez fréquente d'erreurs chez les débutants en C.
2.2.7 la version Pascal
Terminons ce paragraphe en citant un programme équivalent en Pascal.
1 {Deuxième programme}
2
3 PROGRAM addition;
4 VAR nb1, nb2, somme: integer;
5 BEGIN
6 WRITE('donnez le premier entier: '); READLN(nb1);
7 WRITE('donnez le deuxième entier: '); READLN(nb2);
8 somme := nb1 + nb2;
9 WRITELN('voici leur somme':, somme);
10 END;
On remarque une nouvelle différence entre les deux langages : le « = » du C (affectation) est
l'équivalent du « := » du Pascal. Je profite de l'occasion pour vous faire remarquer la différence
fondamentale qui existe entre affectation et égalité. La phrase «si x = 3 alors . . .» effectue une
comparaison entre x et 3, sans modifier la valeur de x ; par contre, «posons x = 3» modifie bien cette
variable : elle lui affecte la valeur 3. Tous les langages de programmation modernes différencient
ces deux significations du signe «égal».
2.3 Arithmétique en C
Le programme précédent vous a permis de voir comment on additionnait deux entiers. En fait,
chaque opération arithmétique a son équivalent en C++ : on combine deux variables au moyen
d'un « opérateur», comme indiqué dans le tableau suivant.
Opération Opérateur Expression instruction C
arithmétique
Addition + a + x a + x
Soustraction - a - b a - b
Multiplication * bx b*x
Division / a/b a/b
reste modulo % r mod s r%s
2.3.1 priorités
Pour que le résultat d'une opération arithmétique comportant plusieurs « opérandes» soit bien
défini, il faut établir des règles de priorité ; pour le C++, comme pour la plupart des langages
de programmation, l'ordre de priorité décroissante est : parenthèses, multiplication ou division,
addition ou soustraction. Une expression faisant intervenir des opérateurs de même priorité est
Intro_C
8
évaluée de gauche à droite. Les quelques exemples qui suivent montrent que l'arithmétique en
C++ est en fait très simple.
2 + 3
¡ 4 = 1 ; 6 = 2 ? 3 = 9 ; 6 ? 2 = 3 = 4 ; 3 ¡ 2 ? 2 = ¡ 1 ; 1 + 9 = 3 ¡ 2 = 2
2
? (4 ¡ 1) = 6 = (1 + 2 + 3 + 4 + 5 + 6) = 3 ¡ 1
Évaluons un trinôme du second degré,
y = ax 2 + bx + c , avec a = 2 , b = 3 , c = 7 et
x
= 5 . Comme la fonction puissance n'est pas encore définie, l'instruction correspondante s'écrira
y = a*x*x + b*x +c
. Le diagramme montre l'ordre de opérations successives, indiquées par des
numéros entre parenthèses.
y = 2 * 5 * 5 + 3 * 5 + 7
(6) (1) (2) (4) (3) (5)
En d'autres termes, l'expression de
y prend les formes successives suivantes :
y
= 10 ? 5 + 3 ? 5 + 7 = 50 + 3 ? 5 + 7 = 50 + 15 + 7 = 65 + 7 = 72 :
L'affectation d'une valeur à
y est la dernière opération effectuée.
2.4 Prendre des décisions : les opérateurs de relation
On peut comparer deux variables à l'aide d'un «opérateur de relation», comme dans l'expression
suivante :
x < y . Cette expression ne peut prendre que deux valeurs, vrai (si y est effectivement
plus grand que
x ) ou faux (autres cas). On parle souvent «d'expression logique» pour désigner
ce type de construction. Le C++ reconnait 6 opérateurs de relation, rassemblés dans le tableau
ci-dessous.
Expression algébrique Expression en C signification
=
x == y x égal à y
6
= x ! = y x différent de y
< x < y x
plus petit que y
> x > y x
plus grand que y
·
x < = y x plus petit que ou égal à y
?
x > = y x plus grand que ou égal à y
Je le répète : tous les langages modernes représentent de manière différente l'affectation (= en
C++, := en Pascal) et la comparaison, == en C++, = en Pascal.
2.4.1 petit exemple
La façon la plus courante d'utiliser une expression logique construite autour d'un opérateur de
relation consiste à employer le mot réservé « if » (si) dans des instructions traduisant des phrases
comme « si
a est plus grand que b , alors...», comme dans l'exemple rudimentaire qui suit.
1
//compar . cpp : l e c t u r e e t comparaison de deux e n t i e r s
2
#include <ios t ream>
3
#include <c s t d l i b >
4
using namespace s td ;
5
int main ( void ){
6
int nb1 , nb2 ;
7 cout << "donner moi deux e n t i e r s e t j e vous d i r a i \n" ;
8 cout << " q u e l l e r e l a t i o n e x i s t e ent r e eux : " << endl ;
9 c in >> nb1 >> nb2 ;
Intro_C
9
10
i f ( nb1 == nb2 ) cout << nb1 << " e s t é g a l à " << nb2 << endl ;
11
i f ( nb1 != nb2 )
12 cout << nb1 << " n ' e s t pas é g a l à " << nb2 << endl ;
13
i f ( nb1 < nb2 )
14 cout << nb1 << " e s t plus p e t i t que " << nb2 << endl ;
15
i f ( nb1> nb2 ) cout << nb1 << " e s t PLUS GRAND que " << nb2 << endl ;
16
i f ( nb1 <= nb2 )
17 cout << nb1 << " e s t plus p e t i t que ou é g a l à " << nb2 << endl ;
18
i f ( nb1 >= nb2 )
19 cout << nb1 << " e s t plus grand que ou é g a l à " << nb2 << endl ;
20 system( " pause " ) ;
21
return 0 ;
22 }
Le programme comporte (ligne 4) une abbréviation plus puissante que la précédente ; plutôt
que d'écrire
std::cout à chaque instruction d'affichage, ou encore using std::cout et ses analogues
pour chaque opérateur, je préviens une fois pour toute que je veux pouvoir utiliser tous les
identificateurs qui font partie de la bibliothèque standard.
J'ai écrit (ligne 9) une instruction de lecture un peu plus compliquée, qui capte les valeurs
de deux entiers. Les deux valeurs peuvent être séparées par un espace ou un passage à la ligne.
Viennent ensuite 6 instructions, allant chacune d'un
if à un point-virgule dont la signification est
la suivante : si l'affirmation entre parenthèses est vraie, alors on exécute l'affichage correspondant ;
sinon, on passe à l'instruction suivante. J'ai joué avec les blancs et les passages à la ligne pour
rendre le programme lisible et varié ( ?). Les parenthèses qui entourent la condition qui suit chaque
if
sont obligatoires.
2.5 Programmer par vous-même
Nous venons de passer en revue quelques éléments du langage C++; ils sont suffisants pour
écrire des programmes simples. Comment cela se passe-t-il en pratique ? En gros, il y a cinq étapes.
- Concevoir l'algorithme. C'est la partie la plus importante, mais elle est souvent passée sous
silence. Pour gagner du temps dans les cours de programmation, on fournit souvent l'algorithme
en même temps que l'exercice à résoudre. Il est cependant très souhaitable de réfléchir
quelques instants à la méthode que l'on va employer, aux variables que l'on devra utiliser,
avant d'écrire la moindre ligne de programme. Tout informaticien devrait s'inspirer de cette
phrase attribuée à Racine :
«Ma pièce, Phèdre, est terminée, je n'ai plus que les vers à écrire.»
- Rédiger le programme sur papier ou directement dans l'ordinateur, à l'aide d'un traitement
de texte, le sauvegarder sur disque ou disquette.
- Compiler le programme.
- Corriger les erreurs détectées par le compilateur et revenir à l'étape précédente, jusqu'à disparition
des messages d'erreur.
- Exécuter le programme.
- Corriger les erreurs, améliorer le programme.
Les étapes 2, 3, et 4 peuvent s'exécuter commodément à l'aide d'un «environnement de programmation
intégré» qui permet d'alterner sans heurt rédaction et compilation. Les disques durs
Intro_C
10
des machines mises à votre disposition comportent un ou plusieurs compilateurs C++. par ailleurs,
ces logiciels sont en général gratuits et téléchargeables par tous ceux qui ont la patience nécessaire.
Ainsi, si vous travaillez sous Windows, vous pouvez vous procurer :
DJGPP, qui fonctionne sous DOS ou dans une fenêtre DOS sous Windows. Il est accompagné de
l'interface graphique RHIDE, qui est une copie de celle de Turbo-Pascal (http : http://www.delorie.com
/djgpp/).
DevC++, qui fonctionne sous Windows et permet d'accéder à l'API Windows (http : //
www.bloodshed.net/)
On peut trouver, sur le site de Borland, des versions gratuites, plus ou moins simplifiées, de
Turbo-C++.
Les personnes qui travaillent sous Linux bénéficient des logiciels gratuits que l'on trouve naturellement
pour ce système d'exploitation, en particulier le compilateur g++ et le traitement de
texte emacs.
Il existe par ailleurs un très grand nombre de sites consacrés à l'enseignement du C ou du C++;
vous trouverez ici : http ://www.developpez.com/c/ un bon point de départ.
Enfin, la plupart des questions que l'on peut se poser sur le langage C++ trouvent leur réponse
dans la Foire Aaux Questions (FAQ) : http ://www.research.att.com/ austern/csc/faq.html
Dernière remarque : les fichiers de programme rédigés en C doivent comporter le suffixe
.c , alors
que ceux qui sont écrits en C++ doivent s'appeler
nnnn.cpp pour ne pas troubler le compilateur.
Chapitre 3
Ça se complique : les structures de
programmation
Les instructions d'un programme sont exécutées l'une après l'autre, dans l'ordre où elles sont
écrites. Cette « exécution séquentielle» n'est pas toujours souhaitable. Ainsi, le dernier programme
du chapitre précédent comportait des ordres d'impression qui n'étaient exécutés que lorsque certaines
conditions étaient remplies. Dans la pratique, on rencontre souvent des enchaînements plus
compliqués. Il arrive aussi que l'on souhaite répéter certaines instructions un grand nombre de
fois ; il serait pénible d'avoir à écrire un nombre égal de lignes de programme. Les «structures
de programmation» présentées dans ce paragraphe permettent justement de choisir avec précision
l'enchaînement et/ou la répétition des instructions d'un programme.
3.1 Choix
3.1.1 deux cas seulement
Je commence par compléter ce qui a été dit au paragraphe précédent concernant la construction
« si. . .alors. . .». Je trouve commode de mettre en évidence la logique du programme en rédigeant
d'abord une ébauche en «pseudo-code». À titre d'exemple, j'envisage d'imprimer des résultats
d'examen ; l'une des actions à réaliser se résume en
si note
> = 10, alors imprimer reçu.
L'avantage de cette formulation est qu'elle se traduit immédiatement en C++ :
if (note >= 10)
cout << "Reçu\n";
Certains préfèrent une représentation graphique (organigramme), avec des flèches représentant les
cas vrai ou faux ; ces représentations sont équivalentes, mais le pseudo-code se généralise plus
facilement à un algorithme complexe.
Prenons maintenant en compte le cas des candidats ajournés, au moyen de la structure
si note
> = 10, alors imprimer reçu, sinon imprimer ajourné.
La version C++ s'écrit
if (note >= 10)
cout << "Reçu\n";
else
cout "Ajourné\n";
11
Intro_C
12
Il n'y a qu'une différence avec Pascal : dans ce langage, chaque
if doit être associé à un then
3.1.2 l'opérateur ternaire « ? »
On dispose de plus en C/C++ d'un «opérateur conditionnel» à
trois opérandes , qui permet de
résumer le code précédent en une ligne, comme ceci :
note >= 10 ? cout << "Reçu" : cout << "Ajourné";
Si la condition est vraie, on exécute la première instruction, si elle est fausse, la deuxième. Remarquez
les deux points qui servent à séparer les deux instructions possibles. On peut parvenir au
même résultat d'une autre façon, plus obscure :
cout << ( note >= 10 ? "Reçu" : "Ajourné");
La parenthèse contient une expression logique (la condition
note >= 10 ), l'opérateur « ? » et deux
chaînes de caractères. Si la condition est vraie, l'ensemble prend une valeur égale au deuxième
argument (ici la chaîne
Reçu ) ; dans le cas contraire, l'ensemble prend la valeur du troisième
argument (la chaîne
Ajourné ). Dans l'un ou l'autre cas, le résultat du ? est injecté dans le flot
de sortie.
3.1.3 beaucoup de cas
Pour tenir compte de l'existence de mentions, je suis amené à compliquer le schéma précédent.
Je pars de l'ébauche
si note
> = 16, imprimer TB
sinon
si note
> = 14 imprimer B
sinon
si note
> = 12 imprimer AB
sinon
si note
> = 10 imprimer P
sinon imprimer Ajourné
dont la traduction en C++ peut s'écrire de plusieurs façons, comme par exemple
if (note >= 16)
cout << "TB\n";
else
if (note >= 14)
cout << "B\n";
else
if (note >= 12)
cout << "AB\n";
else
if (note >= 10)
cout << "P\n";
else
cout << "Ajourné\n";
if (note >= 16)
cout << "TB\n";
else if (note >= 14)
cout << "B\n";
else if (note >= 12)
cout << "AB\n";
else if (note >= 10)
cout << "P\n";
else
cout << "Ajourné\n";
La forme de droite est sans doute préférable car tout aussi compréhensible mais moins encombrante.
Dans la vie courante comme en programmation, un « si » peut très bien commander plusieurs
actions. Pour réaliser cela en C++, je dois créer une « instruction composée » (ou un «bloc»
d'instructions), formée de plusieurs instructions simples entourées d'accolades, comme ceci :
Intro_C
13
if (note >= 10)
cout << "Reçu\n");
else {
cout << "Ajourné" << endl;
cout << "Prenez rendez-vous avec votre enseignant" << endl;
}
Toutes les instructions entre { et } sont solidaires et seront exécutées en bloc (de même qu'en
Pascal, tout ce qui se trouve entre BEGIN et END).
3.2 Répétitions
3.2.1 tant que
J'aborde maintenant un premier exemple de répétition programmée. Je vais utiliser une construction
analogue au pseudo-code suivant
tant qu'on ne l'a pas fait dix fois
répéter l'action demandée
En C++ (et en anglais), « tant que » se dit « while ». Je me propose de lire au clavier 10
entiers et d'en calculer la somme. C'est le rôle du programme
while1.cpp .
1
// whi l e1 . cpp : r é p é t i t i o n s impl e
2
#include <ios t ream>
3
#include <c s t d l i b >
4
using namespace s td ;
5
int main ( void ){
6
int compteur , nombre , somme ;
7
/ ? i n i t i a l i s a t i o n ? /
8 somme = 0 ; compteur = 1 ;
9
/ ? r é p é t i t i o n ? /
10
while ( compteur <= 10) {
11 cout << " ent r e z un nombre : " ;
12 c in >> nombre ;
13 somme = somme + nombre ;
14 compteur = compteur + 1 ;
15 }
16
/ ? c onc lus i on ? /
17 cout << " l a somme des dix nombres e s t : " << somme << endl ;
18 system( " pause " ) ;
19
return 0 ;
20 }
Il faut faire attention à la phase d'initialisation. Certains langages mettent systématiquement à
zéro toutes les variables avant d'exécuter la première instruction ; tel n'est pas le cas du C++. Sans
initialisation, le programme peut commencer avec une valeur de
compteur égale à ce qui traîne
dans la mémoire à cet emplacement : note de musique, morceau de texte. . .et ne jamais s'exécuter
ou ne jamais s'arrêter.
Il arrive souvent que je ne sache pas combien de fois il faut répéter une action ; c'est le cas pour
une liste de courses : je m'arrêterai de dépenser lorsque la liste sera épuisée, soit de façon plus
formelle
tant qu'il reste des objets sur ma liste
chercher et acheter l'objet suivant
Intro_C
14
rayer son nom sur la liste.
L'ordinateur ne sait pas (pas encore) interrompre la répétition quand il arrive au bout d'une
liste. Il revient au même de lui demander d'arrêter lorsqu'apparaît une valeur «anormale» dans
la liste ; on dit que la liste se termine par un «drapeau» ou une «sentinelle». Je me propose de
calculer la taille moyenne des élèves d'une classe ; il est commode de terminer la saisie des données
en fournissant une valeur négative ou nulle, comme dans
while2.cpp .
1
// whi l e2 . cpp : c a l c u l e l a moyenne de p l u s i e u r s nombres
2
#include <ios t ream>
3
#include <c s t d l i b >
4
using namespace s td ;
5
int main ( void ) {
6
int compteur ;
7
f loat t a i l l e , moyenne , somme ;
8
/ ? i n i t i a l i s a t i o n ? /
9 somme = 0 ; compteur = 0 ;
10
/ ? t r a i t ement ? /
11 cout << " ent r e z une t a i l l e (m, <= 0 pour f i n i r ) : " ;
12 c in >> t a i l l e ;
13
while ( t a i l l e > 0) {
14 somme = somme + t a i l l e ;
15 compteur = compteur + 1 ;
16 cout << " ent r e z une t a i l l e (m, <= 0 pour f i n i r ) : " ;
17 c in >> t a i l l e ;
18 }
19
/ ? c onc lus i on ? /
20 moyenne = somme/ compteur ;
21 cout << " l a t a i l l e moyenne e s t : " << moyenne << endl ;
22 system( " pause " ) ;
23 }
Il y a plusieurs nouveautés dans ce programme. Sur la ligne 7, j'ai déclaré trois variables qui
représentent des nombres fractionnaires (on dit parfois à virgule flottante, SINGLE en Pascal), ce
qui est assez normal pour des tailles exprimées en mètres. Vous remarquerez que l'initialisation
est différente de celle de l'exemple précédent (compteur = 0). D'autre part, pour que la condition
d'arrêt (taille
< = 0 ) ait un sens, il faut que taille soit connue, ce qui m'impose de le lire une fois
AVANT la boucle, puis une fois à chaque tour de boucle.
3.2.2 opérateurs composés
Les auteurs du C/C++ et bien des amateurs de ces langages aiment les instructions concises ;
c'est peut-être pourquoi la ligne 15 peut aussi s'écrire
compteur += 1;
Plus généralement, toute modification d'une variable de la forme
variable = variable opérateur expression
peut aussi s'écrire
variable opérateur= expression
où opérateur est l'un des «opérateurs arithmétiques» définis au §2. Attention à ne pas introduire
de blanc entre opérateur et =. On peut compacter la ligne 14 de la même façon.
Intro_C
15
3.2.3 Opérateurs d'incrémentation/décrémentation
Lorsque l'on manipule un compteur (ou un indice), on est souvent amené à augmenter cette
variable d'une unité ; le langage C++ propose une instruction compacte et assez commode pour
ce faire. En fait, les trois instructions ci-dessous sont équivalentes :
i = i+1 ; i += 1 ; i++ ;
Les choses se compliquent un peu lorsque la structure
i++ est incluse dans une expression. La
variable
i i est utilisée puis incrémentée. On emploie souvent le terme de post-incrémentation pour
désigner ce comportement. Il est aussi possible d'augmenter
i de un avant de l'utiliser, au moyen
d'un opérateur de pré-incrémentation,
++i; Vous ne serez pas surpris d'apprendre qu'il existe
aussi des opérateurs de pré- ou post-décrémentation, qui diminuent de un la variable à laquelle ils
s'appliquent, comme dans l'instruction
indice--;
Ces opérateurs sont rarement utilisés seuls, mais ils apparaissent très souvent dans l'écriture des
répétitions, comme expliqué plus loin. En attendant, je vous propose d'observer le fonctionnement
du programme
incr.cpp , qui met en oeuvre plusieurs opérateurs d'incrémentation.
1
/ ? inc r . cpp : pre ¡ e t pos t ¡ inc r ementat ion ? /
2
#include <ios t ream>
3
#include <c s t d l i b >
4
using namespace s td ;
5
int main ( void ){
6
int i ;
7 i = 5 ;
8 cout << i << endl ;
9 cout << i++ << endl ;
10 cout << i << endl << endl ;
11 i = 5 ;
12 cout << i << endl ;
13 cout << ++i << endl ;
14 cout << i << endl ;
15 system( " pause " ) ;
16
return 0 ;
17 }
3.2.4 Encore une répétition : la boucle «for»
Examinons d'abord une répétition utilisant un «while». Je me propose d'imprimer, au moyen
d'un programme en C++, les 12 premiers nombres entiers, leur carré et leur cube. Le programme
ci-dessous convient.
1
// whi l e3 . cpp b ouc l e " wh i l e "
2
#include <ios t ream>
3
#include <c s t d l i b >
4
using namespace s td ;
5
int main ( void ) {
6
int compteur = 1 ;
7
while ( compteur <= 12) {
8 cout << compteur << ' \ t ' << compteur
? compteur << ' \ t '
9 << compteur
? compteur ? compteur << ' \n ' ;
10 compteur += 1 ;
11 }
12 system( " pause " ) ;
13
return 0 ;
Intro_C
16
14 }
J'ai utilisé (ligne 6) un nouveau raccourci ; la variable
compteur y est simultanément définie et
initialisée. De plus, j'ai introduit plusieurs caractères de tabulation,
\t ; il s'agit bien d'un seul
caractère, que l'on peut mettre entre apostrophes.
Une instruction «for » peut être considérée à peu près comme un condensé des lignes 6 à 11 du
programme précédent.
1
// f o r 1 . cpp : b ouc l e " f o r " e l ement a i r e
2
#include <ios t ream>
3
#include <c s t d l i b >
4
using namespace s td ;
5
int main ( void ){
6
int compteur ;
7
for ( compteur = 1 ; compteur <= 1 2 ; compteur++)
8 cout << compteur << ' \ t ' << compteur
? compteur << ' \ t '
9 << compteur
? compteur ? compteur << ' \n ' ;
10 system( " pause " ) ;
11
return 0 ;
12 }
La variable
compteur est déclarée ligne 6 et initialisée ligne 7. Malgré les apparences de la mise en
page, l'opérateur
for ne gouverne qu'une seule instruction, qui commence par cout et se répend
sur deux lignes. Pour répéter un bloc d'instructions, il faut les placer entre accolades.
3.2.5 équivalence «while»-«for»
La structure générale d'une instruction comme celle de la ligne 7 est
for (initialisation ; condition pour continuer ; in- ou dé-crémentation)
instruction ou bloc d'instructions
Plus généralement, on peut écrire
for(Expression_1,Expression_2,Expression_3)
instruction
ce qui est pratiquement équivalent à la construction
Expression_1 ; (initialisation)
while{Expression_2}{ (condition d'arrêt)
instruction
Expression_3 ; (incrémentation)
}
3.2.6 jusqu'à
Tous les exemples de répétition que je viens de présenter ont une caractéristique commune :
on vérifie si une condition est vraie avant d'exécuter la (ou les) instructions de la boucle. Si la
condition est fausse dès le début, la boucle n'est jamais parcourue. Il est parfois utile d'effectuer
cette vérification après avoir décrit au moins une fois le corps de boucle. Au lieu de : tant que
Intro_C
17
(condition), répéter, on voudrait : répéter, jusqu'à ce que (condition). Cette distinction existe en
Pascal (while et repeat. . .until) ; elle existe aussi, sous une forme un peu différente, en C++, comme
le montre le code suivant, une transposition du programme
while3.cpp .
1
// jus qua . cpp : b ouc l e r e p e t e r . . . jus qu ' a ce que
2
#include <ios t ream>
3
#include <c s t d l i b >
4
using namespace s td ;
5
int main ( void ) {
6
int compteur = 1 ;
7
do {
8 cout << compteur << ' \ t ' << compteur
? compteur << ' \ t '
9 << compteur
? compteur ? compteur << endl ;
10 compteur += 1 ;
11 }
12
while ( compteur <= 1 2 ) ;
13 system( " pause " ) ;
14
return 0 ;
15 }
3.3 Choix entre plusieurs possibilités : switch
Il peut m'arriver d'avoir à choisir entre plusieurs possibilités d'égale importance. Dans ce cas,
plutôt que d'avoir recours à une enfilade de
if . . . else ,je peux utiliser la structure switch , comme
le montre l'exemple un peu artificiel
switch.cpp . Il s'agit cependant d'un programme nettement
plus compliqué que les précédents et il n'est pas nécessaire d'assimiler dès maintenant tous les détails.
L'utilisateur va taper au clavier un certain nombre de voyelles et le programme lui répondra
combien il y avait de a, de e,. . .
1
// swi t ch1 . cpp : s t r u c t u r e swi t c h pour compter des v o y e l l e s
2
#include <ios t r eam . h>
3
#include <c s t d l i b >
4
using namespace s td ;
5
int main ( void ){
6
int nba = 0 , nbe = 0 , nbi = 0 , nbo = 0 ,
7 nbu = 0 , nby = 0 ;
8
int voyl ;
9 cout << " tape z des v o y e l l e s , en minuscule \n" ;
10 cout << " c a r a c t è r e f i n de f i c h i e r pour a r r ê t e r \n" ;
11
while ( ( voyl = c in . ge t ( ) ) != EOF){
12
switch ( voyl ) {
13
case ' a ' : ++nba ;
14
break ;
15
case ' e ' : ++nbe ;
16
break ;
17
case ' i ' : ++nbi ;
18
break ;
19
case ' o ' : ++nbo ;
20
break ;
21
case 'u ' : ++nbu ;
22
break ;
23
case ' y ' : ++nby ;
24
break ;
Intro_C
18
25
default :
26 cout << " ce n ' e s t pas une v o y e l l e \n" ;
27 cout << " tapez une v o y e l l e s . v . p . \ n" ;
28
break ;
29 }
30 }
31 cout << "nombre de a : " << nba << " nombre de e : " << nbe << endl ;
32 cout << "nombre de i : " << nbi << " nombre de o : " << nbo << endl ;
33 cout << "nombre de u : " << nbu << " nombre de y : " << nby << endl ;
34 system( " pause " ) ;
35
return 0 ;
36 }
Les variables entières
nba , nbe ,. . .sont des compteurs de voyelles. Surprise, le programme lit
des voyelles entreposées dans
voyl , qui est un entier. Ceci est correct et souvent plus commode
que la déclaration rigoureuse
char voyl; . En effet, les caractères sont codés dans la machine sous
forme d'un entier et peuvent être considérées comme des entiers sans signe.
Je trouve ensuite une boucle
while qui lit une série de lettres et s'arrète quand je frappe
une «marque de fin de fichier ».
EOF est la désignation conventionnelle de cette marque, qui est
différente pour chaque système d'exploitation (Apple, Microsoft, Linux) mais la bonne définition
se trouve dans les entêtes
<stdio.h> ou <iostream> . Chez Microsoft, c'est <control>-z .
La logique qui commande la boucle est concentrée dans l'instruction bizarre de la ligne 10
(bizarre mais caractéristique du C++). La fonction
cin.get() lit un caractère au clavier. L'affectation
attribue à
voyl cette même valeur. Enfin, en C++, une affectation a elle-même une valeur,
justement celle que vient de recevoir l'identificateur de gauche (
voyl ici). On peut donc comparer
cette valeur à
EOF et continuer si elle est différente. Ouf. En français, on aurait dit « si le prochain
caractère lu n'est pas
EOF , alors. . .». Enfin, l'écriture bizarre cin.get de la fonction sera expliquée
vers la fin du cours.
On entre ensuite dans la structure
switch . La variable qui régit le fonctionnement de switch
est citée entre parenthèses. On énumère ensuite chaque cas à l'aide du mot clé
case , puis on
énonce les instructions correspondantes. Il est ici inutile de mettre des accolades pour constituer
un groupe solidaire : tout ce qui se trouve entre deux
case est exécuté.
L'instruction
break provoque un saut à la première ligne qui suit la structure switch. Si
break
ne figure pas, toutes les instructions de la structure sont exécutées.
Le dernier cas (
default ) est un fourre-tout qui récupère tout ce qui n'est pas une voyelle. On
voit que cette structure est semblable au CASE OF de Pascal, à part
break et default .
3.4 Opérateurs logiques
Jusqu'ici, je n'ai présenté que des prises de décision reposant sur des conditions simples. Mais
il est tout à fait possible de construire des expressions logiques composées ou complexes, à l'aide
d'opérateurs logiques, comme dans « si le coefficient de
x 2 est non nul ET si le déterminant est
positif, alors. . .» La traduction en C++ fait appel à l'un des opérateurs rassemblés dans le tableau
suivant.
Expression logique Expression en C++
négation !
et &&
ou
jj
Le premier opérateur n'attend qu'un seul argument (opérateur « unaire»), qui doit être vrai ou
faux ; il produit la valeur opposée. Les deux autres doivent recevoir deux arguments logiques (opérateurs
«binaires»), comme par exemple
Intro_C
19
if (note_moyenne >= 10 || note_examen >= 10)
cout << reçu\n";
Remarquez que la condition globale sera vraie si l'une ou l'autre ou les deux conditions partielles
sont vraies (ou non-exclusif).