You are on page 1of 254

LA PROGRAMMATION POUR...

2 
les élèves ingénieurs
2 
. . . ou les collégiens

2 
débutants

2 
. . . ou confirmés

Cours de l’École des Ponts ParisTech - 2014/2015
Renaud Keriven et Pascal Monasse
IMAGINE - École des Ponts ParisTech
monasse@imagine.enpc.fr
Version électronique et programmes :
http://imagine.enpc.fr/~monasse/Info/

"Ne traitez pas vos ordinateurs comme des êtres vivants...
Ils n’aiment pas ça !"

— "Cet ordinateur ne fait pas du tout ce que je veux !"
— "Exact... Il fait ce que tu lui demandes de faire !"

TABLE DES MATIÈRES

TABLE DES MATIÈRES

Table des matières
1

2

3

4

Préambule
1.1 Pourquoi savoir programmer ? .
1.2 Comment apprendre ? . . . . . .
1.2.1 Choix du langage . . . . .
1.2.2 Choix de l’environnement
1.2.3 Principes et conseils . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

7
10
10
10
11
11

Bonjour, Monde !
2.1 L’ordinateur . . . . . . . . . . . . . .
2.1.1 Le micro-processeur . . . . .
2.1.2 La mémoire . . . . . . . . . .
2.1.3 Autres Composants . . . . .
2.2 Système d’exploitation . . . . . . . .
2.3 La Compilation . . . . . . . . . . . .
2.4 L’environnement de programmation
2.4.1 Noms de fichiers . . . . . . .
2.4.2 Debuggeur . . . . . . . . . . .
2.4.3 TP . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

13
15
15
17
19
20
21
23
23
23
24

.
.
.
.
.
.
.
.
.
.
.
.
.
.

25
25
25
29
31
33
35
37
39
39
42
43
44
45
45

Les tableaux
4.1 Premiers tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.2 Initialisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.3 Spécificités des tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . .

49
49
51
52

.
.
.
.
.

Premiers programmes
3.1 Tout dans le main() ! . . . . . . . . . .
3.1.1 Variables . . . . . . . . . . . . .
3.1.2 Tests . . . . . . . . . . . . . . .
3.1.3 Boucles . . . . . . . . . . . . . .
3.1.4 Récréations . . . . . . . . . . .
3.2 Fonctions . . . . . . . . . . . . . . . . .
3.2.1 Retour . . . . . . . . . . . . . .
3.2.2 Paramètres . . . . . . . . . . . .
3.2.3 Passage par référence . . . . .
3.2.4 Portée, Déclaration, Définition
3.2.5 Variables locales et globales . .
3.2.6 Surcharge . . . . . . . . . . . .
3.3 TP . . . . . . . . . . . . . . . . . . . . .
3.4 Fiche de référence . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.

5 L’optimiseur . . . . . . . . . . . . . .1 Pourquoi ça marche ? . . . .1 Principe . . . . . . . . . .1. . . . . . . . . . . . . . . . . . . 4. . . .1 Multi-balles . . . . . . . . 5. . . . . . . . . . . . . . . . . . . . . . . . 7. .2 Tableaux de taille variable . . . . .3. . . . . . . . . . . . . . . . . 7. .1. . . . . .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fiche de référence . . . . . . . . . . . . . . . . . . . . . .2 Opérateurs . . . . . . . 7. . . . . . . . . 5. . . . . .3 Essai d’explication . . . . . . . . 5. . . . . . . . . . . . . . . . 6. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1.1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 . .1 Limites . . . . . . . . . . . . . . . . . . .3 Conseils . . . . . . . . 5. . . .3. . 5. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 85 85 87 89 90 90 90 91 91 93 93 93 95 95 96 . . . . . . . . . . . . . . . . .2 Pile des appels et débuggeur 7. . . . . . . . . . . . . . . .2 Avantages .2 Efficacité . . . . . . .4 Fiche de référence . . . . . . . . . 5. . . . . . . . . . . . 7.4. . . . . . . . . . . . . . . . . . . . . . . . . . . .7 Inclusions mutuelles . . . . . . . . . . . . . . . . . . . . . . . . .2. . . . . . . . .3 Utilisation dans un autre projet 6. . . . . . . . . . . . . . . . . . . . . . . . 6. . . . .1. . . . . . .2 Avec des chocs ! . . . . . .6 Implémentation . . . . . . . .1. . . . 7. . 4. . . . . . . . . . . . . . . . . . . . .4.3 Fonctions récursives . . . . . . . . . . . . . . . . . . . . . . . . . . 5.1 Révisions . . . 6. . . . . . . . . . . 6. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .5 A ne pas faire. . . . . . . .1. . . . . . . . . . . . . . . . . . . . . Récréations . . . . . . . . . . . . . . . . . . . . . . . . .2. 52 54 55 55 57 59 61 61 . . . . . . . . . . .2. . . . . . . . . . . . . . . 7. . . . . . . . . . . . 7. . . . . . . . 5. . . . . . . . . . .2 Les structures .3 Récréation : TP suite et fin . . . . . . .4. . . . . . . . . . . . . . . . . . . . . . . . . 7. . . . . . . . . . 7. . . . . . . . . . . . . 65 65 65 66 67 67 67 68 69 71 . . . . .1 Exemple . . . . .4. . . . . . . . . . . 7. . . . . Plusieurs fichiers ! 6. . . . . . . . . . . . . . . . . . . . .3 Mélanger les lettres . . . . . . . . . . . .2 Erreurs originales . . . . TP . . . . . . . . . .1. . . . . . . . . . . . . . . . . . . . . . . . . . La mémoire 7. . .4 Fiche de référence . . . . . . . . . . . . . .3. . . . . . . . . . . . . . .4 Fichiers d’en-têtes . . . . . . . . .2 La pile . . . . . .1 Paramètres . . . . .1. . . . . 7. 7. . . . . . . . . . . . .2 Variables Locales . . . . . . . . . . .1 Tableaux et fonctions 4. . . . . . . . . . . . . . .2 Affectation . . . .1 Définition . . . . . . . . . . . .1 Fichiers séparés . . . . . . . . .3 Récréation : TP . . . . . .2 Utilisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1. . . . . . . . . . . Les structures 5. . . . . . . . . . . 6. . . 6. . . .4. . . . . .6 5 6 7 TABLE DES MATIÈRES 4. . . .5 4. . . . .1. . . . . . . . . . . .2. . .TABLE DES MATIÈRES 4. . .4 Le tas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3. . . . . . . . . .6 Assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .4. . . . . . . . . . . . . .4 4. 7. . . . . . . . . . . . . . . .1 L’appel d’une fonction . . . . . . . .. . . . . . . . . . . 6. . . . . . . . . . . .1 Erreurs classiques . . . . . . . . . . . . . . . . . . . . . 4. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 74 74 75 76 76 79 79 80 80 82 82 . . . . .1. . . . . . . 6. . . . . . . . . . . . 6. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . 10. . . . . . . . . . 10 Constructeurs et Destructeurs 10. 10. . . . . . . . . . . . . . . . . . . . . . . .3 Accesseurs . . . . . . . . .1. 8. .7. . . . . 9. . . . . . . . . . . 10. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .9 8 9 TABLE DES MATIÈRES TP . . . . . . . . . . . . . .3. . . . . . . . . . . . . . . . . . . . . 8. . . . . . . . .5 TP . . . . . . . . . . . . . . . . . . . . . . . . . . 96 97 99 Allocation dynamique 8. . 10. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .7 7. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8. . . . . . 10. . . .9 Fiche de référence . . . . . . . . . . . . . .7. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .6. . .7 Protection . . . . . . . . . . .5 TP . . . . . . . . . . . . . . . .1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3 Tableaux d’objets .1 Constructeur vide . 8. . . . . . . . . . . . . .1 Principe . . . . . . . . . . . . . . .6. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9. . . . . . . . . . . . . . . . . . . . . . 10. . . .7 Destructeur . . . . . . . . . . .6 Fiche de référence . . . . . . . . . . . . 9. . . . . . . . . . . . . . . . . . . . . 9. . . . . . . . . . . . . . .4 Exemple des matrices . .4 Objets temporaires . . . . . . . . . . . . . . . . . . .6 Références Constantes . . . .2 Limitations . . . . 9. . . .TABLE DES MATIÈRES 7. . . 9. . . . . . . . . . . . . . . . . . . . .2. . . . . . . . . . . . . . 9. . . . . . . . . . .2 Méthodes constantes . . 9. . . . . . . . . . Fiche de référence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1 Principe . . . . . . 101 101 101 102 103 104 104 105 106 107 110 111 111 Premiers objets 9. . . . . . .7. . . . . . . . . . .4 Boucles et continue . . . . . . . . . . . . . . . . . . . . . . .1 Pourquoi ça marche ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9. . .1 Principe . . . . 10. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115 115 116 118 118 120 122 123 123 125 125 126 126 . . . . . . . . . . . . . . . . . . . . . .11Objets avec allocation dynamique . . . . . . . . . . . . . 10. . . . . . . . . . . . . . . . . . . . . .8 7. . . . . . . . . .8 Destructeurs et tableaux . . . 8. . . . . . .1 Le problème . . . . . . .2 Allocation dynamique . . . . . . . . .2. . . . . . . . . . . . . . .9 Constructeur de copie . . . . . . . . . . . . 8. . . . . . . .3 Visibilité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1 Tableaux bidimensionnels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10. . . . . . . . . . . . 8. . . . . 8. . . 10. . . . . . . . . . . . . .3 Solution . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3 Structures et allocation dynamique 8. . . . . . . . . . . 3 . . . .2 La solution . . .6 Interface . . . . . . . . . . . . . .10Affectation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3 Conséquences . . . . . . . . . . . . . .2 Exemple simple . . . . . . . . .3 Cas général . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131 131 132 132 132 134 135 136 137 137 137 138 140 142 142 143 144 . . . . . . . . . 10. . . . . . . . . .2. . . . .3. . . . . . . . . . .5 Cas des opérateurs . . . . . . . . . . . . . . . . . . . . . . . . . . . .2 Plusieurs constructeurs . . Examens sur machine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10. .1. . .1 Philosophie . 8. . . . . . . 9. .8 TP . . . . . . . . . . . .2 Erreurs classiques . . . . . . . . . . . . 10. . . . . . . . . . .2 Structures vs Classes 9. . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.3. . . . 8. . . . . . . . . . . . . . . . 10. . . . . .

. . . . . . .. . . 189 189 190 190 191 191 191 192 192 . . . . . . . . . .1 Opérateur binaires . . . . . .1 Principe . . . . . . . . . . . .2 Utilité . . . . . . .3 Le vecteur : un tableau encapsulé dans une classe . . . . . . . . . . . . . . . . .12Fiche de référence . . . . . 13. .3. . . . . . . .2 La complexité . . . . . . .. . . . . . . . . . 4 . . . . . . . . . . . . . . . . . . . . . . .5 "inline" . . . . . . . . . . . . . . 145 145 146 150 153 . 13. . . . . . . . . . . . . . . . . . . . .7 Fiche de référence . . . . . . . . . . . . . . . . . .4. . . . . . . .3 Erreurs fréquentes . . . . . . . . . .TABLE DES MATIÈRES TABLE DES MATIÈRES 10. . . .1 Rappels sur les tableaux . . . . . . . 11. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3 Valeurs par défaut . . . . .6. . . . . . .1 Chaînes de caratères . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 En vrac (suite) . . . . . . . . . . . . . . . 13.2. . . . . 11. . . . . . . . . 11. . . . . . . . . . . . . . . . . . . . . . . . . . . . .1 Construction et destruction 10. . . . .6. . . . . . . . . . . 11. . . . . . . . . . . . . . . . . . . . . .11.4. . . . . . . . . . . . . .3 operator() . . . .2 Comment la mesure-t-on ? .1 Principe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12. . . . . . . . . . . 12. . . . 11 En vrac. 11. . .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2 template et fichiers 12. . 12. . . . . . .4. . . . . . . . . . . . . . . . . . . . .3 Boucles et break . . . . . . . . . . . . .4 Accesseurs . . . . . . . . . . .11. . . . . . . . . . . . .2. .4 Surcharge et méthode constante 11.4 P et N P . . . . . . . . . .2 Problèmes ! . .2. . . . . . . . .1 Référence comme type de retour 11. . . . . . . . . . . . . 171 171 172 173 174 175 175 175 177 177 180 182 187 13 Structure de données 13. . . . . . . . . . . .5 const et tableaux . . . . . . . . . . . . . . . . . . . . . . . . .5 Pourquoi est-ce important ? . . . . . . . . . . . . . . . . . . . . . . . . . .2. .4. 11. . . . . . . . . .2. . . . . . . . . . . . . .7 Fiche de référence . . . . . . . . . 10. . . 11. . . . . . .1 Principe . . . . . . . . . . . . . . .6 template . . . . . . . . . . . . . . . . .2. . 12. . . . . . . . 13. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3 La notation O . . 11. . 12. . . . . . . . .2. . . . . . . . . . . . . 155 155 157 157 158 158 159 159 160 160 160 161 161 162 162 163 165 165 167 . . . . . . . . 12.6 Types énumérés . . 12. . . . . . . . . . . . . . . . . . . 11. 11. . . . .4. . . . . . . . . . . . . . . . . . . . . . . . . . . 11. . . . . . . . . . . . . . . .4 STL . . . . . . . .13Devoir écrit . . . . . . . . . . . . . . 13. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2. . . . . . . . . . . . .6. . . . . . 10. . .3. . . . . . . . . . . . . . . .5 Assertions . . . . . . . .2 Chaînes et fichiers . . . . . . . . . . . . . 12.3 Solution ! . . . . . . . . . . . 11. . . .4 Variables statiques . . . . . .11. 11. 11. . .3 Objets et fichiers . . . . . . . 12. . . . . . . . . . . . . 13. . . . 12. . . . . . . . . . . . . . . .3. . . . . . . . . . . . . . . . . . . . . . 13. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .6. . . . . . . . . . . . 10. . . . . . . . . . . . . . . . .3 Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11. . . . . . . . . . . . . . . . . . . . . . . . . . . .2 Valeur conditionnelle . . . . . . . . .2 Utilisation . . . . . . . . . . . . . . . . . 11. . .1 Mais qu’est-ce donc que la complexité ? . . . . . . . . .2 Fichiers . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . .8 Devoir final . . . . . . . . . . . . . . . . . . . . .

. . . . . .3. . . . . . . .3 Stockage de la file de priorité .3 Théorie physique . . . . . . . . . . . . . .9 13. . . .4 Gros tableaux . . . .2. . . . . . . . . . . 13. .3 QuickSort . . . . . . . . . . . . . . . . . . . . . .2 Premier programme graphique avec Imagine++ A. . . . . . fonctions . . . . . . . .1 Mélanger un tableau . . . . . Autres structures .4 File de priorité et HeapSort . . . . . . . . .3 Balle à part . . . . . . .5. . . . . . . . . . . . . .1 L’environnement de programmation . . 192 193 193 193 194 196 197 197 198 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1 Etapes . .6. .2. .3. . . . . . . . .3 Debugger . . Monde ! . . . . . . . . . . . . . . . . . . . . A. . . .1. . . . A. . . . . . . . . . . . . . . .5. . . . . . . . . .3. . . . . 14. . . . .1 Bonjour. . . . . . . . . . . . . . . . . . . . .2 Algorithmes quadratiques . . . . . . . . . . . A.5 Fichiers séparés . . . . . . . . . . . . . . . .4 Retour à la physique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2 Mastermind Graphique . . . . . . . . . . . . .1 Complexité minimale . . . . . . . . . . . . . . . . . . A. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14. . . . . . . .1. . . . . . . . . . . . . . . . . . . 14. . . . . . . . . . . . . . . . . . A. .2 Premières erreurs . . . . . . . . . . . . La pile. . . . . A. . . A. . 13. . . . . A. . .1 Fonctions outils . . . . . . . . . . . . . . . . . . . . 14. . . . . . . . .4 S’il reste du temps . . . . . . . . . . . . . . . . . . . . . . . .1 Premier programme avec fonctions . . . . .3. . . . . . .4. . . . .5. . . . . 199 199 199 200 202 202 203 203 204 204 . . . . 14 Algorithmes de tri 14. . . . . . . . .1. . . . . . . . . . . . . . . . . . .2 Retrait de la file de priorité (pop) . . . . . . . . . . . .3 Quicksort .4. . .6 13. . . . . . . . . . . . . . . .5 Installer Imagine++ chez soi . . . . . . . . . .6. . . . . . . . . . . . . . . . . . . . . . . . . . A. . . . . . .1. . . . .4. . . . . . . . . . . . . . . . .5 13. . .5. . . . . . . . . . . . . . . .7 13. . . . . .5 Conclusion . . . . . . . . . . . . . .1 Mastermind Texte . 205 205 205 207 208 209 209 210 210 210 212 214 214 216 218 218 220 221 223 223 223 224 224 227 227 228 229 230 . .4. . . .1 Usage . . . . . . . . . A. . . . . . . . . . . . . . . . . . . . . . . . boucles. . . .3 Gestion mémoire . . . . . . . . . .6 Les tris . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2 Tris quadratiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .4 13. . . . . . . . . . . . . .6. . . . . . . . . A Travaux Pratiques A. . . . . . . . . 14. . . A. . A. . . . . . .6. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . TABLE DES MATIÈRES . . . . . . . . . . . . A. . . . . . . . . . . . . . . . . . . . . . . . . .2 Variables. . .TABLE DES MATIÈRES 13. . . . . .3 Tableaux . .4 Structures . . . . . Les itérateurs . . . . . . . . . . 14. . . . . . . 5 . . . . .3 Jeu de Tennis . . . . . . .3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A. .4. . . . . . . . . . . . . . . . . . . . . . . A. . . . Last In First Out (LIFO) La file. . . . . . . . . . . . . . . . . . . Récapitulatif des complexités . . . . A. conditions. . . .2 Vecteurs . . . . . A. . .8 13.1 Insertion dans la file de priorité (push) 14. . . A. . . . . . . . . . . . . . .4 HeapSort . . . . . .4.1. . . . . . . . . . . . . . . . . . . A. . . . . . . . . . . . . . . . . First In First Out (FIFO) La liste chaînée . . . A. . . . . . . . . . . . . . . . . . . . . .2 Complexité . . . . . . . . . . . . . . . .4. . .2 Aide . . . . . . A. . . A. . . . . . . . . . . . . . A.2. . . . A. . . . . . . . . 14. . . . . . . . . . . . . . . . . . . A. . . . . . . .

. . . . . . . . . . . A. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .8. . . .3 Tableaux dynamiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2 Tableaux statiques . . . . . . . . . . A. . . . .8. . . . C Fiche de référence finale . . . A. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .9. A. . . . .9. . . . . . . . . . . . .8 Premiers objets et dessins de fractales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .7. . . . . .3 Images . . . . . . .1 Serpent . . . . . . . . . . . . . A. . . . . . . . . . . . . . . . . . . B. . . . . . .7 Images . . . . . . . . . . . . . . . . . . . . . .4 LinAlg . . 231 231 231 232 232 232 233 233 234 234 235 235 236 237 237 238 238 . . . . . . . . . . . . . . . . . . . . . A. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A. . . . . . . . 241 241 242 243 243 245 6 . . . .7. . . . A. .7. . . . .4 Le flocon de neige . . . . . . . . . . .2 Graphics B. . . . . . . .8. . .7. .7. . . . . . . . . . . .7. . . . .9 Tron . . . . . . . . . . . . . . . . . . . . . . . . A. . . A. . . . . . . .TABLE DES MATIÈRES TABLE DES MATIÈRES A. B Imagine++ B. . . . . . . . . . . .6 Structure . . . . . . . . . . .7 Suite et fin .1 Le triangle de Sierpinski . . . . . . . . . . . . . . .3 Changer d’implémentation . . . . . . . . . . . . . . . . . . . . . .9. . . . . .3 Graphismes . .5 Fonctions . . . A. . . . . .1 Allocation . . . . . . . . . . . . .4 Charger un fichier . . . . . . . . . . . A. . . . . . . A. . . . . . .2 Une classe plutôt qu’une structure A. . . . . A. . . . . . . . . . .8. . . A. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1 Common B. . . .2 Tron . . . . . . .7. . . . . . . . . . . .

. Permettez ce terme ouvertement Lucasien.) — — Le Maître Programmeur 1 : "Rassure-toi ! Les ordinateurs sont stupides ! Programmer est donc facile... Et pourtant. Leur manque d’intelligence fait justement qu’il m’est pénible d’en faire ce que je veux. en essayant évidemment de ne pas oublier de les expliquer au passage. Et je n’ai pas envie de devenir comme ça. Pourquoi doit-on être aussi. argot que doit bien évidemment accepter et faire sien l’Apprenti sous peine de ne rien comprendre au discours de ses collègues d’une part. 3. donc. Le jeune Padawan.. les ordinateurs ne sont certes que des machines et les dominer devrait être à ma portée.. Préambule Chapitre 1 Préambule Note : Ce premier chapitre maladroit correspond à l’état d’esprit dans lequel ce cours a débuté en 2003... précis ?" Programmer rend maniaque ! D’ailleurs. 1. Mais demandez-donc à votre voisin s’il reçoit beaucoup de pourriels (terme proposé pour traduire "Spams") ! . envoyer un courriel ou avoir un bogue commencent peut-être à devenir des expressions compréhensibles. Il semble plus approprié que l’habituel Gourou souvent utilisé pour décrire l’expert informaticien...." — L’Apprenti Programmeur 2 : "Maître. Programmer exige de la précision et la moindre erreur est sanctionnée par un message incompréhensible. Nous parlons bien ici d’un savoir-faire à transmettre de Maître à Apprenti et non d’une secte. l’analyse et les conseils qui suivent restent d’actualité.. Si l’on ignore la naïveté de cette première rédaction (et le fait que Star Wars n’est plus autant à la mode !). dans une période où l’Informatique avait mauvaise presse à l’École des Ponts.1.. — (Ce premier chapitre tente surtout de motiver les élèves ingénieurs dans leur apprentissage de la programmation. Nous le maintenons ici en tant que témoin de ce qu’il faillait faire alors pour amener les élèves à ne pas négliger l’Informatique. pour ceux qui connaissent. Les enfants qui se trouveraient ici pour apprendre à programmer sont sûrement déjà motivés et peuvent sauter au chapitre suivant ! Profitons-en pour tenir des propos qui ne les concernent pas. les informaticiens sont tous maniaques. et d’employer des adaptations françaises ridicules ou peu usitées d’autre part.. ils constituent en réalité un argot propre au métier d’informaticien. Anglicismes souvent incompréhensibles. un bug 3 ou même un crash de la machine. Je n’aurai aucun remord dans ce polycopié à utiliser les termes habituels des informaticiens.. 2. Naviguer sur la toile.

programmer : — est un jeu d’enfant." Quel vieux fou ! Pour un peu. Utiliser un ordinateur pour programmer a tout aussi mauvaise presse que de jouer aux jeux vidéo.1. programmer : — est un travail de "technicien 6 " qu’il n’aura jamais à faire lui-même.. 5. Réjouis-toi. avec tout le sens péjoratif que ce terme peut avoir pour lui. : "J’essaierai. 8 . etc. : "Les vrais problèmes qui se poseront à toi. tu pourras créer ces êtres obéissants que sont les programmes. Maître.. Il montre pourtant clairement la situation. — est une activité créatrice et épanouissante. bref. La vérité. je ne suis pas sûr d’en avoir envie. n’est pas digne de lui. Résumons : — Pour celui qui sait. : "Bien. : "La précision est indispensable pour communiquer avec une machine. Programmer est pourtant souvent un travail d’équipe. — Pour celui qui apprend. — est indispensable. il se prendrait pour Dieu. c’est qu’il parle aux machines parce qu’il ne sait pas parler aux hommes.P. Je n’y arriverai pas. Dans le cas où l’élève est ingénieur..P. Tu dois faire un effort.P. "Maître. Ne le prenez pas mal. C’est à l’Homme de s’adapter. programmer : — est difficile. Il ne lui manque plus que des grosses lunettes et les cheveux gras 4 . — n’est pas aussi noble que les Mathématiques. Voilà la vérité ! — ." Je me demande s’il a vraiment raison ! Je suis sûr qu’il doit être nul en Maths. Savoir programmer. Toute ressemblance avec des personnages réels ou imaginaires.. 6." — A. Préambule — M. — Pour l’élève.. apprendre à programmer : — devrait être simple et rapide pour un élève ingénieur. à quoi savoir programmer me servira-t-il ?" — M. tu devras !" — A. Il comble avec ses ordinateurs son manque de contact humain. — ne sert à rien.. mais je crois être davantage doué pour les Mathématiques ! Et puis. En contre-partie tu deviendras son maître. nous pouvons compléter le tableau : — Pour le professeur. tu ne pourras toujours les résoudre par les Mathématiques. Bientôt. — Oublions là ce dialogue aussi caricatural que maladroit. les torts sont partagés : — Le professeur : 4.. — est plus utile qu’apprendre davantage de Mathématiques.. — est une activité ingrate qui favorise le renfermement 5 sur soi-même. En fait.P. L’informaticien type.

De tels programmes leur seront peutêtre utiles plus tard. mais qu’en contrepartie il sera mauvais programmeur. etc. Comme annoncé par le titre de ce cours. etc ? 9. il nécessite une tournure d’esprit complètement différente et beaucoup de travail personnel devant la machine. C’est une erreur fréquente de croire qu’il intéressera ses élèves en leur faisant faire des programmes centrés sur les mathématiques ou le calcul scientifique. et qu’il leur faudra du temps pour apprendre ne serait-ce que les bases de la programmation. Préambule — ne réalise pas que ses élèves ont un niveau avancé en maths parce qu’ils en font depuis plus de dix ans. Programmer pour combler le manque de possibilités de l’ordinateur. — s’arrange un peu trop facilement d’un mépris de bon ton pour la programmation. internet. Une récompense à qui me trouve un substitut satisfaisant à cette expression consacrée. Aujourd’hui. 11. si programmer est effectivement simple en regard de ce que ses élèves savent faire en maths.1. motivation et goût de l’effort seront au rendez-vous.. — L’élève : — ne se rend pas compte que savoir programmer lui sera utile. mais certainement pas à programmer. cela demandera de la motivation et un peu d’effort : essentiellement de mettre ses maths de côté et de retrouver le goût des choses basiques. les logiciels graphiques ou musicaux. Expliquons-nous : le passionné d’informatique a aujourd’hui tellement de choses à faire avec son ordinateur qu’il sera en général incollable sur les jeux. De plus. On l’aura compris. Il lui restera malgré tout à acquérir quelques bases d’arithmétique et de géométrie. il en est de même pour le débutant et le "geek 10 ". Et puis. — oublie qu’il a le plus souvent appris seul quand il était plus jeune.. un master-mind ou un labyrinthe 3D est tout aussi formateur et plus motivant qu’inverser une matrice creuse. Savoir programmer ne sert pas seulement à faire du C++ ou du Java. Il s’agit pourtant d’une base qui se retrouve dans tous les langages et même dans la plupart des logiciels modernes 9 . en programmant des choses simples et ludiques 7 . il se verra vraisemblablement confier à son premier poste la réalisation de quelques petits programmes en plus de ses attributions normales.. Il devrait donc faire venir ses élèves à la programmation par le côté ludique. et de la pratique. il est à la fois facile et difficile d’apprendre à programmer. Pour un collégien. Il lui est plus aisé d’apprendre une n-ième branche des mathématiques que de faire l’effort d’acquérir par la pratique une nouvelle tournure d’esprit. et c’est un phénomène relativement nouveau. ni même du Scilab. car. l’achat du dernier gadget USB à la mode. Du temps. faire le tour de toutes les possibilités d’un ordinateur est une occupation à plein temps ! Ainsi. Pour l’ingénieur. mais tellement vraie : quel cours de programmation ne rabâche pas les célèbres "factorielle".. du Matlab ou du Maple : une utilisation avancée d’Excel ou du Word demande parfois de la programmation ! 10. collégien et ingénieur en sont au même point pour l’apprentissage de la programmation. le "fana info" passe-t-il sa journée à se tenir au courant des nouveaux logiciels 11 et en oublie qu’il pourrait lui aussi 7. La liste est longue. L’algèbre linéaire ou l’analyse numérique sont des domaines passionnants à étudier. 8. considéré comme "le jeune" donc le moins "allergique" aux ordinateurs. il y avait peu à faire avec son ordinateur sinon programmer. "Quick Sort". mais ne sont pas forcément motivants. "suites de Fibonacci". et non avec les mêmes sempiternels exemples 8 . Il y a quelques années. Il faut admettre sans complexe que programmer un flipper. Sans même d’ailleurs avoir le temps d’en creuser convenablement un seul ! 9 . l’installation ou la configuration de son système.

1 Comment apprendre ? Choix du langage Il faut d’abord choisir un langage de programmation. 1. Pourquoi savoir programmer ? 1. conduit à des programmes inconsidérément gourmands en temps ou mémoire. dans un soucis de simplification du C++ 12 . Bien que Java aie été conçu. 1. il nous semble évident qu’il ne s’agit pas du bon choix pour l’apprentissage de la programmation. 2. dans les milieux où peu de gens programment. Apprendre un langage précis n’est pas du temps perdu car les mêmes concepts se retrouvent dans la plupart des langages. le choix actuel se fait souvent entre C++ et Java. débutants ou passionnés. De plus. ce qui est possible techniquement et ce qui ne l’est pas. Nous ne réduisons évidemment pas Java à un sous ensemble de C++. Il lui est supérieur sur certains aspects mais il est d’expressivité plus réduite. tous les élèves sont à égalité. Il est fréquent qu’un stage ou qu’une embauche en premier poste comporte un peu de programmation. 14. C++ est plus complexe dans son ensemble mais n’en connaître que les bases est déjà bien suffisant. En conclusion. ce qui est important pour le spécialiste mais aussi pour le débutant.2 1. C++ permet également une programmation proche de la machine. 2. Résumons et complétons : 1. Ne pas comprendre ce que la machine doit faire pour exécuter un programme. suffisant en pratique. déroutant pour le débutant. Même à un poste non technique. Matlab. Préambule en créer. même. Sans argumenter sur les défauts respectifs des langages qui en font partie. 10 . C’est la base. Un ingénieur pourrait évidemment être tenté d’apprendre à programmer en Maple. Savoir programmer. adaptée au débutant 13 . 3. C’est donc sans complexe que l’ingénieur pourra apprendre à programmer en même temps que le fils de la voisine. par exemple en finance.1. 3. collégiens ou ingénieurs.1. Plus complet. mais pas à proprement parler de langages généralistes complets. En pratique.2. certes.1 Pourquoi savoir programmer ? Nous venons partiellement de le dire. C++ permet une programmation de haut niveau mais aussi une programmation simple. Nous ne verrons donc dans ce cours qu’un sous ensemble du C++. nous préférerons C++ pour des raisons pédagogiques : 1. c’est important pour prendre les bonnes décisions. se programment. C++ est souvent incontournable dans certains milieux. Scilab ou autre. les logiciels courants eux-mêmes peuvent se programmer. 12. entre autres. Il faut qu’il comprenne qu’il s’agit là d’outils spécialisés pour mathématicien ou ingénieur qui lui seront utiles et qui. car seule une bonne compréhension de la machine aboutit à une programmation convenable et efficace 14 . 13. et peut-être surtout. c’est mieux connaître le matériel et les logiciels. Java force un cadre de programmation objet.

souvent farouchement opposés. tout cela avec un seul outil ergonomique. certains aspects pratiques et pourtant simples de C++ ont disparu dans Java 15 . nous n’en prônons aucun en particulier 16 . Conscients des avantages et des inconvénients de chacun des deux systèmes. Mac). 11 . Les opérateurs par exemple.. à tel point que certains n’admettent pas qu’il est possible d’être partisan des deux systèmes à la fois. 1. 17. ce serait la règle des trois "P" : 1. Différents environnements de développement peuvent être choisis.2.3 Principes et conseils Au niveau auquel nous prétendons l’enseigner.). Windows.2.1. S’il n’y avait qu’un seul conseil à donner. 16. Programmer 3.2 Choix de l’environnement Windows et Linux ont leurs partisans.. pour des raisons pédagogiques. les transformer en programme. Enfin. 1. Préambule 1. — Editer ses fichiers. mais qu’il comprend en plus le format CMake (dont nous reparlerons dans les TP). Certains fonctionnent sous toutes les plates-formes (Linux. Encore une fois. Les concepts utilisés sont rudimentaires mais c’est leur mise en oeuvre qui est délicate. Comment apprendre ? 4. c’est : — Toutes les étapes de la programmation regroupées en un seul outil de façon cohérente. Programmer 15. ni connaissances encyclopédiques. Un environnement de programmation. debuggeur. passer en revue ses erreurs. détecter les bugs. répétons que le choix du langage n’est pas le plus important et que l’essentiel est d’apprendre à programmer. L’avantage de ce dernier est qu’il utilise par défaut les mêmes raccourcis clavier que Visual Studio. diront les mauvaises langues. parcourir la documentation. mais c’est encore plus crucial pour le débutant. etc. Sous Linux. L’inconvénient est qu’ils n’ont pas par défaut les mêmes raccourcis clavier que Visual. mentionnons en particulier QtCreator. Comme pour le choix du langage. C’est vrai pour le programmeur confirmé. qui existe en version gratuite. Programmer 2. c’est à dire un logiciel unique permettant de programmer. L’environnement de référence sous Windows est celui de Microsoft. Express. Ceci dit. Son interface est fortement inspirée de KDevelop et XCode. compilateur.2. Le seul utilisable. la programmation ne requiert ni grande théorie. etc. le choix de l’environnement n’est pas limitant et en connaître un permet de s’adapter facilement à n’importe quel autre. C’est probablement l’un des meilleurs logiciels issus de la firme de Redmond 17 . Visual Studio. conseillons KDevelop et sous Mac XCode. est préférable à l’utilisation de multiples logiciels (éditeur. qui trouve en général dans cet environnement des outils puissants. Le reste de ce poly est orienté vers Visual Studio. L’idéal est en fait d’avoir les deux "sous la main". nous pensons qu’un environnement de programmation intégré.

5. en testant au fur et à mesure. Bricoler. tester. Que programmer soit expérimental ne signifie pas pour autant qu’il faille faire n’importe quoi jusqu’à ce que ça marche plus ou moins. Rester (le) maître 18 (de la machine et de son programme). Préambule La pratique est effectivement essentielle. puisqu’il a plus de temps. sans laisser passer la moindre erreur ou imprécision. Même si le programmeur aguerri trouvera la bonne solution du premier jet. — Gardons bien présents ces quelques principes car il est maintenant temps de. Mais c’est tellement facile dans le cas de la programmation. faire. Ajoutons quand même quelques conseils de base : 1. Il s’agit pourtant d’un outil essentiel pour comprendre ce qui se passe dans un programme. Il faut avancer progressivement. qu’il serait dommage de passer à côté ! Au pire. Souvent.. fouiller.1. si programmer n’est pas toujours une partie de plaisir pour tout le monde.2. Là encore. de commandes ou d’instructions. On voit bien qui est le chef ! 12 . C’est ce qui fait qu’un enfant a plus de facilités. il est important pour le débutant d’apprendre à connaître le langage et l’outil de programmation en jouant avec eux. un bon environnement de programmation facilite la tâche. L’ordinateur est un outil expérimental. Il faut donc le considérer comme essentiel et faisant partie intégrante de la conception d’un programme. S’amuser. 3. Provoquer les erreurs pendant la phase d’apprentissage pour mieux les connaître est le meilleur moyen de comprendre beaucoup de choses et aussi de repérer ces erreurs quand elles ne seront plus volontaires. passer à notre premier programme ! 18. la connaissance du debuggeur (l’outil pour rechercher les bugs) est négligée et son apprentissage est repoussé au stade avancé. Comment apprendre ? 1. Le vocabulaire n’est pas choisi au hasard : un programme est une suite d’ordres. 4.. Mais sa programmation est elle aussi une activité expérimentale à la base. etc. défaire. méthodiquement. Ce que nous voulons dire par là. il vaut mieux que le programme obtenu dans la douleur soit intéressant pour celui qui l’a fait ! 2. même dépourvu de bugs. casser. C’est une évidence en matière de pédagogie. Faire volontairement des erreurs. c’est qu’il ne faut pas hésiter à tâtonner. Debugger.

celle qui est appelée automatiquement 4 quand le 1. je vais tenter d’expliquer au passage les mathématiques qui pourraient leur poser problème. à qui main() retournerait-elle un entier ?) . Monde ! Chapitre 2 Bonjour. console. fenêtre de commande. Souvenons nous avec un minimum de respect que c’était déjà un progrès par rapport à la génération précédente.. allons-y ! Décortiquons-le ! Dans ce programme. On dit aussi retourne. dépourvue d’écran et qui utilisait une imprimante pour communiquer avec l’homme. return 0.2. World!". i n t main ( ) { cout << " Hello . je suis pris à mon propre piège ! Alors. signifie aujourd’hui que l’affichage se fera dans une fenêtre simulant l’écran d’un ordinateur de cette époque. Enfin. Bonjour. (Du moins en apparence car en réalité le programme a plein de choses à faire avant d’arriver dans main() et il commence par plusieurs autres fonctions que le programmeur n’a pas à connaître et qui finissent par appeler main(). Il n’y a (mal ?)heureusement rien de magique dans la programmation. Cette fenêtre est appelée terminal. Cette fonction est spéciale car c’est la fonction principale d’un programme C++. xterm. suivant les cas. Cette expression. 3. maintenant vous savez qui appelle main(). c’est avec un peu d’optimisme que j’ai prétendu qu’ils pouvaient le faire en lisant un polycopié destiné à des ingénieurs. ce qui était relativement peu interactif ! 2. fenêtre DOS. World ! " << endl . vestige de l’époque où les ordinateurs étaient dotés d’un écran capable de n’afficher que des caractères et non des graphiques (courbes. à tout hasard. Par contre. A qui renvoie-t-elle cet entier ? Mais à celui qui l’a appelée. voyons ! 4. ils sont bien courageux ! Lorsque je disais tout à l’heure qu’ils pouvaient facilement apprendre à programmer. La ligne 4 int main() définit une fonction appelée main(). je le pensais vraiment. Voilà. Dans un programme. etc. Mais main() n’est appelée par personne puisque c’est la première de toutes.. les fonctions s’appellent les unes les autres. Monde ! (Si certains collégiens sont arrivés ici. qui renvoie 3 un nombre entier. Entendons par là des instructions que nous n’expliquons pas pour l’instant. dessins. les lignes 1 et 2 sont des instructions magiques 2 qui servent à pouvoir utiliser dans la suite cout et endl. qui affiche à l’écran 1 le texte "Hello.) — Si l’on en croit de nombreux manuels de programmation. si personne ne l’appelait. un premier programme doit toujours ressembler à ça : # i n c l u d e <iostream > using namespace s t d . etc. } Eh bien. D’ailleurs.).

14 . Donc. Nous ne sommes pas réellement maîtres de la machine. 9. on ne programme pas convenablement sans comprendre ce que l’ordinateur aura exactement besoin de faire pour exécuter ce programme. En fait. Notons au passage que toutes les instructions se terminent par un point-virgule . dans l’abstrait. Bonjour. Je savais bien que vouloir expliquer tous les barbarismes propres aux informaticiens m’interromprait souvent. dans l’approximatif. dans le magique. On ne donne pas efficacement d’ordre à quelqu’un sans comprendre comment il fonctionne ni ce que les ordres donnés entraînent comme travail. grâce à la variable 6 cout qui correspond à la sortie console 7 . Enfin. World!"<< endl. Commencer par expliquer ce programme. World!". Enfin. la fonction main() se termine ligne 7 par return 0. est un retour à la ligne 9 . c’est même très dommageable pour la suite. Ce qui signifie que la suite de l’affichage sur la console se fera sur une nouvelle ligne. à la ligne 6. cout << "Hello. endl. c’est être encore dans le vide. Ouf ! Que de termes en italique. puisque certaines sont constantes ! 7. Donc. Mais bon.. Les données sont rangées ou stockées dans des variables qui mémorisent des valeurs. La deuxième.2. Que de concepts à essayer d’expliquer ! Et pour un programme aussi simple ! Mais là n’est pas le problème. La première de ces données est la chaîne de caractères 8 "Hello. 6. Délimitée par les accolades ({ ligne 5 et } ligne 8). Monde ! programme est lancé 5 . Taper des instructions et voir ce qui se passe sans comprendre ce qui se passe n’est pas raisonnable. des données séparées par des <<. un texte. il se termine ou meurt. seule ligne "intéressante". De même... Après quoi. Ces variables ne sont d’ailleurs pas toujours variables au sens usuel. il s’exécute ou tourne. un programme démarre ou est lancé. C’est toute cette approche qui est négligée quand on commence comme nous venons de le faire. qui lui ordonne de retourner l’entier 0. affiche. En clair. Qu’est-ce que je disais ! On affiche dans une fenêtre console ! 8. Stop ! Stop ! Stop ! Faux départ ! On reprend le : 5.

Le C++ est un langage très complet. Aujourd’hui l’horloge va environ à 3GHz. 2. (Mais attention : une instruction demande plus d’un cycle d’horloge !) 14. — Les instructions sont typiquement : — Lire ou écrire un nombre dans un registre ou en mémoire. — Il dialogue avec le monde extérieur via de la mémoire 14 en plus grande quantité que ses registres. sous forme de nombres.1. peut-être même trop.2. typiquement 1Go (giga-octets). quoi ! 11. transformer des nombres en autres nombres. Il ne sait que calculer 10 . Sans être exhaustif. — Effectuer des calculs simples : addition. Les premiers langages étaient plus éloignés de l’Homme car plus proches de la machine qui était alors rudimentaire. les instructions à exécuter et les données sur lesquelles travailler. un programme en C++ se veut le plus proche possible de l’Homme. multiplication. et l’on peut envisager que les futurs langages seront plus proches de l’Homme. La largeur de son spectre est une des raisons de son succès. un micro-processeur ne sait faire que des choses relativement basiques. Monde! 2. Aujourd’hui.1 Le micro-processeur Quel qu’il soit 12 et quelle que soit sa vitesse 13 . — Il possède un petit nombre de mémoires internes appelées registres. En fait. Bien que peu compréhensible pour le débutant. retenons juste ceci : — Il sait exécuter une suite ordonnée d’instructions. — Tester ou comparer des valeurs et décider éventuellement de sauter à une autre partie de la suite d’instructions. Bonjour.1 L’ordinateur Pour savoir ce qu’un ordinateur sait vraiment faire. 12. un ordinateur ne sait pas faire de C++. Un computer. etc. C’est aussi ce qui fait que son apprentissage complet demande un long travail et nous ne verrons ici qu’un partie restreinte du C++ ! 2. 10. Pentium ou autre 13. 15 . Plus exactement la fréquence à laquelle il exécute ses instructions. — Cette mémoire contient. Il peut être relativement proche de la machine si nécessaire et au contraire de "haut niveau" quand il le faut.1. tout en restant évidemment accessible 11 à la machine. Cette notion est évidemment dépendante de notre savoir faire informatique à l’instant présent. il faut commencer par son organe principal : le micro-processeur. L’ordinateur Chapitre 2 (deuxième essai) Comment ça marche ? Le problème avec le programme précédent est qu’il est très loin de ce qu’un ordinateur sait faire naturellement. soit 1024 × 1024 × 1024 mémoires de 8 bits (mémoires pouvant stocker des nombres entre 0 et 255).

mais où le nom de ces instructions ainsi que leurs arguments sont explicites.edx // // // mettre dans le registre eax le contenu de l’adresse où est mémorisée la variable a effectuer eax=eax*3 idem mais b dans ecx effectuer edx=eax+ecx*2 mettre le contenu du registre edx à l’adresse où est mémorisée la variable c Sous l’environnement Visual Studio que nous utiliserons.3 // mov ecx. où a. mais aussi de savoir où est rangée la variable a." en C++.dword ptr [a] // // // imul eax.ecx. jeune Padawan ! Nous en reparlons aussi tout de suite ! 16." : 00415A61 00415A64 00415A67 00415A6A 00415A6D 8B 6B 8B 8D 89 45 C0 4D 14 55 F8 03 EC 48 E0 A part encore une fois la colonne de gauche..ecx dword ptr [c]. Le nombre au début de chaque ligne est une adresse.c sont trois variables entières : 00415A61 00415A64 00415A67 00415A6A 00415A6D mov eax. Si on avait demandé c=3∗a+3∗b. Ceci parce qu’il s’agit d’un programme en langage assembleur.dword ptr [b] ecx. Le vrai langage du micro-processeur est le langage machine. en la suite de nombres 8B45F86BC0038B4DEC8D14488955E0 est propre au Pentium que nous avons utilisé pour notre exemple : Une fois traduit en langage machine pour un micro-processeur donné. c’est moi qui ai ajouté les remarques sur le coté droit !). Voici ce que ça donne pour notre "c=3∗a+2∗b. Nous allons en reparler. Monde ! Voici par exemple ce que doit faire le micro-processeur quand on lui demande d’exécuter "c=3∗a+2∗b. notre programme serait devenu : 00415A61 00415A64 00415A67 00415A6A 00415A6D 00415A6F 8B 6B 8B 6B 03 89 45 C0 4D C9 C1 45 F8 03 EC 03 E0 mov imul mov imul add mov eax.dword ptr [a]" lui demanderait non seulement de décoder cette suite de symboles. les informaticiens programmaient comme cela il n’y a pas si longtemps.3 eax. le reste est relativement lisible pour l’Homme (attention.[eax+ecx*2] // mov dword ptr [c]. puisqu’il contiennent des lettres.dword ptr [a] eax. chaque suite de nombres 15 correspond évidemment à une instruction précise.eax 15. ce programme est désigné comme du Code Machine. A part lui..3 ecx.eax. Comprendre "mov eax. dans lequel les instructions sont des nombres. C’est tout de suite moins compréhensible 16 ! Notons que chaque micro-processeur à son jeu d’instructions ce qui veut dire que la traduction de c=3∗a+2∗b. Bonjour. Et pourtant.b. et beaucoup moins bien que lorsqu’on a pu enfin programmer en assembleur ! 16 . le micro-processeur ne comprend pas l’assembleur.dword ptr [b] // lea edx. Remarquons aussi que les concepteurs du Pentium ont décidé de créer une instruction spécifique pour calculer edx=eax+ecx∗2 en une seule fois car elle est très fréquente.. En réalité. c’est-à-dire un langage où chaque instruction est vraiment une instruction du micro-processeur. Patience. C’était déjà très bien par rapport à l’époque antérieure où il fallait programmer en base 2.eax.1. un programme C++ n’a de sens que pour ce micro-processeur. Nombres un peu bizarres.2. L’ordinateur 2. certes.

ou même des premières cartes programmables avec 256 octets ! 19. on a opté pour un système adapté au fait que ces nombres sont sur 8 bits : plutôt que d’écrire les nombre en binaire. 13. Ainsi. de 0 à 7.F).[eax+ecx*3]" n’existe pas ! Mais revenons à nos nombres. . Il faut inventer des chiffres au delà de 9 et on prend A. 10..B. on compte avec deux chiffres au lieu de dix d’habitude (c’est à dire en décimal ou base 10).. un peu comme les variables du C++. 2.1. 110. on l’a déjà dit. E. Encore un anglicisme. A. le choix de la base 16 permettait de représenter le contenu d’un octet sur deux chiffres (0. Il est aujourd’hui encore utilisé quand on désigne le contenu d’un octet ou une adresse 22 . 11. D.. etc. 10. Souvenons nous avec une larme à l’oeil des premiers PC qui avaient 640Ko (kilo-octet soit 1024 octets). avec deux chiffres.. on compte de 0 à 15 soit 16 nombres possibles .. Bonjour.F.B.. Bref avec n bits. F. ou FF en hexadécimal. 11. 2. 1C..C. avec 2 chiffres. Ainsi 1F en hexadécimal vaut 31. 12..1.2 La mémoire La mémoire interne du micro-processeur est gérée comme des registres.D.2.. Les nombres 00415A61. etc.A. sur plus de 2 chiffres : 8 pour les processeurs 32 bits. 21. B=byte=8 bits 20. ou 2 nombres en hexadécimal et va de 0 à 255. ou base 2. .. avec 3 chiffres. Chaque chiffre s’appelle un bit.. ces nombres étaient écrits en binaire. alors qu’entre binaire et décimal.E. ou hexadécimal. Coin des collégiens (suite) : en base 16. Ainsi. surtout dans des abréviations comme 512kb/s données pour le débit d’un accès internet. 111 en binaire vaut 7 . On numérote simplement les octets et on obtient ainsi des adresses mémoire.. Un octet peut s’écrire avec 8 bits en binaire. b=bit. cela donne : 0. chaque chiffre hexadécimal valant pour un paquet de 4 bits.E. ce qui était exactement ce que comprenait le micro-processeur. byte en anglais. . 1A. 1B. Quand on compte. Monde ! 2.. C’est devenu déraisonnable quand la taille de la mémoire a dépassé les quelques centaines d’octets.. de 0 à 255 soit 256 = 16 × 16 nombres possibles. devient en mémoire : 17..C. 101.. 1. ou 11111111 en binaire. désignée en général par la mémoire de l’ordinateur. Il s’agit des fameuses "barrettes" 18 de mémoire que l’on achète pour augmenter la capacité de sa machine et dont les prix fluctuent assez fortement par rapport au reste des composants d’un ordinateur. 22. Quoique. On voit facilement qu’avec un chiffre on compte de 0 à 1 soit deux nombres possibles . c’est moins immédiat.. Attention donc à ne pas confondre byte et bit.. notre fameux c=3∗a+2∗b. Le coin des collégiens : en binaire. B. de 0 à 3. 100.. Je me souviens avoir appris la base 2 en grande section de maternelle avec des cubes en bois ! Étrange programme scolaire.. 1. Un octet 19 correspond à un nombre binaire de 8 bits 20 .D.. vus plus haut sont des adresses ! Au début. typiquement une ou plusieurs barrettes pour un total de 1 ou 2Go. on utilise de la mémoire en quantité bien plus importante..1. 19. Dans ce cas. 17 . Aujourd’hui.9. soit 8 = 2 × 2 × 2 nombres. Et je ne dis pas ça pour me trouver une excuse d’être devenu informaticien.. on peut coder 2n (2 multiplié par lui-même n fois) nombres. 18. soit à 28 = 256 valeurs possibles. Cela donne : 0. il n’est pas question de donner des noms à chaque octet. 9. Le système hexadécimal 21 était adopté. Avec 1 chiffre. . 111. mais en nombre prédéfini. Le contenu d’un octet de mémoire étant lui aussi donné sous la forme d’un nombre. Pour stocker 17 la suite d’instructions à lui fournir. Pour se repérer dans la mémoire. on compte avec 16 chiffres.. L’ordinateur car "lea edx. Les conversions de binaire à hexadécimal sont très simples. voire pour les plus agés d’entre nous des premiers ordinateurs personnels avec 4Ko. C. soit 4 = 2 × 2 nombres . 16 pour les processeurs 64 bits. Cette mémoire est découpée en octets.

d’autres (little-endian) 27 que a = a4 a3 a2 a1 ... soit 232 valeurs possibles (plus de 4 milliards). avec 4 octets on peut compter en binaire sur 4 × 8 = 32 bits. de tel sorte que l’instruction 8B45F8 aille bien chercher la variable a ! C’est un travail pénible. Comme les PowerPC des vieux Macs 27.dword ptr [a] dans notre cas.1.. 25.2.. cela effaçait des instructions et le programme pouvait faire de "grosses bêtises" — ceci est aujourd’hui impossible sous Windows ou Linux. L’ordinateur 2. 00500000 a1 00500001 a2 a 00500002 a3 00500003 a4 00500004 b1 00500005 b2 b 00500006 b3 00500007 b4 . où les octets a1 . . 24... et ne concerne plus que certains systèmes. La mémoire ne sert pas uniquement à stocker la suite d’instructions à exécuter mais aussi toutes les variables et données du programme. Si on se trompait. . Au mieux.. on risquait d’écrire au mauvais endroit de la mémoire. on a en plus 25 : adresse mémoire contenu représente . Cela explique la simplicité de l’instruction mov eax. 26. Elles ne sont pas à une adresse mémoire définie à l’avance de manière absolue mais à une adresse relative à l’emplacement où la fonction rangera ses variables locales en fonction de ce que le programme aura fait avant. Ce qui était le plus pénible n’était pas de décider où il fallait ranger les variables en mémoire.. Ainsi nos variables a. les variables étaient des variables locales à la fonction main() donc stockées dans la pile. Bref... Dans notre cas. Monde ! adresse mémoire contenu représente 00415A61 8B 00415A62 45 mov eax.. Les variables ayant plus de 256 valeurs possibles sont forcément stockées sur plusieurs octets. . Bonjour. cela effaçait une autre variable — ce comportement est encore possible de nos jours — au pire. que le C++ fait pour nous et que les programmeurs faisaient autrefois à la main 24 . .dword ptr [a] 00415A63 F8 00415A64 6B 00415A65 C0 imul eax. Nous verrons tout cela plus tard. les registres du micro-processeur étant insuffisants. Ainsi.. Cela signifie que : Tout comme pour les instructions. mais d’ajuster les instructions en conséquence..eax. Nous faisons ici un horrible mensonge à des fins simplificatrices..b. Comme les processeurs Intel et AMD 18 . un nombre stocké par un microprocesseur dans un fichier peut ne pas être compréhensible par un autre micro-processeur qui relit le fichier ! 23. a4 combinés donnent l’entier a sur 32 bits.c sont stockées quelque part en mémoire sur un nombre d’octets suffisant 23 pour représenter des nombres entiers (ici 4 octets) et dans un endroit décidé par le C++. Certains processeurs (dits big-endian) 26 décident a = a1 a2 a3 a4 .3 ..

Un lecteur de 40Ko coûtait 5000F ! 33. Elle est rapide 28 mais a la mauvaise idée de s’effacer quand on éteint l’ordinateur. d’autres. on y stockait les instructions nécessaires pour que le programmeur puisse remplir ensuite la RAM avec les instructions de son programme. disques durs 33 ou bandes magnétiques. on utilisait des mémoires modifiables malgré tout. Il suffisait alors de mettre en ROM le nécessaire pour gérer ces moyens de stockages. L’ordinateur Autres Composants Micro-processeur et mémoire : nous avons vu le principal. dont nous ne parlerons pas ici. la mémoire qui sert à démarrer s’appelle le BIOS. Moyens de stockage Certains permettent de lire des données. les moyens de stockages étaient mécaniques : cartes ou bandes perforées. réservés aux importants centres de calcul. Moins que les registres.1. mais avec du matériel spécialisé (EPROMS). les cartes mémoire. les "clés USB". pour de très petites quantités de données. Cette mémoire contient en général le minimum pour que l’ordinateur démarre et exécute une tâche prédéfinie. Types de mémoire La mémoire dont nous parlions jusqu’ici est de la mémoire vive ou RAM. Maintenant. Complétons le tableau avec quelques autres éléments importants de l’ordinateur. Il est flashable et ses paramètres de règlage sont en CMOS. Rajoutez un facteur 50 supplémentaire entre la mémoire et la mémoire cache du processeur ! Au début. etc. Aujourd’hui. mais le quotidien des ordinateurs personnels. Alors. Il est pénible qu’une ROM ne puisse être modifiée. à une époque. 100 fois plus en débit) a a. d’autres les deux à la fois. Il faut donc aussi de la mémoire morte ou ROM. 28. Initialement. disquettes 32 . de manière aléatoire. Monde ! 2. DVD. Attention à l’usure de la pile ! 30. à une mémoire consommant peu (CMOS) et complétée par une petite pile. Bonjour. on a souvent recours à de la mémoire pouvant se modifier de façon logicielle (mémoire "flashable") ou. etc. de manière séquentielle. on peut rajouter les CD. c’est-à-dire la plupart du temps ! 31. dans l’ordre que l’on veut. d’autres d’en écrire. ou même que le cache mémoire du processeur.2. Certains ne délivrent les données que dans l’ordre. Le luxe. 32. Les premiers étaient de véritables moteurs de voiture. Puis ils devinrent magnétiques : mini-cassettes 31 . Dans un PC. A chaque fois qu’on allumait l’ordinateur mais aussi à chaque fois que le programme plantait et s’effaçait lui-même. 19 . Il fallait retaper le programme à chaque fois 30 ! On a donc rapidement eu recours à des moyens de stockage pour sauver programmes et données à l’extinction de l’ordinateur.3 2. Faire travailler le micro-processeur avec le disque dur est BEAUCOUP plus lent qu’avec la mémoire (1000 fois plus lent en temps d’accès. c’est-à-dire de la mémoire conservant ses données quand l’ordinateur est éteint mais qui en contre-partie ne peut être modifiée 29 . 29. Très lent et très peu fiable.1. Ils sont en général bien plus lents que la mémoire et c’est sûrement ce qu’il faut surtout retenir ! On recopie donc en RAM la partie des moyens de stockage sur laquelle on travaille.

. Maintenant. véritables puissances de calcul. comme des entrées et des sorties entre le micro-processeur et la réalité. Le système d’exploitation est aujourd’hui responsable de gérer les fichiers. à tel point que certains programmeur les utilisent pour faire des calculs sans même afficher quoi que ce soit. c’est pire.2 Système d’exploitation Notre vision jusqu’ici est donc la suivante : 1. imprimante. C’était le célèbre code ASCII (65 pour A. on a eu envie de gérer plusieurs programmes et d’en exécuter plusieurs : l’un après l’autre. Unix (dont linux) et MAC/OS sont les plus répandus. Ainsi les cartes graphiques. ce principe a évolué : 1. sont-elles devenues une partie essentielle de l’ordinateur. enfin avec plusieurs processeurs par machine. Espérons qu’un jour les utilisateurs ne seront pas eux-aussi des périphériques ! 20 . Un programme est souvent lui même en plusieurs parties s’exécutant en même temps (les threads). les formats sont aujourd’hui nombreux. il exécute en permanence plusieurs instructions en même temps (on dit qu’il est super-scalaire) ! 38. Ils étaient initialement là pour servir d’interface avec l’Homme. Qui appellerait périphérique un caméscope qu’on relie à un ordinateur pour envoyer des vidéos sur internet ou les transférer sur un DVD ? Ce serait presque l’ordinateur qui serait un périphérique du caméscope ! 2. concurrents. d’autres encore contenaient eux-mêmes des fichiers 36 . Plus encore. modem. Les plus courantes étaient les textes. 3. c’est l’ordinateur qui est parfois juste considéré comme maillon entre différents appareils. Windows. Ces instructions lui permettent de lire d’autres instructions présentes sur le disque dur et qu’il recopie en RAM. Bonjour. Le contenu du disque dur a été organisé en fichiers. scanner. 2. écran. A moins que les entrées ou les sorties ne soient échangées via les périphériques. Les répertoires. Certains fichiers représentaient des données 34 . etc. 35. les interfaces avec les périphériques ou les utilisateurs 39 . s’est dégagé le concept de système d’exploitation 38 . Assez vite. souris. mais son rôle le plus délicat est de gérer les programmes (ou tâches ou 34. Il exécute les instructions en question pour il lire des données (entrées) présentes elles-aussi sur le disque dur et générer de nouvelles données (sorties).). il est difficile de voir encore les choses de cette façon. qui pouvaient être considérées comme un périphérique allant avec l’écran. Monde ! Périphériques On appelle encore périphériques différents appareils reliés à l’ordinateur : clavier. 37. On parle de fichier exécutable. Pour gérer tout cela. Système d’exploitation 2. 36. Aujourd’hui.. 66 pour B. Le processeur démarre avec les instructions présentes en ROM. Operating System 39. d’autres des programmes 35 . puis pour plusieurs utilisateurs en même temps (multi-utilisateurs) 37 . puis plusieurs en même temps (multi-tâches). Quant au processeur. où chaque octet représentait un caractère. etc. et plus ou moins normalisés. A l’ère du multimédia.2.2. 2. Les processeurs devenant plus rapides et les capacités du disque dur plus importantes.

3. Au mieux. Monde ! 2. les différentes versions d’un système d’exploitation essaient de pouvoir exécuter les programmes faits pour les versions précédentes. le disque étant très lent. La mémoire d’un process devient mémoire virtuelle : si un process est déplacé à un autre endroit de la mémoire physique (la RAM). même s’ils contiennent tous les deux des instructions pour le même processeur. sans prévenir. On en profite même pour mettre temporairement hors RAM (donc sur disque dur) un process en veille. La Compilation process) en train de s’exécuter. tout comme les versions successives d’une famille de processeur essaient de continuer à comprendre les instructions de leurs prédécesseurs. Un fichier exécutable est spécifique. mais aussi à un système d’exploitation donné. 2. 41.2. Seule sa lenteur (et le bruit du disque dur !) permet en général de s’en rendre compte (on peut alors s’en assurer avec le gestionnaire de tâche du système). Il doit pour cela essentiellement faire face à deux problèmes 40 : 1. il ne s’en rend pas compte. que l’on paye souvent au prix d’une complexité et d’une lenteur accrues. Avec l’arrivée des systèmes d’exploitation. Bonjour. un programme exécutable linux ne tournera pas sous Windows et réciproquement. le disque dur à la place de la mémoire et peut devenir très lent. non seulement à un processeur donné. tout comme les versions successives d’un logiciel essaient de pouvoir lire les données produites avec les versions précédentes. il utilise. Il s’agit de donner la main de manière intelligente et équitable. Les processeurs ont évidemment évolué pour aider le système d’exploitation à faire cela efficacement. 2. la mémoire contenant les instructions de celle contenant les données. En pratique. Lorsqu’un process à besoin de trop de mémoire. au sein d’un même process. Autre progrès : on gère maintenant la mémoire virtuelle de façon à séparer les process entre eux et. mais aussi de replacer un process interrompu dans la situation qu’il avait quittée lors de son interruption. On peut aussi utiliser le disque dur pour qu’un process utilise plus de mémoire que la mémoire physique : mais attention. Il se contente de modifier anarchiquement ses données. Faire travailler le processeur successivement par petites tranches sur les différents programmes. les fichiers exécutables ont du s’adapter pour de nombreuse raisons de gestion et de partage de la mémoire. Il est rigoureusement impossible qu’un process buggé puisse modifier ses instructions ou la mémoire d’un autre process en écrivant à un mauvais endroit de la mémoire 41 .3 La Compilation Tout en essayant de comprendre ce qui se passe en dessous pour en tirer des informations utiles comme la gestion de la mémoire. ce qui est déjà pas mal ! 21 . C’est la compatibilité ascendante. On dit qu’il swappe (ou pagine). Gérer la mémoire dédiée à chaque process. nous avons entrevu que transformer 40. En pratique. ce process risque de devenir lui aussi très lent. une partie ajustable de la mémoire est réservée à chaque process.

D’autres langages. la production du fichier exécutable se fait de la façon suivante : 1. Link : fichier objet + autres fichiers objets + bibliothèque standard ou autres → fichier exécutable. Par opposition. mais aussi d’une bibliothèque supplémentaire fournie par un tiers.. — des fonctions ou variables faisant partie du langage et que le programmeur utilise sans les reprogrammer lui-même.. Monde ! un programme C++ en un fichier exécutable est un travail difficile mais utile. etc. 44. cout|min()|. L’exécution alors est évidemment très lente. Elle nécessite un cours à part entière et nous n’en parlerons pas ici ! 45. suite d’instructions en langage machine. La synthèse de ces fichiers en un fichier exécutable s’appelle l’édition des liens. Un programme Java est alors traduit en son équivalent dans ce langage machine. Il peut s’agir de la bibliothèque des fonctions faisant partie de C++.).3. 2. Dans le cas du C++ et de la plupart des langages compilés (Fortran. La contrepartie de cette portabilité est évidemment une perte d’efficacité. Une bibliothèque est en fait un ensemble de fichiers objets pré-existants regroupés en un seul fichier. comme Java. En résumé. le "vrai" langage machine du processeur est alors appelé code natif. Compilation : fichier source → fichier objet. 22 . c’est-à-dire de dépendance au processeur et au système. le "byte code". en plaçant une couche intermédiaire entre le processeur et le programme : la machine virtuelle. Cependant. C.. on sait tout programmer (Évidemment. La Compilation 2. le programme C++. La traduction en code natif ou en byte code d’un programme s’appelle la compilation 44 . Quand on sait programmer un compilateur. 43. Le résultat peut être exécuté sur n’importe quelle machine virtuelle Java. peut exécuter des programmes dans un langage machine virtuel 43 . Le travail de traduction est fait à l’exécution du programme qui est alors analysé au fur et à mesure 42 : on parle alors de langage interprété. un compilateur est un programme ! On le programme avec le compilateur précédent ! Même chose pour les systèmes d’exploitation. L’ensemble de ces instructions constitue ce qu’on appelle une bibliothèque 45 . Les principes de la compilation sont une des matières de base de l’informatique. Certains logiciels disposant d’un langage de programmation comme Maple ou Scilab ne transforment pas leurs programmes en langage machine. le fichier objet ne se suffit pas à lui-même.. décident de résoudre les problèmes de portabilité.2. Le programme qui réalise cette opération est plus souvent appelé linker qu’éditeur de liens. appelée bibliothèque standard. Des instructions supplémentaires sont nécessaires pour former un fichier exécutable complet : — de quoi lancer le main() ! Plus précisément. tout ce que le process doit faire avant et après l’exécution de main(). 42. On transforme un fichier source. mais qu’il veut utiliser dans son programme actuel. traditionnelle et très formatrice. etc). — des fonctions ou variables programmées par le programmeur lui-même dans d’autres fichiers source compilés par ailleurs en d’autres fichiers objet. même s’il est parfois pré-traité pour accélérer l’exécution. évidemment écrite pour un processeur et un système donnés. Un langage compilé est alors à opposer à un langage interprété. en un fichier objet. Cette machine. la compilation se fait vers du code natif. Bonjour. comme cout.

2 Debuggeur Lorsqu’un programme ne fait pas ce qu’il faut. Ca n’est évidemment pas très pratique.2. Lorsqu’un langage est interprété. quelques informations supplémentaires sont utiles pour le suivre.4. un debuggeur pour traquer les erreurs de programmation.lib ou . 47. 2.4 2. L’environnement de programmation L’environnement de programmation L’environnement de programmation est le logiciel permettant de programmer. Dans notre cas il s’agit de Visual Studio Express.4.cpp 46 . Il est mieux de pouvoir suivre son déroulement instruction par instruction et d’afficher à la demande la valeur des variables. 46. Bonjour.c sera considéré comme du C. Un environnement contient au minimum un éditeur pour créer les fichiers sources. Un fichier en . — Un fichier objet sera en .o (Linux/Mac) — Un fichier exécutable en . c’est le micro-processeur qui exécute le programme et on ne peut pas l’arrêter à chaque instruction ! Il faut alors mettre en place des points d’arrêt en modifiant temporairement le code machine du programme pour que le processeur s’arrête lorsqu’il atteint l’instruction correspondant à la ligne de source à debugger.so (Linux/Mac) 2.h — Les bibliothèques (ensembles de fichiers objets archivés en un seul fichier) : fichiers .exe (Windows) ou sans extension (Linux/Mac) Nous verrons aussi plus loin dans le cours : — Les "en-tête" C++ ou headers servant à être inclus dans un fichier source : fichiers . Monde ! 2. un compilateur/linker pour créer les exécutables. Si c’est compliqué à mettre au point. surtout dans un environnement de programmation graphique. et un gestionnaire de projet pour gérer les différents fichiers sources et exécutables avec lesquels on travaille. Débogueur en français ! 23 . il peut simplement s’agir d’un ensemble de programmes.obj (Windows) ou . il est relativement simple de le faire s’exécute pas à pas car c’est le langage lui-même qui exécute le programme. C’est le rôle du debuggeur 47 .4.dll (Windows) ou . c’est très simple à utiliser. Nous reportons ici le lecteur au texte du premier TP. Dans d’autres cas. Dans le cas d’un langage compilé. on peut essayer de comprendre ce qui ne va pas en truffant son source d’instructions pour imprimer la valeur de certaines données ou simplement pour suivre son déroulement.a ou . etc. En plus de quelques notions rudimentaires de C++ que nous verrons au chapitre suivant. Nous verrons au fur et à mesure des TP comment le debuggeur peut aussi inspecter les appels de fonctions.1 Noms de fichiers L’extension (le suffixe) sert à se repérer dans les types de fichier : — Un fichier source C++ se terminera par . espionner la modification d’une variable.

9. Monde ! TP Vous devriez maintenant aller faire le TP en annexe A.3 2. 4. Toujours bien indenter.. en retenir quelque chose est indispensable ! Vous y trouverez aussi comment installer Visual sur votre ordinateur (lien http://imagine. Touches utiles : F7 = = Build F5 = = Start debugging F10 = = Step over F11 = = Step inside Ctrl+K. L’environnement de programmation 2. Voici donc ce qu’il faut retenir du TP : 1. 6. Nettoyer (“clean”) quand on quitte. 2.2. Lancer directement une exécution sauve et génère automatiquement.Ctrl+F = Indent selection — Nous en savons maintenant assez pour apprendre un peu de C++. Si la pratique est essentielle. toujours travailler en local et sauvegarder sur le disque partagé. clé USB. Ne pas laisser passer des warnings ! 8. 3.4. etc.fr/~monasse/ Imagine++ mentionné à la fin du TP). Double-cliquer sur un message d’erreur positionne l’éditeur sur l’erreur.4. 24 . Utiliser CMake pour construire une solution Windows.enpc. Bonjour. Attention toutefois de ne pas confirmer l’exécution si la génération s’est mal passée.1. Savoir utiliser le debuggeur. 5. Sous Windows. 7..

etc. Une donnée ne pouvant être stockée n’importe comment.1 Tout dans le main() ! Rien dans les mains. Nous avons déjà rencontré les int qui sont le plus souvent aujourd’hui stockés sur quatre octets. 3. Et bien des élèves. 2. en différents points successifs : les expressions.. mais tout dans le main(). L’essentiel est avant tout de faire un programme qui marche ! 3. il faut à chaque fois décider de la place prise en mémoire (nombre d’octets) et du format. ce n’est que dans un autre chapitre que nous verrons la façon dont les fonctions mémorisent leurs variables dans la "pile". en plaçant l’intégralité du programme dans la fonction main(). c’est-à-dire de la façon dont les octets utilisés vont représenter les valeurs prises par la variable. C’est déjà une étape importante que de programmer au kilomètre. les fonctions.. Nous allons plutôt ici essayer de voir les choses telles qu’elles se présentent quand on apprend : progressivement et sur un peu tous les sujets à la fois 1 ! Ainsi. Nous allons commencer par programmer n’importe comment. . Premiers programmes Chapitre 3 Premiers programmes Parés à expérimenter au fur et à mesure avec notre environnement de programmation. rien dans les poches. — On organise souvent un manuel de programmation de façon logique par rapport au langage. Voici comment un débutant programme 2 . les variables. puis nous ajouterons un minimum d’organisation en apprenant à faire des fonctions. s’il est fait pour être lu dans l’ordre..1. La contre-partie de cette présentation est que ce polycopié. 1. il est temps d’apprendre les premiers rudiments du C++. les instructions.. est peut-être moins adapté à servir de manuel de référence.3. dès que le professeur n’est plus derrière ! . Le résultat est indigeste car il faut alors être exhaustif sur chaque point.1 Variables Types Les variables sont des mémoires dans lesquelles sont stockées des valeurs (ou données).

111 26 . −2. i n t k . puis -2147483647 et ainsi de suite jusqu’à -1. n’étaient pas faites dans les premières versions du C. Certains langages n’ont pas la notion de type ou essaient de deviner les types des variables.. i n t q=r = 4 ... 2147483647 = 011. Premiers programmes soit 32 bits.. Nous avons aussi vu que cette simple idée donne déjà lieu à deux façons d’utiliser les 4 octets : big-endian ou little-endian.110. Définition.m. l ... et pouvant prendre 232 = 4294967296 valeurs possibles 3 . 2. o=n ..001. les int s’adaptent au processeur et un programme compilé sur un processeur 64 bits aura des int sur 64 bits ! Si l’on a besoin de savoir dans quel cas on est.. Initialisation.3. de −2147483648 à 2147483647 suivant une certaine correspondance avec le binaire 7 . regardons sur un exemple la syntaxe à utiliser : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int i . les int stockent les nombres entiers relatifs 4 .000.. petit frère du C++. −2 = 111. / / D é f i n i t i o n m u l t i p l e k= l = 3 . Coin des collégiens : c’est à dire 0.. −3. 4. / / Ne m o d i f i e que i . Par convention. s =13. 1 = 000. / / Affectation multiple m= 4 . c’est préciser son type.. Constantes Avant de voir d’autres types de variables.. p=INT_MAX . s’il pourra mettre cette information à profit pour détecter des erreurs de programmation. −2147483648 = 100. En fait. Affectation. 7.. répétons-le : Préciser un type... 1. Tout dans le main() ! 3. en a avant tout besoin pour traduire le source C++ en langage machine. 5. c’est initialement pour préciser la mémoire et le format des variables qu’elles sont typées.. i n t n=5 .111. Nous verrons que le compilateur se livre à un certain nombre de vérifications de cohérence de type entre les différentes parties d’un programme. le C++ fournit les constantes INT_MIN et INT_MAX qui sont les valeurs minimales et maximales prises par les int. −1 = 111. Dire qu’une variable est un int. . mais aussi −1. p a s j ! cout << i << " " << j << " " . à un près ! 6. / / Erreur ! Dans ce programme : 3. cout << k << " " << l << " " << m << " " .001. Là. −2147483647 = 100. int j . // Affectation cout << i << " " . j=i . tout le monde fait pareil ! On compte en binaire à partir de 0. En C++. / / Définition i =2.1. Le compilateur. le suivant est -2147483648. i =1. avec autant de nombres négatifs que de nombres positifs 5 . . pourtant bien pratiques. / / E r r e u r ! const i n t s =12. dans le cas de 32 bits 6 . Ces vérifications. et arrivé à 2147483647. On a par exemple : 0 = 000.000.. c’est préciser la place mémoire et le format d’une variable. / / I n i t i a l i s a t i o n s cout << n << " " << o << " " << p << endl . car avant tout. soit.

les variables ont été définies au fur et à mesure des besoins. une fois les lignes 14 et 16 supprimées. En C++. — Enfin. En résumé. La ligne 14 provoque une erreur. 5 et 6 définissent un int nommé j . on peut rajouter const devant le type d’une variable : celle-ci devient alors constante et on ne peut modifier son contenu. Notez que. Tout dans le main() ! — Les lignes 1 et 2 définissent une variable nommée i 8 de type int puis affecte 2 à cette variable. La ligne 15 définit une telle variable et la ligne 16 est une erreur. il est inutile de tenter une initialisation simultanée. des variables sont définies et affectées en même temps.1. ce qui permet davantage de clarté. on peut définir les variables en cours de route. puis mémorise 1 dans i. le C ne permettait de définir les variables que toutes d’un coup au début du main(). En fait. elles valent n’importe quoi 9 ! — Attention toutefois. car cela pose des problèmes de portabilité. soit 2.3. Mais attention : 8. Les messages d’erreur du compilateur utiliseront plutôt ce vocabulaire ! 9. La représentation binaire de 2 est donc stockée en mémoire là où le compilateur décide de placer i. du moins sur une machine 32 bits. Ce n’est pas une évidence. Notez bien que i et j sont bien deux variables différentes : i passe à 1 mais j reste à 2 ! — La ligne 8 nous montre comment définir simultanément plusieurs variables du même type. Ce qui suit le "double slash" ( // ) est une remarque : le compilateur ignore toute la fin de la ligne. C’est une mauvaise idée d’utiliser la valeur d’une variable qui vaut n’importe quoi et un compilateur émettra généralement un warning si on utilise une variable avant de lui fournir une valeur ! 10. Premiers programmes 3. N’utilisez pas de caractères accentués. Le nom d’une variable est aussi appelé identificateur. — La ligne 9 nous apprend que l’on peut affecter des variables simultanément à une même valeur. ce qui permet de mettre des commentaires aidant à la compréhension du programme. remarque précédente sur INT_MAX 27 . — A la ligne 12. Par exemple. un entier ne vaut pas 0 lorsqu’il est créé et les octets où il est mémorisé gardent la valeur qu’il avaient avant d’être réquisitionnés pour stocker l’entier en question. recopie la valeur de i. ce (passionnant !) programme affiche 10 : 2 1 2 3 3 4 5 5 2147483647 Les noms de variable sont composés uniquement des caractères a à z (et majuscules). il n’est pas très esthétique). — La ligne 3 affiche la valeur de i puis un espace (sans aller à la ligne) — Les lignes 4. Ainsi. C’est interdit. on parle plutôt de variables initialisées : elles prennent une valeur initiale en même temps qu’elles sont définies. chiffres et underscore _ (évitez celui-ci. Portée Dans l’exemple précédent. cf. les variables ne sont pas initialisées par défaut : tant qu’on ne leur a pas affecté une valeur et si elles n’ont pas été initialisées. pour des raisons d’efficacité. mais ne peuvent pas commencer par un chiffre. dans j .

double x = 1 2 . ici sur 4 octets (Les curieux pourront explorer la documentation de Visual et voir que 11.1. i= j . / / Erreur : j n ’ e x i s t e pas encore ! i n t j =2. 66 pour B.3. C’est ce qu’on appelle la portée d’une variable.2 f . signed char d= −128. bool t = t r u e . que nous allons voir tout de suite. quelques types supplémentaires : f l o a t y=1. 28 . Tout dans le main() ! 3. Premiers programmes les variables "n’existent" (et ne sont donc utilisables) qu’à partir de la ligne où elles sont définies. sur d’autres de 0 à 255). Voici malgré tout les plus courants : i n t i =3. 3 . // // // // // Entier r e l a t i f Nombre r é e l ( d o u b l e p r é c i s i o n ) Caractère Chaîne de c a r a c t è r e s B o o l é e n ( v r a i ou f a u x ) Les nombres réels sont en général approchés par des variables de type double ("double précision".. nombres réels moins précis mais plus courts que les double. s t r i n g s= " hop " . j =k . les booléens sont des variables qui valent vrai (true) ou faux ( false ). le programme suivant provoque des erreurs de portée aux lignes 2 et 8 : int i . qu’il n’est heureusement pas besoin de connaître puisque la syntaxe ’A’ entre simples guillemets est traduite en 65 par le compilateur. Les caractères sont représentés par un entier sur un octet (sur certaines machines de -128 à 127.). etc. pour information. la correspondance caractère/entier étant celle du code ASCII (65 pour A. 3 ) . complex<double > z ( 2 . Les doubles guillemets sont eux réservés aux "chaînes" de caractères 11 . Attention.. unsigned char d = 2 5 4 . Voici. } i =k . Nous verrons ça aussi. ici sur 8 octets). unsigned i n t j = 4 . Ainsi. char c= ’A ’ . l’utilisation des string nécessite un #include<string> au début du programme. a. en prenant un peu d’avance sur la syntaxe des tests. Enfin. / / E r r e u r : k n ’ e x i s t e p l u s . Elles ont une durée de vie limitée et meurent dès que l’on sort du bloc limité par des accolades auquel elles appartiennent a . etc. Autres types Nous verrons les différents types au fur et à mesure. i f ( j >1) { i n t k=3. // // // // // Nombre Entier Entier Entier Nombre r é e l simple précision naturel r e l a t i f un o c t e t n a t u r e l un o c t e t complexe où l’on trouve : — les float . C’est un peu plus compliqué pour les variables globales.

c ’ e s t que i <=2 .01 avec 37 zéros avant le 1. — les unsigned int. 13. if ( t ) k=5. Le ’et’ s’écrit &&. Ils ne seront pas utilisés dans ce livre. Tout dans le main() ! les float valent au plus FLT\_MAX (ici.1.8e+308 et 2..2 Tests Tests simples Les tests servent à exécuter telle ou telle instruction en fonction de la valeur d’une ou de plusieurs variables.. — et enfin les nombres complexes 13 . / / S i on e s t i c i . Coin des collégiens : pas de panique ! Vous apprendrez ce que sont les nombres complexes plus tard. le ’ou’ ||. } Les variables de type booléen servent à mémoriser le résultat d’un test : bool t = ( ( i ==3)||( j = = 4 ) ) . l’égalité ==.. 10−38 ou 1e−38 vaut 0. 3. else j =5.012 toujours avec 37 zéros entre la virgule et le 1 (le 1 est à la place 38).2e−308).00.4e+38 vaut 34 suivis de 37 zéros (38 chiffres après le 3) et 1. Coin des collégiens : 1038 ou 1e+38 vaut 1 suivi de 38 zéros. la non-égalité !=. >=. < et <=. cout << " Une deuxième i n s t r u c t i o n " << endl . i f ( i >2) / / i e s t − i l p l u s g r a n d que 2? j =3.. environ 3. Si plusieurs instructions doivent être exécutées quand un test est vrai ( if ) ou faux (else). / / Cas p l u s c o m p l i q u é ! i f ( i ! = 3 || ( j ==2 && k ! = 3 ) || ! ( i > j && i >k ) ) { / / I c i . environ 1. qui vont de 0 à 255.3. 12.2e−38 vaut 0. entiers positifs utilisés pour aller plus loin que les int dans les positifs (de 0 à UINT_MAX... Il est trop tôt pour comprendre la syntaxe "objet" de cette définition mais il nous parait important de mentionner dès maintenant que les complexes existent en C++. la négation !.4e+38 12 ) et que leur valeur la plus petite strictement positive est FLT\_MIN (ici. qui vont de -128 à 127. soit 4294967295 dans notre cas).000. i e s t d i f f é r e n t d e 3 ou a l o r s / / j v a u t 2 e t k e s t d i f f é r e n t d e 3 ou a l o r s / / on n ’ a p a s i p l u s g r a n d a l a f o i s d e j e t d e k cout << " Une première i n s t r u c t i o n " << endl . Ils sont toujours entre parenthèses.. et les inégalités >. En compliquant : 3.2e−38). Premiers programmes 3.. de même que pour les double les constantes DBL\_MAX et DBL\_MIN valent ici environ 1. — les unsigned char.1. . 29 . on utilise des accolades pour les regrouper. — les signed char. Tout cela se comprend facilement sur l’exemple suivant : i f ( i ==0) / / i e s t − i l n u l ? cout << " i e s t nul " << endl .

instruction que nous verrons plus loin. ! Le "switch" On a parfois besoin de faire telle ou telle chose en fonction des valeurs possibles d’une variable. Premiers programmes Enfin. Tout dans le main() ! 3. Attention. Chaque cas possible pour les valeurs de la variable est précisé avec case et doit se terminer par break 15 . Au r e v o i r ! " << endl .. correspond aux cas non précisés. case ’ f ’ : cout << " Vous avez tapé ’ f ’ . à placer en dernier. break . do { c=_ g e t c h ( ) . Attention : utiliser if ( i==3) . un cin >> c. Enfin. Plusieurs case peuvent être utilisés pour préciser un cas multiple.. Le programme suivant 16 réagit aux touches tapées au clavier et utilise un switch pour afficher des commentaires passionnants ! 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 # i n c l u d e <iostream > using namespace s t d . il faudra utiliser _getch() après avoir fait un #include <conio. Sous Windows. Sans lui.h>.3.. case ’ e ’ : case ’ i ’ : case ’o ’ : case ’u ’ : case ’y ’ : cout << " Vous avez tapé une a u t r e v o y e l l e ! " << endl . affecte 3 à i puis renvoie 3 comme résultat du test. char c . break . Faire if ( i=3) . et non if ( i=3) ..1. # i n c l u d e <conio . le programme exécute aussi les instructions du cas suivant ! 16. 30 . une dernière chose très importante : penser à utiliser == et non = sous peine d’avoir des surprises 14 .. Elle est heureusement signalée aujourd’hui par un warning.. break . ce qui est considéré comme vrai car la convention est qu’un booléen est en fait un entier. h> / / Non s t a n d a r d ! i n t main ( ) { bool f i n i = f a l s e .. lit bien un caractère au clavier mais ne réagit pas à chaque touche : il attend qu’on appuie sur la touche Entrée pour lire d’un coup toutes les touches frappées ! Récupérer juste une touche à la console n’est malheureusement pas standard et n’est plus très utilisé dans notre monde d’interfaces graphiques.h> (cf. 14. C’est une erreur grave et fréquente d’oublier le break. f i n i =true . C’est peut-être l’erreur la plus fréquente chez les débutants.. / / Non s t a n d a r d ! switch ( c ) { case ’ a ’ : cout << " Vous avez tapé ’ a ’ ! " << endl . On utilise alors souvent l’instruction switch pour des raisons de clarté de présentation. le mot clé default. faux s’il est nul et vrai s’il est non nul ! 15. lignes 3 et 10) et sous Unix getch() après avoir fait un #include <curses.

et lui réitère sa question jusqu’à obtenir un nombre correct : 1 2 3 4 5 6 7 8 9 10 # i n c l u d e <iostream > using namespace s t d . le switch précédant ceci est équivalent à 17 : i f ( c== ’ a ’ ) cout << " Vous avez tapé e l s e i f ( c== ’ f ’ ) { cout << " Vous avez tapé f i n i =true . 3. Tout dans le main() ! default : cout << " Vous avez tapé a u t r e chose ! " << endl . / / On r e t o u r n e au d é b u t d e l a b o u c l e s i 17.. i n t main ( ) { int i . des structures de données pour tout. 31 . qui "tourne en rond" tant qu’un test est vrai. Si vous avez tout compris. do { / / Début d e l a b o u c l e cout << "Un nombre e n t r e 1 e t 1 0 . } while ( i <1 || i > 1 0 ) . ne pas oublier les break ! Vous avez pu remarquer cette ligne 2 un peu cryptique. break . Le programme suivant attend que l’utilisateur tape au clavier un entier entre 1 et 10. } } while ( ! f i n i ) .. Un namespace est un préfixe pour certains objets. Commençons par le do . il faut l’utiliser à bon escient. a u t r e chose ! " << endl . Au r e v o i r ! " << endl . On voit bien que le switch n’est pas toujours plus clair ni plus court. return 0. Et plus nous connaîtrons de C++. c i n >> i . rappelons la principale source d’erreur du switch : Dans un switch.1. etc..3 Boucles Il est difficile de faire un programme qui fait quelque chose sans avoir la possibilité d’exécuter plusieurs fois la même instruction. La ligne 2 permet d’omettre ce préfixe.1.. Premiers programmes 26 27 28 29 30 31 32 } 3. La plus utilisée est le for () . plus nous devrons nous rappeler cette règle et éviter de faire des fonctions pour tout. ’ f ’ .3. des objets pour tout. || c== ’ o ’ || c== ’ u ’ || c== ’ y ’ ) une a u t r e v o y e l l e ! " << endl . C’est le rôle des boucles. Le préfixe des objets standard du langage est std. Avant tout. while. SVP : " . C’est comme tout. } e l s e i f ( c== ’ e ’ || c== ’ i ’ cout << " Vous avez tapé else cout << " Vous avez tapé ’ a ’ ! " << endl . des fichiers séparés pour tout. mais ça n’est pas la plus simple à comprendre. Ainsi cout et endl ont pour nom complet std :: cout et std :: endl.

Attention. le for () est utilisé comme dans l’exemple précédent pour effectuer une boucle avec une variable (un indice) qui prend une série de valeurs dans un certain intervalle. On utilise aussi la virgule . quand on sait que : — On peut définir la variable dans la première partie du for () . On trouvera en fait plutôt : f o r ( i n t i = 1 . car plusieurs instructions séparées par une virgule deviennent en C++ une seule instruction qui consiste à exécuter l’une après l’autre les différentes instructions ainsi rassemblées. il s’arrête pour i=39 et j=43. i = i +2 . 18. i <=100. return 0. Le programme suivant affiche les entiers de 1 à 100 : i n t i =1. et exécute une instruction à la fin de chaque boucle. f o r ( i = 1 .1. Vient ensuite le while qui vérifie le test au début de la boucle. comme le while. Pour les curieux : ça n’a en fait rien d’extraordinaire. le programme suivant part de i=1 et j=100. — i++ est une abbréviation de i=i+1 — Puisqu’il n’y a ici qu’une seule instruction dans la boucle. Toujours pour les curieux. j > i . Premiers programmes / / ce t e s t est vrai cout << " Merci ! Vous avez tapé " << i << endl .. 32 . i ++) cout << i << endl . les accolades étaient inutiles. on a crée une boucle spéciale tant elle est fréquente : le for () qui exécute une instruction avant de démarrer. Les vieux C++ ne permettaient pas de définir la variable dans la première partie du for () . test et instruction finale sont séparées par un . Instruction initiale. ce qui donne le programme suivant.3. 20. i <=100. j = j −3) cout << i << " " << j << endl . Notez aussi qu’on peut abréger i=i+2 en i+=2 et j =j−3 en j −=3. i = i +1) { cout << i << endl . i = i +1. La variable cin est le pendant en entrée ("console in") de la sortie cout. cette variable admet le for () pour portée : elle n’est plus utilisable en dehors du for () 18 . } Enfin. Ainsi. pour mettre plusieurs instructions 19 dans l’instruction finale du for. effectue un test au début de chaque tour. Des C++ un peu moins anciens permettaient de le faire mais la variable survivait au for () ! 19. Tout dans le main() ! 11 12 13 14 } 3. absolument équivalent au précédent : int i . while ( i <=100) { cout << i << endl . } En général. et augmente i de 2 et diminue j de 3 à chaque tour jusqu’à ce que leurs valeurs se croisent 20 : f o r ( i n t i =1 . Notez la ligne 9 qui met dans i un nombre tapé au clavier. j = 1 0 0 .

cela donne : 0. Par exemple. / / On p r o p o s e l e m i l i e u cout << " S e r a i t −ce " << c << " ? : " ..0.3. Coin des collégiens : compter "modulo N". Premiers programmes 3. Pour cela.1. i n t main ( ) { i n t n=rand ( ) % 1 0 0 . / / V a l e u r s e x t r è m e s bool trouve= f a l s e ..4 3. i f ( i >n ) cout << "C ’ e s t moins " << endl . do { cout << " Votre p r i x : " . } while ( i ! = n ) .. do 21.3. char r .1. il va procéder par dichotomie. return 0. Modulo 4. afin de trouver au plus vite : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 # i n c l u d e <iostream > using namespace s t d . surtout à programmer. C’est évidemment plus intéressant. / / nombre à d e v i n e r e n t r e 0 e t 99 int i .1. Le programme choisit le prix. } Seule la ligne 7 a besoin d’explications : — la fonction rand() fournit un nombre entier au hasard entre 0 et RAND_MAX. Tout dans le main() ! Récréations Nous pouvons déjà faire de nombreux programmes. cout << " Repondez par + . e l s e i f ( i <n ) cout << "C ’ e s t plus " << endl . i n t main ( ) { cout << " C h o i s i s s e z un nombre e n t r e 1 e t 100 " << endl .3. jouer au juste prix. le modulo 100 sert à retomber entre 0 et 99.2. else cout << " Gagne ! " << endl . − ou = " << endl . c i n >> i .. i n t a =1 .1. quand c’est le programme qui devine. Par exemple 12%10 vaut 2 et 11%3 aussi ! Ici.0. c’est retomber à 0 quand on atteint N. On a besoin de rajouter #include <cstdlib> pour l’utiliser — % est la fonction modulo 21 . 33 . do { i n t c =( a+b ) / 2 . et l’utilisateur devine : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # i n c l u d e <iostream > # include <cstdlib > using namespace s t d . b = 1 0 0 .2.

/ / Rebond v e r t i c a l s i on s o r t } int nj= j +dj . Cette fonction n’est pas standard. i f ( r== ’ = ’ ) trouve= t r u e . } Notez ce endGraphics() dont la fonction est d’attendre un clic de l’utilisateur avant de terminer le programme. / / N o u v e l l e p o s i t i o n i f ( j +dj >h || j +dj <0) { d j=−d j .RED ) . j . / / E f f a c e m e n t i = n i . / / F e n ê t r e g r a p h i q u e i n t i =0 . / / D e s s i n d e l a b a l l e m i l l i S l e e p ( 1 0 ) . de manière à laisser la fenêtre visible le temps nécessaire. i n t main ( ) { i n t w=300 . j = 0 . / / On c h a n g e d e p o s i t i o n j =nj . on e s s a i e e n t r e c +1 e t b } while ( ! trouve && ( a<=b ) ) . / / C ’ e s t p l u s . 4 .1. La ligne 2 34 . j . openWindow (w.1. while ( r ! = ’ = ’ && r ! = ’ + ’ && r ! = ’− ’ ) . et elle est dans le namespace Imagine.. 4 . } endGraphics ( ) . . f i l l R e c t ( i . i f ( i +di >w || i +di <0) { di=−di . Tout dans le main() ! 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 } 3. (Voir l’annexe B pour les instructions graphiques. h ) .. e l s e i f ( r== ’− ’ ) b=c −1. Premiers programmes c i n >> r . d j = 3 . // Vitesse while ( t r u e ) { f i l l R e c t ( i . h> using namespace Imagine . On peut aussi compléter le programme "supplémentaire" du TP de l’annexe A. else cout << " Vous avez t r i c h é ! " << endl . Il s’agissait d’une balle rebondissant dans un carré.WHITE ) . . / / Rebond h o r i z o n t a l s i on s o r t } i n t n i = i +di . 4 . h = 2 1 0 . // Position i n t di =2 . on e s s a i e e n t r e a e t c −1 else a=c + 1 . 4 . / / C ’ e s t moins .3. / / On a t t e n d un peu . return 0. return 0.) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 # i n c l u d e <Imagine/Graphics . i f ( trouve ) cout << " Quel boss j e s u i s ! " << endl .

2 Fonctions Lorsqu’on met tout dans le main() on réalise très vite que l’on fait souvent des copier/coller de bouts de programmes. h> 2 using namespace Imagine .3. 35 .. Fonctions F IGURE 3.. et nous verrons alors comment faire. Mal découper un programme est la meilleure façon de ne plus avoir envie de le faire la fois suivante.1 montre un résultat : 1 # i n c l u d e <Imagine/Graphics .. — et pour moins se fatiguer ! Attention à bien comprendre quand faire une fonction et à ne pas simplement découper un programme en petits morceaux sans aucune logique a . En fait. Prenons le programme suivant. Premiers programmes 3. pouvoir réutiliser le travail déjà fait est le fil conducteur d’une bonne programmation. fillRect et milliSleep) sont aussi fournies par Imagine. 3.2. ou longtemps auparavant. nous aurons envie de réutiliser ce qui aura été fait dans d’autres programmes. mais aussi pour faire des économies de frappe au clavier ! Il faut regrouper les passages identiques en fonctions : — pour obtenir un programme clair. qui dessine des traits et des cercles au hasard. ou dans les programmes d’autres personnes. permet de l’appeler sans utiliser son nom complet Imagine::endGraphics(). Les autres fonctions appelées dans ce petit programme (openWindow. nous nous contentons. Plus tard. a.. ou juste pour faire plaisir au professeur. et dont la figure 3. Si des lignes de programmes commencent à se ressembler.. On le fait pour des raisons de clarté. Pour l’instant. de réutiliser ce que nous venons de taper quelques lignes plus haut. le bon critère est ici que la bonne solution est généralement la moins fatiguante. grâce aux fonctions. ..1 – Traits et cercles au hasard. c’est qu’on est vraisemblablement devant l’occasion de faire des fonctions. Encore une fois.

rand ( ) % 2 5 6 ) . i n t x2=rand ( ) % 3 0 0 . f o r ( i n t i = 0 . Fonctions 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 3. i n t main ( ) { openWindow ( 3 0 0 . y1 . i < 1 5 0 . c’est mauvais signe ! Bref.. Encore la règle du moindre effort. / / C e r c l e } endGraphics ( ) . Il vaudrait mieux mettre ces valeurs dans des variables et faire dépendre le reste du programme de ces variables. / / Rayon Color c c =Color ( rand ( ) % 2 5 6 . } i n t main ( ) 22. c c ) . i n t r c =rand ( ) % 1 0 . i ++) { i n t x1=rand ( ) % 3 0 0 . / / P o i n t i n i t i a l i n t y1=rand ( ) % 2 0 0 . c’est l’appel répété à rand() et à modulo pour tirer un nombre au hasard. } La première chose qui choque 22 . / / C e n t r e du c e r c l e i n t yc=rand ( ) % 2 0 0 . Il faut dès le début d’un programme repérer les paramètres constants utilisés à plusieurs reprises et les placer dans des variables dont dépendra le programme. / / T r a c é d e s e g m e n t i n t xc=rand ( ) % 3 0 0 .. a. / / P o i n t f i n a l i n t y2=rand ( ) % 2 0 0 . yc . return 0. C’est un défaut constant de tous les débutants et il faut le corriger tout de suite. Color c=Color ( rand ( ) % 2 5 6 . rand ( ) % 2 5 6 ) . 2 0 0 ) . c ) . Premiers programmes # include <cstdlib > using namespace s t d .3. Au passage. rand ( ) % 2 5 6 .b) bien en avance sur ce que nous sommes censés savoir faire.. On gagne alors beaucoup de temps a quand on veut les modifier par la suite. On aura souvent besoin de tirer des nombres au hasard dans un certain intervalle et il est naturel de le faire avec une fonction. à part évidemment la syntaxe "objet" des variables de type Color pour lesquelles on se permet un Color(r.2. nous corrigeons une deuxième chose qui choque : les entiers 300 et 200 reviennent souvent. notre programme devient : / / Nombre e n t r e 0 e t n−1 i n t hasard ( i n t n ) { r e t u r n rand ()%n . il faudra remplacer dans le programme tous les 300 et tous les 200. Si nous voulons changer les dimensions de la fenêtre. 36 . rc . Si on fait trop de copier/coller ou de remplacer avec l’éditeur. y2 .v. / / RVB drawLine ( x1 . rand ( ) % 2 5 6 . / / RVB f i l l C i r c l e ( xc . x2 ..

nous avons oublié d’initialiser le générateur aléatoire. } . hasard ( 2 5 6 ) . dis_bonjour_a_la_dame ( " Germaine " ) . nous n’aurions plus qu’à modifier la fonction hasard(). C’est encore une règle importante. si ce n’est que : 1. / / T r a c é d e s e g m e n t i n t xc=hasard (w) .1 Retour Nous venons de définir sans l’expliquer une fonction hasard() qui prend un paramètre n de type int et qui retourne un résultat. 37 . / / Rayon Color c c =Color ( hasard ( 2 5 6 ) . Enfin. Premiers programmes 3.2. f o r ( i n t i = 0 . nous n’avons alors plus à nous souvenir de l’existence de la fonction rand() ni de comment on fait un modulo. toujours ! 3. Son type de retour est alors void et il n’y a pas de return à la fin. h ) . f i l l C i r c l e ( xc . Mme " << nom_de_la_dame << " ! " << endl . return 0.3. . y2 . hasard ( 2 5 6 ) ) ..2. de type int lui aussi. yc=hasard ( h ) . Par exemple : void dis_bonjour_a_la_dame ( s t r i n g nom_de_la_dame ) { cout << " Bonjour . i < 1 5 0 . x2 . drawLine ( x1 . c ) . h = 2 0 0 . y1 . y1=hasard ( h ) . dis_bonjour_a_la_dame ( " F i t z g e r a l d " ) . C’est un peu vrai. Moindre effort.. y2=hasard ( h ) . hasard ( 2 5 6 ) ) . nous verrons une autre fois ce que cela signifie et comment le faire en modifiant juste la fonction hasard(). / / C e r c l e } endGraphics ( ) . openWindow (w. C’est même mieux que ça : nous devenons indépendant de ces deux fonctions. Mais en pratique. et si vous voulions tirer des nombres au hasard avec une autre fonction 23 .. Pourquoi vouloir le faire ? Dans notre cas parce que la fonction rand() utilisée est suffisante pour des applications courantes mais pas assez précise pour des applications mathématiques. On doit également faire une fonction quand on veut séparer et factoriser le travail. Si vous le permettez. Par exemple. hasard ( 2 5 6 ) .. / / P o i n t f i n a l Color c=Color ( hasard ( 2 5 6 ) . Il est ensuite plus facile a de modifier la fonction que toutes les lignes qu’elle a remplacées ! a. Fonctions { c o n s t i n t w=300 . yc . i ++) { i n t x1=hasard (w) . / / C e n t r e du c e r c l e i n t r c =hasard (w/ 2 0 ) . faire un modulo ne répartit pas vraiment équitablement les nombres tirés. Il n’y a pas grand chose à savoir de plus. c c ) . } On pourrait penser que hasard(w) est aussi long à taper que rand()%w et que notre fonction est inutile. rc . 23. / / P o i n t i n i t i a l i n t x2=hasard (w) . Une fonction peut ne rien renvoyer.

24. else s =1. return 0. Une fonction peut comporter plusieurs instructions return 24 . return s . raccrocher ( ) . Pour une fonction void. ce qui est bien plus clair et plus proche de notre façon de penser : i n t s i g n e _ a v e c _ u n _ s e u l _ r e t u r n ( double x ) { int s . } 3.3. i f ( x ==0) s =0. i n t numero=numero_telephone (nom ) . comme le Pascal 38 . composer ( numero ) .2. on utilise return sans rien derrière pour un retour en cours de fonction : void t e l e p h o n e r _ a v e c _ u n _ s e u l _ r e t u r n ( s t r i n g nom) { i f ( j_ai_le_telephone ) { i f (mon telephone_marche ) { i f ( e s t _ d a n s _ l _ a n n u a i r e (nom ) ) { i n t numero=numero_telephone (nom ) . Fonctions 3. i f ( ! mon telephone_marche ) return . e l s e i f ( x <0) s =−1. d e v e n u i n u t i l e ! return 1. i f ( ! e s t _ d a n s _ l _ a n n u a i r e (nom ) ) return . } } } } } void t e l e p h o n e r _ p l u s _ s i m p l e ( s t r i n g nom) { i f ( ! j_ai_le_telephone ) return . composer ( numero ) . i f ( x >0) / / N o t e z l ’ a b s e n c e d e e l s e . Cela permet de sortir quand on en a envie. Premiers programmes 2. i f ( ca_decroche ) { parler ( ) . Contrairement à certains vieux langages. } i n t s i g n e _ p l u s _ s i m p l e ( double x ) { i f ( x <0) r e t u r n −1.

ou double x=1/3. Premiers programmes 3. et non double x=1/3. et non x=hop. i n t a=hasard2 ( 1 . alors la variable en question n’est pas modifiée.. 1.. double x=double(i/j)... Attention à bien utiliser x=hasard3() et non simplement x=hasard3 pour appeler cette fonction sans paramètre..3. 1 0 ) .2. } / / Nombre e n t r e 0 e t 1 double hasard3 ( ) { r e t u r n rand ( ) / double (RAND_MAX) . Division entière : — double x=1.2. il suffit de convertir une de ces variables en double avec la syntaxe double (. et si ce paramètre était une variable dans la fonction appelante.. Ce simple programme est aussi l’occasion de parler d’une erreur très fréquente : la division de deux nombres entiers donne un nombre entier ! Ainsi.0/3. comme dans notre cas.3 Passage par référence Lorsqu’une fonction modifie la valeur d’un de ses paramètres. Plus clairement.) que nous verrons plus tard. puis convertit 0 en double pour le ranger dans x. Voici comment faire pour en passer plusieurs ou n’en passer aucun : / / Nombre e n t r e a e t b i n t hasard2 ( i n t a . — double x=double(i)/j... Cette conversion en double arrive trop tard ! 3. raccrocher ( ) .0. parler ( ) . a et non double x=i/j. 2.2 Paramètres Nous n’avons vu que des fonctions à un seul paramètre. écrire double x=1/3. i n t b ) { r e t u r n a +( rand ( ) % ( b−a + 1 ) ) . ni même a. } . Fonctions i f ( ! ca_decroche ) return . Il ne sait pas au moment de calculer 1/3 qu’on va mettre le résultat dans un double ! Il faut alors faire en sorte que le 1 ou le 3 soit une double et écrire double x=1.2. est une erreur car le C++ commence par calculer 1/3 avec des entiers.. Fonction sans paramètre : x=hop(). } 3.0/3. double x=hasard3 ( ) . Si. le programme suivant échoue : 39 . on a affaire à deux variables de type int. . ce qui donne 0.

alors qu’au moment de l’appel à echanger2. le x et le y de echanger2 deviennent des "liens" avec a et b. b ) . Il affiche 2 et non 6. et ce. on choisit l’exemple suivant pour justifier le besoin des références : void echanger1 ( i n t x . } . c’est en fait a qui est utilisée. On peut toutefois faire en sorte que la fonction puisse vraiment modifier son paramètre.2.. x=y .3. En fait.2.. Fonctions 3.. echanger2 ( a . cout << a << " " << b << endl . b ) . Mais son passage à 6 ne modifie pas a. En pratique. Premiers programmes void t r i p l e ( i n t x ) { x=x ∗ 3 . ce qui explique tout ! C’est la valeur de a qui est passée à la fonction triple () et non pas la variable a ! On parle de passage par valeur. } Généralement. cout << a << endl . puis 6. le paramètre x de la fonction triple vaut bien 2. . x=y . on utilise aussi les références pour faire des fonctions retournant plusieurs valeurs à la fois. i n t a =2 . Il suffit de rajouter un & derrière le type du paramètre : void t r i p l e ( i n t& x ) { x=x ∗ 3 . } . echanger1 ( a . Une bonne façon de comprendre le passage par référence est de considérer que les variables x et y de echanger1 sont des variables vraiment indépendantes du a et du b de la fonction appelante. b = 3 . A chaque fois que l’on utilise x dans echanger2. y= t . On s’agit alors d’un passage par référence (ou par variable). cout << a << " " << b << " " . Pour encore mieux comprendre allez voir le premier exercice du TP 2 (A.. y= t . } void echanger2 ( i n t& x .. i n t a =2. Nous verrons plus loin que x est mémorisé à un endroit différent de a. i n t& y ) { i n t t =x . echanger1() ne marchant pas. triple (a ). Ce programme affiche 2 3 3 2.. de la façon suivante : 40 . i n t y ) { i n t t =x .1) et sa solution.

hasard ( 2 5 6 ) .. x2 . i n t& y ) { x=. openWindow (w.. f o r ( i n t i = 0 .. hasard ( 2 5 6 ) ) . notre programme de dessin aléatoire deviendrait : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 # i n c l u d e <Imagine/Graphics . int a . b . / / Nombre e n t r e 0 e t n−1 i n t hasard ( i n t n ) { r e t u r n rand ()%n . y1 ) . y2 . h ) .2. y=hasard ( h ) . i n t& x . y2 ) . h . Color c=une_couleur ( ) . rc . i n t x2 .. h = 2 0 0 .. h . x2 . } i n t main ( ) { c o n s t i n t w=300 . xc . } . x1 . c ) . i ++) { i n t x1 . drawLine ( x1 . i < 1 5 0 . h> using namespace Imagine . } Color une_couleur ( ) { r e t u r n Color ( hasard ( 2 5 6 ) . / / Rayon Color c c =une_couleur ( ) . c c ) . un_point ( a . 41 .. Fonctions void un_point ( i n t& x . return 0. / / C e r c l e } endGraphics ( ) . yc ) .3. y1 . . y1 . / / C e n t r e du c e r c l e un_point (w. / / T r a c é d e s e g m e n t i n t xc .... y2 . Premiers programmes 3. f i l l C i r c l e ( xc . yc . y=. # include <cstdlib > using namespace s t d . h . i n t r c =hasard (w/ 2 0 ) . } void un_point ( i n t w. / / P o i n t i n i t i a l un_point (w. / / P o i n t f i n a l un_point (w.. b ) . i n t h . Ainsi. i n t& y ) { x=hasard (w) . yc .

x1 . Par contre. i n t r c =hasard (w/ 2 0 ) . Il devient même : i n t x1 . / / P o i n t f i n a l un_point (w. y2 . nous créons des fonctions en les définissant. yc .2. rc . } void f ( ) { } car à la ligne 3. h . Il faut alors connaître la règle suivante : 42 .3. et qu’aucun ordre ne conviendra. x2 . les fonctions ont une portée et ne sont connues que dans les lignes de source qui lui succèdent. y1 . y2 . il est plus difficile de faire compiler : void f ( ) { g ( ) . x2 . y2 ) . h . une_couleur ( ) ) . yc ) .4 Portée. Une des raisons de ce besoin est que : comme les variables. yc . une_couleur ( ) ) . / / C e r c l e 26 27 28 29 30 31 32 33 34 3. y1 ) . Fonctions 3. Il suffit ici de mettre les lignes 6 et 7 avant le main() pour que le programme compile. Déclaration. Définition Depuis le début. h . / / C e n t r e du c e r c l e un_point (w. Premiers programmes 40 } Avec le conseil suivant : penser à utiliser directement le résultat d’une fonction et ne pas le mémoriser dans une variable lorsque c’est inutile. / / P o i n t i n i t i a l un_point (w. / / Rayon f i l l C i r c l e ( xc . y1 . xc . i n t x2 . drawLine ( x1 . f () n’est pas connue. / / T r a c é d e s e g m e n t i n t xc . return 0. Ainsi.2. } puisque les deux fonctions on besoin l’une de l’autre. c’est-à-dire sans connaître le corps de la fonction. / / Erreur : g ( ) inconnue } void g ( ) { f (). le programme suivant ne compile pas : 1 2 3 4 5 6 7 i n t main ( ) { f (). Il est parfois utile de ne connaître que le type de retour et les paramètres d’une fonction sans pour autant savoir comment elle est programmée.

3. Premiers programmes

3.2. Fonctions

— Remplacer le corps d’une fonction par un ; s’appelle déclarer la fonction.
— Déclarer une fonction suffit au compilateur, qui peut "patienter" a jusqu’à sa définition.
a. En réalité, le compilateur n’a besoin que de la déclaration. C’est le linker qui devra trouver quelque part la définition de la fonction, ou plus exactement le résultat de la compilation
de sa définition !

Notre programme précédent peut donc se compiler avec une ligne de plus :
void g ( ) ;
void f ( )
{
g();
}

/ / D é c l a r a t i o n de g

/ / OK: f o n c t i o n d é c l a r é e

void g ( ) { / / D é f i n i t i o n d e g
f ();
}

3.2.5

Variables locales et globales

Nous avons vu section 3.1.1 la portée des variables. La règle des accolades s’applique évidemment aux accolades du corps d’une fonction.
Les variables d’une fonction sont donc inconnues en dehors de la fonction
On parle alors de variables locales à la fonction. Ainsi, le programme suivant est interdit :
void f ( )
{
int x ;
x =3;
}
void g ( ) {
int y ;
y=x ; / / E r r e u r : x i n c o n n u
}
Si vraiment deux fonctions doivent utiliser des variables communes, il faut alors
les "sortir" des fonctions. Elles deviennent alors des variables globales, dont voici un
exemple :
1 int z ; / / globale
2
3 void f ( )
4 {
5
int x ; / / loc ale
43

3.2. Fonctions

6
7
8
9
10
11
12
13
14
15
16
17

3. Premiers programmes

...
i f ( x<z )
...
}
void g ( )
{
int y ; / / loc ale
...
z=y ;
...
}
L’utilisation de variables globales est tolérée et parfois justifiée. Mais elle constitue une
solution de facilité dont les débutants abusent et il faut combattre cette tentation dès le
début :
les variables globales sont à éviter au maximum car
— elles permettent parfois des communications abusives entre fonctions,
sources de bugs a .
— les fonctions qui les utilisent sont souvent peu réutilisables dans des
contextes différents.
En général, elles sont le signe d’une mauvaise façon de traiter le problème.
a. C’est pourquoi les variables globales non constantes ne sont pas tolérées chez le débutant. Voir le programme précédent où g() parle à f () au travers de z.

3.2.6

Surcharge

Il est parfois utile d’avoir une fonction qui fait des choses différentes suivant le type
d’argument qu’on lui passe. Pour cela on peut utiliser la surcharge :
Deux fonctions qui ont des listes de paramètres différentes peuvent avoir le
même nom a . Attention : deux fonctions aux types de retour différents mais
aux paramètres identiques ne peuvent avoir le même nom b .
a. Car alors la façon de les appeler permettra au compilateur de savoir laquelle des fonctions on veut utiliser
b. Car alors le compilateur ne pourra différencier leurs appels.

Ainsi, nos fonctions "hasard" de tout à l’heure peuvent très bien s’écrire :
1
2
3
4
5
6
7
8
9

/ / Nombre e n t r e 0 e t n−1
i n t hasard ( i n t n ) {
r e t u r n rand ()%n ;
}
/ / Nombre e n t r e a e t b
i n t hasard ( i n t a , i n t b ) {
r e t u r n a +( rand ( ) % ( b−a + 1 ) ) ;
}
/ / Nombre e n t r e 0 e t 1
44

3. Premiers programmes

3.3. TP

F IGURE 3.2 – Mini tennis...

10 double hasard ( ) {
11
r e t u r n rand ( ) / double (RAND_MAX) ;
12 }
13 . . .
14
i n t i =hasard ( 3 ) ;
// entre 0 et 2
15
i n t j =hasard ( 2 , 4 ) / / e n t r e 2 e t 4
16
double k=hasard ( ) ; / / e n t r e 0 e t 1
17 . . .

3.3

TP

Nous pouvons maintenant aller faire le deuxième TP donné en annexe A.2 afin de
mieux comprendre les fonctions et aussi pour obtenir un mini jeu de tennis (figure 3.2).

3.4

Fiche de référence

Nous commençons maintenant à nous fabriquer une "fiche de référence" qui servira
d’aide mémoire lorsqu’on est devant la machine. Nous la compléterons après chaque
chapitre avec ce qui est vu dans le chapitre, mais aussi pendant le TP correspondant.
Les nouveautés par rapport à la fiche précédente seront en rouge. La fiche finale est en
annexe C.

45

3.4. Fiche de référence

3. Premiers programmes

Fiche de référence (1/2)
Variables

— if (i==0) j=1;

— Définition :
int i;
int k,l,m;

— if (i==0) j=1;
else
j=2;

— Affectation :
i=2;
j=i;
k=l=3;

— if (i==0) {
j=1;
k=2;
}

— Initialisation :
int n=5,o=n;

— bool t=(i==0);
if (t)
j=1;

— Constantes :
const int s=12;
— Portée :
int i;
// i=j; interdit!
int j=2;
i=j; // OK!
if (j>1) {
int k=3;
j=k; // OK!
}
//i=k; interdit!
— Types :
int i=3;
double x=12.3;
char c=’A’;
string s="hop";
bool t=true;
float y=1.2f;
unsigned int j=4;
signed char d=-128;
unsigned char d=25;
complex<double>
z(2,3);
— Variables globales :
int n;
const int m=12;
void f() {
n=10;
// OK
int i=m; // OK
...
— Conversion :
int i=int(x),j;
float x=float(i)/j;
Tests
— Comparaison :
== != < > <= >=
— Négation : !
— Combinaisons : && ||

— switch (i) {
case 1:
...;
...;
break;
case 2:
case 3:
...;
break;
default:
...;
}

if (x<0)
return -1;
if (x>0)
return 1;
return 0;
}
void afficher(int x,
int y) {
if (x<0 || y<0)
return;
if (x>=w || y>=h)
return;
DrawPoint(x,y,RED);
}
— Appel :
int f(int a) { ... }
int g() { ... }
...
int i=f(2),j=g();

— do {
...
} while(!ok);

— Références :
void swap(int& a,
int& b){
int tmp=a;
a=b;b=tmp;
}
...
int x=3,y=2;
swap(x,y);

— int i=1;
while(i<=100) {
...
i=i+1;
}

— Surcharge :
int hasard(int n);
int hasard(int a,
int b);
double hasard();

Boucles

— for(int i=1;i<=10;i++) Divers
...
— i++;
— for(int i=1,j=10;j>i;
i--;
i=i+2,j=j-3)
i-=2;
...
j+=3;
Fonctions
— j=i%n; // Modulo
— Définition :
— #include <cstdlib>
int plus(int a,int b) {
...
int c=a+b;
i=rand()%n;
return c;
x=rand()/
}
double(RAND_MAX);
void affiche(int a) {
cout << a << endl; Entrées/Sorties
}
— #include <iostream>
— Déclaration :
using namespace std;
int plus(int a,int b);
...
cout <<"I="<<i<<endl;
— Retour :
cin >> i >> j;
int signe(double x) {

46

3. Premiers programmes

3.4. Fiche de référence

Fiche de référence (2/2)
Erreurs fréquentes
— double x=1/3; // NON!
int i,j;
— Pas de définition de fonction
x=i/j; // NON!
dans une fonction !
x=double(i/j); //NON!
— int q=r=4; // NON!
Imagine++
— if (i=2) // NON!
if i==2 // NON!
— Voir documentation...
if (i==2) then // NON!
Clavier
— for (int i=0,i<100,i++)

Build : F7
// NON!
— Debug : F5
— int f() {...}
— Step over : F10
int i=f; // NON!

47

— Step inside : F11
— Indent : Ctrl+K,Ctrl+F
Conseils
— Nettoyer en quittant.
— Erreurs et warnings : cliquer.
— Indenter.
— Ne pas laisser de warning.
— Utiliser le debuggeur.
— Faire des fonctions.

.

BougeBalle ( x1 .1 Premiers tableaux De même qu’on a tout de suite ressenti le besoin d’avoir des boucles pour faire plusieurs fois de suite la même chose. v3 . nous manqueraient rapidement. x[1]. — 4. nous allons rajouter les tableaux qui.u[4] .. sinon. u4 . le compilateur réserve quelque part en mémoire de quoi stocker les 4 variables en question et gère de quoi faire en sorte que x[ i ] désigne la bonne variable..v [ 4 ] . x[2] et x[3]. v1 . u3 . y1 .. Nous étudierons dans un autre chapitre les tableaux de taille variable et les questions de mémoire ("pile" et "tas"). u4 . v2 ) .. u1 . u3 . y4 . y3 . Les tableaux Chapitre 4 Les tableaux Tout en continuant à utiliser les fonctions pour les assimiler.. Un autre exemple pour mieux comprendre. v3 ) .. BougeBalle ( x3 . u2 . BougeBalle ( x2 .. u1 .4. / / B a l l e .. v [ i ] ) . dans lequel int x[4] définit un tableau de 4 variables de type int : x[0]. i ++) BougeBalle ( x [ i ] . / / B a l l e i n t x4 .. En pratique. y3 . v4 . Ainsi. f o r ( i n t i = 0 . v2 .y[4] .. qui additionne des double deux par deux en mémorisant les résultats : . D’où les tableaux. v4 ) . il a été rapidement utile de faire plusieurs fois la même chose mais sur des variables différentes. le programme suivant : i n t x1 . . y4 . u [ i ] . i < 4 . y1 . v1 ) . Nous n’irons pas trop vite et ne verrons pour l’instant que les tableaux à une dimension et de taille fixe. . / / B a l l e i n t x3 . BougeBalle ( x4 . / / B a l l e i n t x2 . / / Balles . 1 2 3 4 pourra avantageusement être remplacé par : int x [4] . u2 . y2 . y [ i ] . y2 .

connue à l ’ e x é c u t i o n . ..4. / / i c i ..1. k [n+1]. i ++) z [ i ]= x [ i ]+ y [ i ] . Premiers tableaux 4. i < 1 0 0 . . z [ 5 ] . int i [n ] . . z[100] irait peut-être chercher la variable i de la boucle. . m a i s . // i n t n2 . z [ 1 0 0 ] . // i n t t 1 [ n1 ] . i n t n1 . . y [ 1 0 0 ] . Même si on pense que le compilateur pourrait connaître la taille. ce qui est plus lisible. il joue au plus idiot et n’accepte que des constantes : 1 2 3 4 5 6 7 8 9 10 11 12 13 double x [ 1 0 ] . y [ 4 ] . f o r ( i n t i = 0 . L’habitude est de faire une boucle avec i<100 comme test. j [2∗n ] . . Ensuite : un tableau doit avoir une taille fixe connue à la compilation. Tout accès à t[n] peut provoquer une erreur grave pendant l’exécution du programme.. n3 = 5 . m a i s connue uniquement à l ’ e x é c u t i o n donc ERREUR n3 p r e n d une v a l e u r .. . si on remplaçait la boucle pour que i aille de 1 à 100. / / i c i . x[100] irait certainement chercher y[0] à la place. . soit on accède à une zone mémoire illégale et le programme peut "planter" b . non c o n s t a n t e donc ERREUR ( S I ! ) 50 . // i n t n3 . on utilise x[0] à x[99]. a. z[i] avec n’importe quoi pour i irait écrire en dehors de la zone réservée aux données. D’abord : les indices d’un tableau t de taille n vont de 0 à n-1. Soit on va lire ou écrire dans un endroit utilisé pour une autre variable a . Il y deux choses essentielles à retenir.. . // // i n t t 3 [ n3 ] . . mais pas une variable.. l e s x [ i ] et y [ i ] prennent des valeurs . on u t i l i s e l e s z [ i ] . Mais attention à ne pas mettre i<=100 ! 2... Les tableaux double x [ 1 0 0 ] . Ci-dessus. plutôt que i<=99. const i n t n=5. Cette taille peut être un nombre ou une variable constante. 1. De même. // / / OK / / OK n1 n ’ a même p a s d e v a l e u r donc ERREUR n2 p r e n d une v a l e u r . // // i n t t 2 [ n2 ] . ce qui risquerait de faire ensuite des choses étranges. ce qui stopperait le programme plus ou moins délicatement ! Dans le dernier exemple. i valant n’importe quoi ! b. C’ EST UNE DES ERREURS LES PLUS FRÉQUENTES EN C++. c i n >> n2 . Dans l’exemple ci-dessus.

f o r ( i n t i = 1 . 2. Si vous devez calculer s = i=1 f (i) pour f donnée . double s .2. i <=100. s t r i n g s [ 2 ] = { " hip " . notamment quand on traduit une formule mathématique.. i ++) f [ i −1]=3∗ i + 4 . i <=100. " hop " } . P100 1 Je m’explique. i <=100.2 Initialisation Tout comme une variable.. + f (100). comme on le voit parfois : 1 2 3 4 5 6 double f [ 1 0 0 ] . / / Ca va mieux comme c a ! f o r ( i n t i = 1 ..4}. i ++) f [ i ]=3∗ i + 4 . ayant corrigé vos bugs : 5 6 7 8 9 10 double f [ 1 0 0 ] . Les tableaux 4. on peut très facilement utiliser des tableaux. / / S t o c k e f ( i ) d a n s f [ i −1] f o r ( i n t i = 1 . Initialisation Connaissant ces deux points.2}. / / Erreur ! 1. 6 f o r ( i n t i = 1 . un tableau peut être initialisé : int t [4]={1 . Attention toutefois : ne pas utiliser de tableau quand c’est inutile. mais plutôt directement sans tableau : 5 double s = 0 . n’allez pas écrire.4. f o r ( i n t i = 1 .. à la machine. ce qui épargnera.2 . i ++) s=s+ f [ i − 1 ] . Coin des collégiens : c’est-à-dire s = f (1) + f (2) + . ni.3 . t ={1 . la syntaxe utilisée pour l’initialisation ne marche pas pour une affectation 2 : int t [ 2 ] . i <=100. et à vous des bugs (donc vos nerfs !). Attention. i <=100. / / A t t e n t i o n aux i n d i c e s ! double s = 0 . par exemple f (i) = 3i + 4. un tableau (donc de la mémoire et des calculs). 51 . 4. i ++) 7 s=s +(3∗ i + 4 ) . même. i ++) s=s+ f [ i ] . Nous verrons plus bas que l’affectation ne marche même pas entre deux tableaux ! Tout ceci s’arrangera avec les objets.

On comprendra plus tard pourquoi.3. i n t v a l ) { x= v a l . 52 .. Un void f(int& t[4]) ou toute autre syntaxe est une erreur. i n t v a l ) { f o r ( i n t i = 0 . 4. 0 ) . Il est du coup de plus en plus fréquent que les programmeurs utilisent directement des variables de type vector qui sont des objets implémentant les fonctionnalités des tableaux tout en se comportant d’avantage comme des variables standard. on a besoin de passer les tableaux en paramètres à des fonctions. } / / R a p p e l : c ’ e s t c e c i q u i marche ! void a f f e c t e 2 ( i n t& x .3 . — Une fonction ne peut pas retourner un tableau b .4. affiche ( t ) . i ++) s [ i ]= v a l . La syntaxe à utiliser est simple : void a f f i c h e ( i n t s [ 4 ] ) { f o r ( i n t i = 0 . int t [4]={1 . Nous pensons aussi que la connaissance des tableaux. } / / Une f o n c t i o n q u i marche s a n s ’& ’ void r e m p l i t ( i n t s [ 4 ] . i < 4 . i n t v a l ) { x= v a l . Les tableaux Spécificités des tableaux Les tableaux sont des variables un peu spéciales. } . donc : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 / / R a p p e l : c e c i ne marche p a s void a f f e c t e 1 ( i n t x . mais il faut savoir deux choses : — Un tableau est toujours passé par référence bien qu’on n’utilise pas le ’&’ a . / / a ne s e r a p a s mis à 0 3... ni retourné. même si elle demande un petit effort..2 . Spécificités des tableaux 4. i ++) cout << s [ i ] << endl . affecte1 (a . b. i n t a =1. i < 4 . a. les concepteurs du C++ ont voulu qu’un tableau ne soit ni passé par valeur.. par soucis d’efficacité. est incontournable et aide à la compréhension de la gestion de la mémoire.4}. Nous préférons ne pas parler dès maintenant des vector car leur compréhension nécessite celle des objets et celle des "template".. Ils ne se comportent pas toujours comme les autres variables 3 .3 4.1 Tableaux et fonctions Tout comme les variables.3. } .

4. Une fonction n’est pas tenue de travailler sur une taille de tableau unique. i n t n ) { f o r ( i n t i = 0 . c=somme1 ( a . on la passe en paramètre en plus du tableau : 1 2 3 4 5 6 7 8 9 10 11 / / Une f o n c t i o n q u i ne marche p a s void a f f i c h e 1 ( i n t t [ ] ) { f o r ( i n t i = 0 . affecte2 (a . mais il est impossible de demander à un tableau sa taille ! On utilise la syntaxe int t [] dans les paramètres pour un tableau dont on ne précise pas la taille.. } / / Comment on f a i t en p r a t i q u e void a f f i c h e 2 ( i n t t [ ] . 53 . c ) . i ++) z [ i ]= x [ i ]+ y [ i ] . i n t z [ 4 ] ) f o r ( i n t i = 0 . b ) .b [ 4 ] . remplit ( t . Les tableaux 17 18 19 20 21 22 cout << a << endl . .. / / OK: ’ z ’ e s t p a s s é p a r r é f é r e n c e ! } int a [4] .. Spécificités des tableaux // vérification / / a s e r a b i e n mis à 0 // vérification / / Met l e s t [ i ] à 0 / / V é r i f i e que l e s t [ i ] v a l e n t 0 et aussi : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 / / Somme d e deux t a b l e a u x q u i ne c o m p i l e même p a s / / Pour r e t o u r n e r un t a b l e a u i n t somme1 ( i n t x [ 4 ] . f o r ( i n t i = 0 . i <TAILLE ( t ) .4. i n t y [ 4 ] . i ++) cout << t [ i ] << endl . / / ERREUR somme2 ( a . int t [ 4 ] . } / / En p r a t i q u e . / / r e m p l i s s a g e de a e t b int c [ 4 ] . cout << a << endl . on f e r a donc comme ç a ! / / Somme d e deux t a b l e a u x q u i marche void somme2 ( i n t x [ 4 ] . b . i < 4 .3. return z . 0 ) . i ++) z [ i ]= x [ i ]+ y [ i ] . } .. i < 4 . affiche ( t ) .. i <n . / / OK Enfin. i ++) / / TAILLE ( t ) n ’ e x i s t e p a s ! ? ? ? ? cout << t [ i ] << endl . Comme il faut bien parcourir le tableau dans la fonction et qu’on ne peut retrouver sa taille. 0 ) . i n t y [ 4 ] ) [ 4 ] { / / on p e u t i m a g i n e r m e t t r e l e / / [ 4 ] i c i ou a i l l e u r s : // rien n ’y f a i t ! int z [ 4 ] . et c’est utilisé tout le temps..

. t ) . 3 ) . i ++) t [ i ]= s [ i ] .. i < 4 . t [ 4 ] . i n t t [ 4 ] ) { t =s .4} . 4 . / / OK 4.2 . / / OK } . 2 ) . / / OK . int s [4]={1 . 2 } . ni même un warning. s e t 1 ( s . c’est que : Affecter un tableau ne marche jamais mais ne génère pas toujours une erreur de compilation. f o r ( i n t i = 0 . le programme : int s [4]={1 . / / OK a f f i c h e 2 ( t2 .. . t [ 4 ] . C’est le cas entre deux paramètres de fonction. 54 .. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 / / F o n c t i o n q u i ne marche p a s / / Mais q u i c o m p i l e t r è s b i e n ! void s e t 1 ( i n t s [ 4 ] .4.4} .2 .3.2 . t =s . / / OK Le problème.4} .. int t2 [ 3 ] = { 3 . t [ 4 ] .2 Affectation C’est simple : Affecter un tableau ne marche pas ! Il faut traiter les éléments du tableau un par un. t ) . 5 } . / / Ne f a i t p a s c e qu ’ i l f a u t ! / / m a i s c o m p i l e s a n s warning ! } / / F o n c t i o n q u i marche ( e t q u i c o m p i l e ! −) void s e t 2 ( i n t s [ 4 ] . Les tableaux int t1 [ 2 ] = { 1 ..3 .3 . Ainsi..4.3 . a f f i c h e 2 ( t1 . / / Sans e f f e t s e t 2 ( s . Récréations 12 13 14 15 4. Nous comprendrons plus tard pourquoi et l’effet exact d’une telle affectation.. / / ERREUR d e c o m p i l a t i o n ne marche pas et on est obligé de faire : int s [4]={1 . i ++) t [ i ]= s [ i ] . i n t t [ 4 ] ) { f o r ( i n t i = 0 . i < 4 .

4. / / Nombre d e b a l l e s // / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / Generateur a l e a t o i r e / / A n ’ a p p e l e r qu ’ une f o i s .3) — Les fonctions noRefreshBegin et noRefreshEnd qui servent à accélérer l’affichage de toutes les balles (voir documentation de Imagine++ annexe B).. # include <cstdlib > # i n c l u d e <ctime > using namespace s t d . / / Rayon d e l a b a l l e const i n t nb_balls =30. puis amélioré avec des fonctions et de constantes lors du TP de l’annexe A.4.4. Grâce aux tableaux. Nous tirons aussi la couleur et la position et la vitesse initiales des balles au hasard.1 Multi-balles Nous pouvons maintenant reprendre le programme de la balle qui rebondit. h> using namespace Imagine .. Récréations F IGURE 4. // / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / C o n s t a n t e s du programme c o n s t i n t width = 2 5 6 . (momentanément figées ! Allez sur la page du cours pour un programme animé !) 4. donné à la section 3. Plusieurs fonctions devraient vous être inconnues : — L’initialisation du générateur aléatoire avec srand((unsigned int)time(0)). qui est expliquée dans le TP 3 (annexe A.1 – Des balles qui rebondissent. Voici le listing du programme (exemple d’affichage (malheureusement statique !) figure 4.4 Récréations 4.2. Les tableaux 4.1) : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # i n c l u d e <Imagine/Graphics . / / Largeur de l a f e n e t r e const i n t height =256. / / Hauteur d e l a f e n e t r e const i n t b a l l _ s i z e =4. a v a n t Random ( ) void InitRandom ( ) 55 . il est facile de faire se déplacer plusieurs balles à la fois.4.1.

y+=v . Color c o l ) { f i l l R e c t ( x−b a l l _ s i z e . byte ( Random ( 0 . 56 . c o l ) . height −b a l l _ s i z e ) . vb [ i ] . / / C o u l e u r s d e s b a l l e s InitRandom ( ) . y−b a l l _ s i z e . } // / / / / / / / / / / / / / / / / / / / / / / / / / / / / / // Position et vitesse aleatoire void I n i t B a l l e ( i n t &x . / / Rebond s u r l e s b o r d s h a u t e t b a s e t c o m p t a g e du s c o r e i f ( y+v< b a l l _ s i z e || y+v>height −b a l l _ s i z e ) v=−v . / / Position et v i t e s s e des b a l l e s i n t xb [ n b _ b a l l s ] . v=Random ( 0 . h e i g h t ) . byte ( Random ( 0 . 2 5 5 ) ) . 4 ) . f o r ( i n t i = 0 . i n t b ) { r e t u r n a +( rand ( ) % ( b−a + 1 ) ) . ub [ i ] . i n t &y . } // / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / Fonction p r i n c i p a l e i n t main ( ) { / / Ouverture de l a f e n e t r e openWindow ( width . i n t &u . } // / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / A f f i c h a g e d ’ une b a l l e void D e s s i n e B a l l e ( i n t x . Color &c ) { x=Random ( b a l l _ s i z e . 2 5 5 ) ) ) . i n t &y . i n t y . Récréations 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 4. i n t &v ) { / / Rebond s u r l e s b o r d s g a u c h e e t d r o i t i f ( x+u>width−b a l l _ s i z e || x+u< b a l l _ s i z e ) u=−u . Les tableaux { srand ( ( unsigned i n t ) time ( 0 ) ) . i ++) { I n i t B a l l e ( xb [ i ] . u=Random ( 0 .2∗ b a l l _ s i z e +1 . 2 5 5 ) ) . ub [ n b _ b a l l s ] . 2 ∗ b a l l _ s i z e +1 . Color cb [ n b _ b a l l s ] .4. width−b a l l _ s i z e ) . i n t &u . vb [ n b _ b a l l s ] . 4 ) .4. yb [ i ] . } // / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / D e p l a c e m e n t d ’ une b a l l e void BougeBalle ( i n t &x . i n t &v . } / / Entre a e t b i n t Random ( i n t a . i < n b _ b a l l s . c=Color ( byte ( Random ( 0 . / / Mise a j o u r d e l a p o s i t i o n x+=u . y=Random ( b a l l _ s i z e . cb [ i ] ) . yb [ n b _ b a l l s ] .

4. double &y . On ne peut évidemment constater la différence sur une figure dans un livre.4. 4. White ) . ub [ i ] . cb [ i ] ) . et à l’arc-cosinus acos(). double &u . et en précisant juste l’indice de la balle à déplacer (lignes 71 et 110).. 4 ) . width−b a l l _ s i z e ) . Auquel cas. Lorsqu’une balle se déplace. 2. Les tableaux 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 } 4. yb [ i ] . D e s s i n e B a l l e ( xb [ i ] . Les formules du choc de deux balles peuvent se trouver facilement dans un cours de prépa. aux sinus et cosinus cos() et sin () . vb [ i ] ) . ou sur le web. en pensant bien à les reconvertir en int lors de l’affichage (ligne 37). return 0. BougeBalle ( xb [ i ] . Color &c ) { 26 x=Random ( b a l l _ s i z e .4. Notez les lignes 79 et 80 qui évitent de considérer le choc d’une balle avec elle-même (nous verrons l’instruction continue une autre fois). La fonction ChocBalles implémente ces formules. on appelle ChocBalles qui modifie les vitesses des deux balles. (Notez l’inclusion du fichier <cmath> pour avoir accès à la racine carré sqrt () . } noRefreshEnd ( ) . yb [ i ] . 29 v=Random ( 0 .. } / / Boucle p r i n c i p a l e while ( t r u e ) { milliSleep (25).2 Avec des chocs ! Il n’est ensuite pas très compliqué de modifier le programme précédent pour que les balles rebondissent entre-elles. Téléchargez donc le programme sur la page du cours ! 23 / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / 24 / / P o s i t i o n e t v i t e s s e a l e a t o i r e 25 void I n i t B a l l e ( double &x . On réalise ensuite que les variables entières qui stockent positions et vitesses font que les erreurs d’arrondis s’accumulent et que les vitesses deviennent nulles ! On bascule alors toutes les variables concernées en double. yb [ i ] . Récréations D e s s i n e B a l l e ( xb [ i ] . on regarde aussi si elle rencontre une autre balle. yb [ i ] . 4 ) . } endGraphics ( ) . 57 . 28 u=Random ( 0 . cb [ i ] ) . 27 y=Random ( b a l l _ s i z e . On modifie donc BougeBalle en passant les tableaux complets des positions et des vitesses. double &v . Le tout donne un programme bien plus animé. Le listing ci-après a été construit comme suit : 1. La boucle de la ligne 78 vérifie ensuite via le test de la ligne 81 si l’une des autres balles est heurtée par la balle courante. i < n b _ b a l l s . Il faut donc que BougeBalle connaisse les positions des autres balles. noRefreshBegin ( ) . height −b a l l _ s i z e ) . f o r ( i n t i = 0 . 3. i ++) { D e s s i n e B a l l e ( xb [ i ] .

double&v2 ) { / / Distance double o2o1x=x1−x2 . v 2 i =V∗ s ∗ s . double&y2 . i . v2+= v 2 i ∗ i y + v 2 j ∗ j y . c o l ) . j < n b _ b a l l s . double&u2 . s= s i n ( th ) . n t i c . j ) double v 1 i =V∗ c ∗ c . Les tableaux c=Color ( byte ( Random ( 0 . i f (V==0) r e t u r n . double u [ ] . i y =Vy/V. / / Rebond s u r l e s b o r d s h a u t e t b a s e t c o m p t a g e du s c o r e i f ( y [ i ]+ v [ i ] < b a l l _ s i z e || y [ i ]+ v [ i ] > height −b a l l _ s i z e ) v [ i ]=−v [ i ] . v1= v 1 i ∗ i y + v 1 j ∗ j y +v2 . o2o1y=y1−y2 . i . / / Dans r e p è r e d ’ o r i g i n e (O. j y = i x . double&u1 . / / Angle double th=acos (H/d ) . y ) u1= v 1 i ∗ i x + v 1 j ∗ j x +u2 . u2+= v 2 i ∗ i x + v 2 j ∗ j x . double v [ ] . 2 5 5 ) ) . double&v1 . 2 5 5 ) ) . / / Même v i t e s s e / / R e p è r e s u i v a n t V ( o2 .4. Color c o l ) { f i l l R e c t ( i n t ( x)− b a l l _ s i z e . 2∗ b a l l _ s i z e +1 . i n t i ) { / / Rebond s u r l e s b o r d s g a u c h e e t d r o i t i f ( x [ i ]+u [ i ] > width−b a l l _ s i z e || x [ i ]+u [ i ] < b a l l _ s i z e ) u [ i ]=−u [ i ] . / / Même c e n t r e ? / / R e p è r e ( o2 . y ) double Vx=u1−u2 . 2 5 5 ) ) ) . double y [ ] . Vy=v1−v2 . f o r ( i n t j = 0 . j x =−iy . double d= s q r t ( o2o1x ∗ o2o1x+o2o1y ∗ o2o1y ) . x . / / V i t e s s e a p r è s c h o c d a n s ( o2 . } // / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / A f f i c h a g e d ’ une b a l l e void D e s s i n e B a l l e ( double x . double&x2 .4. double&y1 . i n t ( y)− b a l l _ s i z e . i f ( d==0) r e t u r n . v 2 j=−v 1 j . double y . byte ( Random ( 0 . org # i n c l u d e <cmath> void C h o c B a l l e s ( double&x1 . Récréations 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 4. j ++) { 58 . byte ( Random ( 0 . x . v 1 j =V∗ c ∗ s . / / Hauteur d ’ a t t a q u e double H=o2o1x ∗ j x +o2o1y ∗ j y . c=cos ( th ) . j ) double i x =Vx/V. } // / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / D e p l a c e m e n t d ’ une b a l l e void BougeBalle ( double x [ ] . } // / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / Choc e l a s t i q u e d e deux b a l l e s s p h e r i q u e s / / c f l a b o . double V= s q r t ( Vx∗Vx+Vy∗Vy ) .2∗ b a l l _ s i z e +1 .

ub [ i ] . / / C o u l e u r s d e s b a l l e s InitRandom ( ) . ub . yb [ i ] . cb [ i ] ) . i ) . vb [ i ] . cb [ i ] ) . } noRefreshEnd ( ) . v [ i ] . chaîne de caractère. BougeBalle ( xb . i < n b _ b a l l s . f o r ( i n t i = 0 . D e s s i n e B a l l e ( xb [ i ] . } } / / Mise a j o u r d e l a p o s i t i o n x [ i ]+=u [ i ] . et s . } / / Boucle p r i n c i p a l e while ( t r u e ) { milliSleep (25). vb . yb [ i ] . Les tableaux 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 4. u[ j ] . / / Position et v i t e s s e des b a l l e s double xb [ n b _ b a l l s ] . yb [ n b _ b a l l s ] . Color cb [ n b _ b a l l s ] . y [ i ]+=v [ i ] . } // / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / Fonction p r i n c i p a l e i n t main ( ) { / / Ouverture de l a f e n e t r e openWindow ( width . u[ i ] . D e s s i n e B a l l e ( xb [ i ] .4. yb [ i ] . v [ j ] ) . i ++) { I n i t B a l l e ( xb [ i ] . f o r ( i n t i = 0 .4. pour lequel s[ i ] renvoie le i-ème caractère de la chaîne s. y [ j ] . i < n b _ b a l l s . noRefreshBegin ( ) . Récréations i f ( j == i ) continue . } endGraphics ( ) . return 0. i ++) { D e s s i n e B a l l e ( xb [ i ] . yb . y [ i ] . x [ j ] .3 Mélanger les lettres Le programme suivant considère une phrase et permute aléatoirement les lettres intérieures de chaque mot (c’est-à-dire sans toucher aux extrémités des mots). vb [ n b _ b a l l s ] .4. } 4. size () le nombre de caractères de s (nous expliquerons plus tard la notation "objet" de cette fonction). La phrase considérée ici devient par exemple : Ctete pteite psahre dreviat erte ecorne libslie puor vorte parvue ceeravu 59 . i f ( abs ( x [ i ]+u [ i ]−x [ j ] ) < 2 ∗ b a l l _ s i z e && abs ( y [ i ]+ v [ i ]−y [ j ] ) < 2 ∗ b a l l _ s i z e ) { ChocBalles ( x [ i ] . White ) . cb [ i ] ) . ub [ n b _ b a l l s ] . Il utilise pour cela le type string. h e i g h t ) . yb [ i ] .

t [ b ]= c . 60 . " l i s i b l e " . l − 2 ) . int b . " v o t r e " . s t r i n g t =s . i ++) { i n t a=Random ( 1 .4. } // / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / Permuter l e s l e t t r e s i n t e r i e u r e s de s n f o i s s t r i n g Melanger ( s t r i n g s . // / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / Generateur a l e a t o i r e / / A n ’ a p p e l e r qu ’ une f o i s . InitRandom ( ) . do b=Random ( 1 . f o r ( i n t i = 0 .4. i f ( l <=3) return s . t [ a ]= t [ b ] . " d e v r a i t " . " phrase " . Récréations 4. " encore " . i n t n ) { int l=int ( s . i n t b ) { r e t u r n a +( rand ( ) % ( b−a + 1 ) ) . char c= t [ a ] . } return t . Les tableaux L’avez vous comprise ? Peu importe ! C’est le listing que vous devez comprendre : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 # i n c l u d e <iostream > # include <string > # include <cstdlib > # i n c l u d e <ctime > using namespace s t d . } / / Entre a e t b i n t Random ( i n t a . i <n . } i n t main ( ) { const i n t n=11. while ( a==b ) . " pauvre " . size ( ) ) . " e t r e " . s t r i n g phrase [ n ] = { " C e t t e " . a v a n t Random ( ) void InitRandom ( ) { srand ( ( unsigned i n t ) time ( 0 ) ) . l − 2 ) . " cerveau " } . " pour " . " p e t i t e " .

Les tableaux 4. nous complétons. 4. en rouge. cout << endl . la "fiche de référence" avec ce qui a été vu pendant ce chapitre et son TP. TP F IGURE 4.3 afin de mieux comprendre les tableaux et aussi pour obtenir un master mind (voir figure 4. return 0..4.5 f o r ( i n t i = 0 . i <n .6 Fiche de référence Comme promis. i ++) cout << Melanger ( phrase [ i ] .5. 3 ) << " " .2 le résultat d’une partie intéressante !). TP Nous pouvons maintenant aller faire le troisième TP donné en annexe A.2 – Master mind.. 48 49 50 51 52 53 } 4. 61 .

unsigned char d=25.y). Les tableaux Fiche de référence (1/2) Variables — Définition : int i. string s="hop". } — bool t=(i==0). — Affectation : i=2. j=k. while(i<=100) { . — Initialisation : int n=5. } Boucles ... // OK . } int i=f(2).. int hasard(int a. int tmp=a. default: .. } — Déclaration : int plus(int a.2f. break. — Portée : int i..RED). — if (i==0) { j=1.j=j-3) . } — for(int i=1... return -1.t[3]. // i=j. k=l=3. i=i+2.. — Retour : int signe(double x) { — Définition : if (x<0) — double x[5].6. k=2. complex<double> z(2. } while(!ok).. float y=1.j=g(). } t[i]=0.i++) void swap(int& a. return..3. — switch (i) { case 1: . i=i+1. case 2: case 3: .. // OK! } //i=k. — Variables globales : int n.i++) DrawPoint(x. void afficher(int x.j>i. Tableaux — int i=1. const int m=12..3.i<4. — if (i==0) j=1. return 0. — for(int i=1.i++) int g() { . interdit! int j=2.i<5.. if (t) j=1.. } a=b. — do { int x=3.i<n. break.. i=j. — Références : int n) { for(int i=0.. unsigned int j=4.l. // OK int i=m. — Conversion : int i=int(x).y[5]. . char c=’A’...i++) . // OK! if (j>1) { int k=3. bool t=true.i++) if (x>0) y[i]=2*x[i]. int b). — Initialisation : int y) { int t[4]={1.. void f() { n=10...j=10. for (int i=0.4}.j[2*n].4. if (x<0 || y<0) string s[2]={"ab". else j=2.. signed char d=-128.i<3."c"}. Tests — Comparaison : == != < > <= >= — Négation : ! — Combinaisons : && || — if (i==0) j=1.2.. swap(x. float x=float(i)/j. — Constantes : const int s=12.3}. — void init(int t[]. . j=i. . . } void affiche(int a) { cout << a << endl. t[i]=s[i]. Fonctions — Définition : int plus(int a.i<=10.m. Fiche de référence 4. } int i[n].. double x=12.y.. int& b){ t[i]=0.. return c.b=tmp.o=n. int k. — const int n=5. for(int i=0..3). interdit! — Types : int i=3. } — En paramètre : — Appel : — void init(int t[4]) { int f(int a) { ..j. return 1.y=2. double hasard().int b)... — Affectation : if (x>=w || y>=h) int s[3]={1. return. } for(int i=0. } 62 — Surcharge : int hasard(int n).int b) { int c=a+b.2.

Erreurs fréquentes } int t[4].. Utiliser le debuggeur.2}...Ctrl+F Conseils — — double x[10].. int l=s. x=i/j.. // Modulo — #include <cstdlib> . Les tableaux 4. .y[10]. i-=2. — int s[3]={1.size().. Ne pas laisser de warning. Tableaux : pas pour transcrire une formule mathématique ! . Faire des fonctions. — return t. j+=3..4. // NON! — Pas de définition de fonction — int t[2]. cout <<"I="<<i<<endl. Indenter. if i==2 // NON! if (i==2) then // NON! Clavier — for (int i=0. — #include <cmath> double sqrt(double x).. // NON! int i.i++) — y[i]=2*x[i].3}. for (int i=1.2. — j=i%n.. // NON! 63 Nettoyer en quittant. . — #include <string> using namespace std. t=s. i=rand()%n. x=rand()/ double(RAND_MAX). — #include <ctime> . — int t[n]. double cos(double x).t[3]..i<100.i++) — Build : F7 // NON! — Debug : F5 — int f() {. // NON! Imagine++ — if (i=2) // NON! — Voir documentation. //NON — — int n=5. // NON! dans une fonction ! — int q=r=4. double sin(double x). double acos(double x). // NON! — double x=1/3.} int i=f. cin >> i >> j. string s="hop".6. srand((unsigned int) time(0)).i<=10. // NON — — int f()[4] { // NON! — int t[4].. t=f(). Erreurs et warnings : cliquer. Entrées/Sorties — #include <iostream> using namespace std.. //NON! — Step over : F10 — Step inside : F11 — Indent : Ctrl+K. char c=s[0]. Fiche de référence Fiche de référence (2/2) Divers — i++. i--. // NON! x=double(i/j). t={1.j.

.

3 .4} . Les tableaux permettent de grouper des variables de même type.2 . il est tout aussi indispensable des fabriquer des structures de données.1.. . les mêmes conseils. 5. qui prendra la forme d’un inventaire des erreurs classiques commises par de nombreux débutants.. et même de celles. mais pour manipuler plusieurs variables simultanément.1 Révisions Avant cela. encore et toujours. } .. nous répéterons. constatées chez certains ! Enfin. int i=f .. — 5.1 Erreurs classiques En vrac : — Mettre un seul = dans les tests : if ( i=2) — Oublier les parenthèses : if i==2 — Utiliser then : if ( i==2) then — Mettre des virgules dans un for : for ( int i=0. Les structures Chapitre 5 Les structures Les fonctions et les boucles nous ont permis de regrouper des instructions identiques. . plus rares mais plus originales. .5.. t [ 4 ] . il est utile de nous livrer à une petite révision. — Vouloir affecter un tableau à un autre : int s [4]={1 . t =s .i<100..i++) — Oublier les parenthèses quand on appelle une fonction sans paramètre : int f () { .

.... / / Du g r a n d a r t . ou même mieux (voyez-vous la différence ?) : bool a l l o k = t r u e . ni même à ce qu’un compilateur peut comprendre ! Deux exemples : — Mélanger la syntaxe (si peu !) : void s e t ( i n t t [ 5 ] ) { . i f ( allok ) .. pour exécuter une instruction quand tous les ok(i) sont vrais.2 5. f o r ( i n t i = 0 . Ne me faîtes pas dire ce que je n’ai pas dit ! Les informaticiens théoriques considèrent parfois les programmes comme des formules. int s [ 5 ] . i <n . mais en plus ça ne colle ni aux grands principes de la syntaxe du C++. — Vouloir faire plusieurs choses à la fois. i f ( allok ) . i ++) ok ( i ) ) . Les structures Erreurs originales Là. i <n .. Souvent. f o r ( i n t i = 0 . / / Là . f o r ( i n t i = 0 . c ’ e s t quand même n ’ i m p o r t e q u o i ! alors qu’il suffit d’un : set ( s ) . croire que le for est un symbole mathématique comme 1 ou 1 . qu’on peut finalement simplifier en : bool a l l o k = t r u e . Révisions 5. i <n && a l l o k . on a déjà vu tenter un : i f ( f o r ( i n t i = 0 .. i f ( allok ) . i ++) a l l o k =( a l l o k && ok ( i ) ) .1.. le débutant ne se trompe plus : il invente carrément avec sans doute le fol espoir que ça existe peut-être. Ainsi. ou ne pas comprendre qu’un programme est une suite d’instructions à exécuter l’une après l’autre et non pas une forPn 1 mule Sn . 1. } . . non seulement ça n’existe pas.. / / J u s q u e l à .5.. t o u t va b i e n ! s e t ( i n t s [ 5 ] ) .. i ++) a l l o k =( a l l o k && ok ( i ) ) . i ++) a l l o k =ok ( i ) . alors qu’il faut faire : bool a l l o k = t r u e .. mais ça n’a rien à voir ! 66 . Par exemple.. i <n && a l l o k .1.

dont les variables en question deviennent des "sous-variables" appelées champs de la structure. limité et conçu pour faire des choses bien précises. Color c o u l e u r . a . de la confusion avec un autre langage. ou de son imagination débordante ! Toutefois. x=2.5. }. il est plus raisonnable d’adopter la conduite suivante : Tout ce qui n’a pas été annoncé comme possible est impossible ! 5.1 Définition Si les tableaux permettent de manipuler plusieurs variables d’un même type.2. y=3. 67 . et même des structures dans des structures : s t r u c t Cercle { Point centre . Indenter ! — Cliquer sur les messages d’erreurs et de warnings pour aller directement à la bonne ligne ! — Ne pas laisser de warning. On peut évidemment définir des champs de différents types. a .2 Les structures 5. les structures sont utilisées pour regrouper plusieurs variables afin de les manipuler comme une seule. d’une mauvaise assimilation des leçons précédentes. Voici par exemple un type Point possédant deux champs de type double nommés x et y : s t r u c t Point { double x . Les structures 5.2. y . Les structures Il est compréhensible que le débutant puisse être victime de son manque de savoir. En conséquence. il faut bien comprendre qu’un langage est finalement lui aussi un programme. — Utiliser le debuggeur. avec la particularité supplémentaire qu’on accède aux champs avec un point : Point a .3 Conseils — Indenter. On crée un nouveau type. 5.1. Attention par contre à Ne pas oublier le point virgule après l’accolade qui ferme la définition de la structure ! L’utilisation est alors simple. La structure est un nouveau type qui se manipule exactement comme les autres.4. Les champs se définissent avec la syntaxe des variables locales d’une fonction. Indenter.3. double rayon .

. y+b . le passage en paramètre. Les structures 5. 3 . M. 5. Comme pour un tableau ! 68 . x ) / 2 . 4 . P o i n t b ) { / / Passage par valeur r e t u r n s q r t ( ( a . r e t u r n M. y =( a . C. Les structures }. x= i . l’initialisation.. nous avions bien promis que seuls les tableaux avaient des particularités (passage par référence. Cercle C. 3. et même définir un champ de type tableau ! Ainsi.2. Point P [ 1 0 ] . 1 0 . / / Tableau de s t r u c t u r e s f o r ( i n t i = 0 . y ) ) . centre . } 2.2 Utilisation Les structures se manipulent comme les autres types 2 . c o u l e u r =Red . y ) / 2 . i ++) { P [ i ] . y−b . } .. } void a g r a n d i r ( C e r c l e& C ..2. l’affectation. / / I n i t i a l i s a t i o n .. alors il est vraisemblable que les paramètres en question puissent être avantageusement regroupés en une structure.5. Red } . // Affectations C e r c l e C= { { 1 2 . y−b . Point b ) { // retour P o i n t M. Si un programme devient pénible parce qu’on passe systématiquement plusieurs paramètres identiques à de nombreuses fonctions. // Initialisations c=a . x =12. y= f ( i ) . le retour d’une fonction : tout est semblable au comportement des types de base. 4 } . / / M o d i f i e l e r a y o n } Point milieu ( Point a . y =13. i < 1 0 . C . y ) ∗ ( a . 4 . On peut évidemment faire des tableaux de structures. L’intérêt des structures est évident et il faut Regrouper dans des structures des variables dès qu’on repère qu’elles sont logiquement liées... rayon = 1 0 . Ce sera plus simple et plus clair. rayon ∗ e c h e l l e . double e c h e l l e ) { / / Par r é f é r e n c e C . M. b=a . D’ailleurs. double d i s t a n c e ( P o i n t a . 3 . Seule nouveauté : on utilise des accolades pour préciser les valeurs des champs en cas d’initialisation 3 . 1 3 } . C. centre . les lignes suivantes se comprennent facilement : P o i n t a = { 2 . x ) ∗ ( a . x−b . c . x+b . pas de retour possible et pas d’affectation. x =( a . La définition. C . x ) + ( a . P [ i ] .. x−b . rayon=C .

. Les structures 5. / / Erreur ! D’ailleurs.. Attention. t=lancer ( ) . La situation s’améliorera avec les objets. tout comme pour les tableaux. P={1 . Récréation : TP F IGURE 5. Coin des collégiens : il y a dans ce TP des mathématiques et de la physique pour étudiant de l’enseignement supérieur. i ++) t . i < 5 . de [ i ]=1+ rand ( ) % 6 . / / champ d e t y p e t a b l e a u }. répétons-le : Tout ce qui n’a pas été annoncé comme possible est impossible ! 5. .1 – Corps célestes et duel.4 afin de mieux comprendre les structures. Tirage lancer ( ) { Tirage t . / / Un d é b u t d e j e u d e Yam ’ s s t r u c t Tirage { / / i n t de [ 5 ] .3. Tirage t ... la syntaxe utilisée pour l’initialisation ne marche pas pour une affectation 4 : Point P .. } .... mais on peut très bien faire les programmes en ignorant tout ça ! 69 . Nous ferons même des tableaux de structures 5 ! Nous obtien4.5.3 Récréation : TP Nous pouvons maintenant aller faire le TP donné en annexe A.. / / Un d é d e 1 à 6 return t .. 5.2}.. f o r ( i n t i = 0 .

5. Récréation : TP 5. Les structures drons un projectile naviguant au milieu des étoiles puis un duel dans l’espace (figure 5.1) ! 70 .3.

. Tests — Comparaison : == != < > <= >= — Négation : ! — Combinaisons : && || — bool t=(i==0). const int m=12.... — Variables globales : int n.. — for(int i=1. interdit! — Types : int i=3. signed char d=-128. float x=float(i)/j. default: . i=i+2. — Surcharge : — for(int i=1. .o=n.RED).. float y=1.. — Initialisation : int n=5.4.i<=10. . // OK .. } void affiche(int a) { cout << a << endl.. int& b){ — int i=1. while(i<=100) { a=b. — Conversion : int i=int(x). bool t=true... Les structures 5. } void afficher(int x. — Références : void swap(int& a. DrawPoint(x. complex<double> z(2.. } int g() { . int y) { if (x<0 || y<0) return. — Appel : int f(int a) { . Fonctions double hasard(). — Constantes : const int s=12..i++) swap(x.m.. string s="hop".. return 0. unsigned char d=25. void f() { n=10. interdit! int j=2.l. en rouge. if (x>=w || y>=h) return. return c. // i=j. int b). .j..int b) { int c=a+b. } Boucles — do { . — switch (i) { case 1: . case 2: case 3: . } while(!ok). if (t) j=1. — Définition : int plus(int a. int i=f(2). k=2.. Fiche de référence (1/2) — if (i==0) j=1. j=i.. — if (i==0) { j=1. unsigned int j=4. } — if (i==0) j=1. // OK! if (j>1) { int k=3. .j=g(). la "fiche de référence" avec ce qui a été vu pendant ce chapitre et son TP. if (x>0) return 1. — Portée : int i.b=tmp. // OK! } //i=k.j=10.j=j-3) int hasard(int a. char c=’A’.. nous complétons. } . 71 .. — Déclaration : int plus(int a. } Variables — Définition : int i.... double x=12. else j=2. k=l=3.3).5. } — Retour : int signe(double x) { if (x<0) return -1. } i=i+1.. // OK int i=m. — Affectation : i=2.3.. int k..y).y=2.4 5. . j=k. break.. break. Fiche de référence Fiche de référence Encore une fois.int b).. i=j. } int x=3.y.. int tmp=a.2f. int hasard(int n).j>i..

5.4. Fiche de référence

5. Les structures

Fiche de référence (2/2)
— #include <cstdlib>
— int n=5;
...
int t[n]; // NON
— Définition :
i=rand()%n;
— int f()[4] { // NON!
— double x[5],y[5];
x=rand()/
int t[4];
for(int i=0;i<5;i++)
double(RAND_MAX);
...
y[i]=2*x[i];
— #include <ctime>
return t; // NON!
...
— const int n=5;
}
srand((unsigned int)
int i[n],j[2*n];
int t[4]; t=f();
time(0));
— Initialisation :
— int s[3]={1,2,3},t[3];
— #include <cmath>
int t[4]={1,2,3,4};
t=s; // NON!
double sqrt(double x);
string s[2]={"ab","c"};
double cos(double x); — int t[2];
— Affectation :
t={1,2}; // NON!
double sin(double x);
int s[3]={1,2,3},t[3];
double acos(double x); — struct Point {
for (int i=0;i<3;i++) — #include <string>
double x,y;
t[i]=s[i];
using namespace std;
} // NON!
string s="hop";
— En paramètre :
— Point a;
char c=s[0];
— void init(int t[4]) {
a={1,2}; // NON!
int l=s.size();
for(int i=0;i<4;i++)
t[i]=0;
Imagine++
Entrées/Sorties
}
— #include <iostream>
— Voir documentation...
using namespace std;
— void init(int t[],
Clavier
...
int n) {
cout
<<"I="<<i<<endl;
for(int i=0;i<n;i++)
— Build : F7
cin >> i >> j;
t[i]=0;
— Debug : F5
}
Erreurs fréquentes
— Pas de définition de fonction — Step over : F10
Structures
dans une fonction !
— Step inside : F11
— struct Point {

int q=r=4; // NON!
double x,y;
— Indent : Ctrl+K,Ctrl+F
— if (i=2) // NON!
Color c;
if i==2 // NON!
};
Conseils
if (i==2) then // NON!
...
— Nettoyer en quittant.
Point a;
— for (int i=0,i<100,i++)
— Erreurs et warnings : cliquer.
a.x=2.3; a.y=3.4;
// NON!
a.c=Red;
— int f() {...}
— Indenter.
Point b={1,2.5,Blue};
int i=f; // NON!
— Ne pas laisser de warning.
— double x=1/3; // NON!
Divers
— Utiliser le debuggeur.
int i,j;
— i++;
x=i/j; // NON!
— Faire des fonctions.
i--;
x=double(i/j); //NON!
— Tableaux : pas pour transcrire
i-=2;
— double x[10],y[10];
une formule mathématique !
j+=3;
for (int i=1;i<=10;i++)
y[i]=2*x[i]; //NON — Faire des structures.
— j=i%n; // Modulo
Tableaux

72

6. Plusieurs fichiers !

Chapitre 6
Plusieurs fichiers !
Lors du dernier TP, nous avons réalisé deux projets quasiment similaires dont seuls les
main() étaient différents. Modifier après coup une des fonctions de la partie commune aux
deux projets nécessiterait d’aller la modifier dans les deux projets. Nous allons voir maintenant
comment factoriser cette partie commune dans un seul fichier, de façon à en simplifier les éventuelles futures modifications. Au passage 1 nous verrons comment définir un opérateur sur de
nouveaux types.

Résumons notre progression dans le savoir-faire du programmeur :
1. Tout programmer dans le main() : c’est un début et c’est déjà bien !
2. Faire des fonctions : pour être plus lisible et ne pas se répéter ! (Axe des instructions)
3. Faire des tableaux et des structures : pour manipuler plusieurs variables à la fois.
(Axe des données)
Nous rajoutons maintenant :
4. Faire plusieurs fichiers : pour utiliser des parties communes dans différents projets ou solutions. (A nouveau, axe des instructions)

F IGURE 6.1 – Plusieurs fichiers sources...
1. Toujours cette idée que nous explorons les différentes composantes du langages quand le besoin
s’en fait sentir.

6.1. Fichiers séparés

6.1

6. Plusieurs fichiers !

Fichiers séparés

Nous allons répartir notre code source dans plusieurs fichiers. Mais avant toute
chose :
Pour un maximum de portabilité du code, choisir des noms de fichiers avec
seulement des caractères standard (pas de lettres accentuées ni d’espace)
D’ailleurs il est aussi préférable d’éviter les accents pour les noms de variables et de
fonctions, tant pis pour la correction du français...

6.1.1

Principe

Jusqu’à présent un seul fichier source contenait notre programme C++. Ce fichier
source était transformé en fichier objet par le compilateur puis le linker complétait le
fichier objet avec les bibliothèques du C++ pour en faire un fichier exécutable. En fait,
un projet peut contenir plusieurs fichiers sources. Il suffit pour cela de rajouter un
fichier .cpp à la liste des sources du projet :
— Ouvrir le menu Project/Add New Item ou faire Ctrl+Maj+A ou cliquer sur
— Choisir l’ajout d’un fichier ’Visual C++/Code/C++ file’ et en préciser le nom
(sans qu’il soit besoin de préciser .cpp dans le nom)
Ainsi, en rajoutant un fichier C++ hop à un projet contenant déjà main.cpp, on se
retrouve avec une structure de projet identique à celle de la figure 6.1.
Après cela, chaque génération du projet consistera en :
1. Compilation : chaque fichier source est transformé en un fichier objet (de même
nom mais de suffixe .obj). Les fichiers sources sont donc compilés indépendamment les uns des autres.
2. Link : les différents fichiers objets sont réunis (et complétés avec les bibliothèques
du C++) en un seul fichier exécutable (de même nom que le projet).
Une partie des instructions du fichier principal (celui qui contient main()) peut donc
être déportée dans un autre fichier. Cette partie sera compilée séparément et réintégrée
pendant l’édition des liens. Se pose alors le problème suivant : comment utiliser dans
le fichier principal ce qui ce trouve dans les autres fichiers ? En effet, nous savions
(cf section 3.2.4) qu’une fonction n’était "connue" que dans les lignes qui suivaient
sa définition ou son éventuelle déclaration. Par "connue", il faut comprendre que le
compilateur sait qu’il existe ailleurs une fonction de tel nom avec tel type de retour et
tels paramètres. Malheureusement 2 :
une fonction n’est pas "connue" en dehors de son fichier. Pour l’utiliser dans
un autre fichier, il faut donc l’y déclarer !
En clair, nous allons devoir procéder ainsi :
— Fichier hop.cpp :
2. Heureusement, en fait, car lorsque l’on réunit des fichiers de provenances multiples, il est préférable que ce qui se trouve dans les différents fichiers ne se mélange pas de façon anarchique...

74

6. Plusieurs fichiers !

6.1. Fichiers séparés

// Définitions
void f ( i n t x ) {
...
}
int g () {
...
}
/ / Autres f o n c t i o n s
...
— Fichier main.cpp :
// Déclarations
void f ( i n t x ) ;
int g ( ) ;
...
i n t main ( ) {
...
// Utilisation
i n t a=g ( ) ;
f (a );
...
Nous pourrions aussi évidemment déclarer dans hop.cpp certaines fonctions de main.cpp
pour pouvoir les utiliser. Attention toutefois : si des fichiers s’utilisent de façon croisée,
c’est peut-être que nous sommes en train de ne pas découper les sources convenablement.

6.1.2

Avantages

Notre motivation initiale était de mettre une partie du code dans un fichier séparé
pour l’utiliser dans un autre projet. En fait, découper son code en plusieurs fichiers a
d’autres intérêts :
— Rendre le code plus lisible et évitant les fichiers trop longs et en regroupant les
fonctions de façon structurée.
— Accélérer la compilation. Lorsqu’un programme devient long et complexe, le
temps de compilation n’est plus négligeable. Or, lorsque l’on régénère un projet,
l’environnement de programmation ne recompile que les fichiers sources qui ont
été modifiés depuis la génération précédente. Il serait en effet inutile de recompiler un fichier source non modifié pour ainsi obtenir le même fichier objet 3 ! Donc
changer quelques lignes dans un fichier n’entraînera pas la compilation de tout
le programme mais seulement du fichier concerné 4 .
Attention toutefois à ne pas séparer en de trop nombreux fichiers ! Il devient alors plus
compliqué de s’y retrouver et de naviguer parmi ces fichiers.
3. C’est en réalité un peu plus compliqué : un source peu dépendre, via des inclusions (cf section
6.1.4), d’autres fichiers, qui, eux, peuvent avoir été modifiés ! Il faut alors recompiler un fichier dont une
dépendance a été modifiée. Visual gère automatiquement ces dépendances (C’est plus compliqué, mais
possible aussi, sous linux...)
4. En fait, Visual est même capable de ne recompiler que certaines parties du fichier quand les modifications apportées sont simples...

75

6.1. Fichiers séparés

6. Plusieurs fichiers !

F IGURE 6.2 – Même source dans deux projets

6.1.3

Utilisation dans un autre projet

Pour utiliser dans un projet 2 un fichier source d’un projet 1, il suffit de rajouter le
source en question dans la liste des sources du projet 2 ! Pour cela, après avoir sélectionné le projet 2 :
— Choisir le menu Project/Add Existing Item (ou Alt+Maj+A ou

)

— Sélectionner le fichier source.
ou plus simplement :
— Avec la souris, glisser/déplacer le fichier entre les deux projets 5 en maintenant
la touche Ctrl enfoncée (un + apparaît).
On obtient le résultat figure 6.2.
Attention : il faut bien comprendre que
le fichier ainsi partagé reste dans le répertoire du premier projet
comme le confirme l’affichage de ses propriétés (à droite figure 6.2). Dans notre exemple,
chaque projet a son propre fichier principal, main.cpp ou main2.cpp, mais il y a un
seul hop.cpp. Modifier le contenu de ce fichier aura des conséquences sur les deux
projets à la fois. C’est ce que nous voulions !

6.1.4

Fichiers d’en-têtes

Le fichier séparé nous permet de factoriser une partie du source. Toutefois, il faut
taper les déclarations de toutes les fonctions utilisées dans chaque fichier principal les
utilisant. Nous pouvons mieux faire 6 . Pour cela, il est temps d’expliquer ce que fait
l’instruction #include que nous rencontrons depuis nos débuts :
La ligne #include "nom" est automatiquement remplacée par le contenu
du fichier nom avant de procéder à la compilation.
Il s’agit bien de remplacer par le texte complet du fichier nom comme avec un simple
copier/coller. Cette opération est faite avant la compilation par un programme dont
nous n’avions pas parlé : le pré-processeur. La plupart des lignes commençant par un #
lui seront destinées. Nous en verrons d’autres. Attention : jusqu’ici nous utilisions une
5. On peut aussi glisser/déplacer le fichier depuis l’explorateur Windows
6. Toujours le moindre effort...

76

/Project1" comme répertoire. // Utilisation i n t a=g ( ) . On peut aussi préciser au compilateur une liste de répertoires où il peut aller chercher les fichiers d’en-tête.6.. mais en choisissant "Visual C++/Code/Header file" au lieu de "Visual C++/Code/C++ file". qui se trouve dans le répertoire du premier projet 9 ) : # i n c l u d e " .. i n t main ( ) { . etc. — Fichier hop. il nous suffit de mettre les déclarations se rapportant au fichier séparé dans un troisième fichier et de l’inclure dans les fichiers principaux. int g ( ) . / P r o j e c t 1 /hop .cpp du deuxième projet (il faut préciser l’emplacement complet de l’en-tête.h : // Déclarations void f ( i n t x ) . faire comme pour le source. Il est d’usage de prendre pour ce fichier supplémentaire le même nom que le fichier séparé. i n t main ( ) { .cpp du premier projet : # i n c l u d e " hop . — Fichier main. Les fichiers d’en-tête iostream. mais avec l’extension ..h : on appelle ce fichier un fichier d’en-tête 8 . h " .h (voir après) 8. Voila ce que cela donne : — Fichier hop. f (a ). 77 .. Utiliser pour cela les propriétés du projet.. } / / Autres f o n c t i o n s . } int g () { . qui va chercher le fichier nom dans les répertoires des bibliothèques C++ 7 . 9.. sont parfois appelés en-têtes système. h " .. . .... — Fichier main2. Pour créer ce fichier. Après quoi. option "C/C++ / General / Additional Include Directories" en précisant ".. Fichiers séparés forme légèrement différente : #include <nom>. Grâce à cette possibilité du pré-processeur.. Leur nom ne se termine pas toujours par .... un #include "hop.cpp : // Définitions void f ( i n t x ) { .. .h" suffit.. Plusieurs fichiers ! 6. 7.1.h comme header.

ces nouveaux types doivent être connus du fichier séparé. En effet. i n t b=g ( ) .. }..h. . 2..h : / / Types s t r u c t Vecteur { double x . Cela donne par exemple : — Fichier vect. Il faut donc vraiment : 1. le fichier d’en-tête ne contient pas seulement les déclarations des fonctions mais aussi les définitions des nouveaux types (comme les structures) utilisés par le fichier séparé. // Déclarations double norme ( Vecteur V ) . } int g () { . Vecteur B ) { 78 . y .. h " .1. Plusieurs fichiers ! // Utilisation f (12). on inclut aussi l’entête dans le source.cpp : # i n c l u d e " hop . // Définitions void f ( i n t x ) { . — Fichier vect.cpp sont cohérentes avec leur déclaration dans hop.. Vecteur B ) . pour être sur que les fonctions définies dans hop. Fichiers séparés 6. Vecteur plus ( Vecteur A. mais aussi du fichier principal...cpp : # include " vect . En fait. Inclure l’en-tête dans le fichier principal mais aussi dans le fichier séparé.. } / / Autres f o n c t i o n s . ce qui donne : — Fichier hop. h" / / Fonctions e t t y p e s // Définitions double norme ( Vecteur V) { . Mettre dans l’en-tête les déclarations des fonctions et les définitions des nouveaux types. et bien que ça soit pas obligatoire... } Vecteur plus ( Vecteur A.6... En pratique.

11. Les fonctions secondaires n’ont pas à apparaître 10 . — Fichier main. i n t main ( ) { .1.6) 79 . On devrait même tout faire pour bien les cacher et pour interdire au fichier principal de les utiliser. s’il est possible de déclarer autant de fois que nécessaire une fonction.. ne pas déclarer dans l’en-tête toutes les fonctions du fichier séparé mais seulement celles qui seront utilisées par le fichier principal.. Donc pas de #include "vect. Il serait possible de le faire dès maintenant... mais nous en reparlerons plutôt quand nous aborderons les objets..1.1.6. 2....2. 6.cf section 3. la philosophie de ce système est que 10.6 Implémentation Finalement. // Utilisation Vecteur C=plus (A... Une même fonction peut alors se retrouver définie plusieurs fois : dans le fichier séparé et dans le fichier principal qui l’inclut..5 A ne pas faire.cpp" ! 6... Or.cpp du premier : # include " vect . B ) . Plusieurs fichiers ! 6. double n=norme (C ) . h" . Il est "fortement" conseillé de : 1. Fichiers séparés . ne jamais inclure un fichier séparé lui-même ! C’est généralement une "grosse bêtise" 11 . il est interdit de la définir plusieurs fois (ne pas confondre avec la surcharge qui rend possible l’existence de fonctions différentes sous le même nom .. } / / Autres f o n c t i o n s . .

il peut être tenté de tirer profit de ce qui s’y trouve et d’utiliser plus que ce que l’en-tête déclare..h et #endif à la fin. se contente de rajouter ces fichiers à son projet. Considérons l’exemple suivant qui définit un vecteur 13 2D et en implémente l’addition : 12.2. Pour éviter cela. Les pré-processeurs savent heureusement détecter ce cas de figure.h et si B. Certains compilateurs peuvent ne pas connaître #pragma once. Or. mais vous êtes plus forts en programmation que les "vieux". à notre niveau actuel. — Placer #ifndef VECT_H et #define VECT_H au début du fichier vect. — Celui qui les utilise. Nous pourrons alors faire en sorte que l’utilisateur ne triche pas. etc. Plusieurs fichiers ! — Le fichier séparé et son en-tête forment un tout cohérent. — Le fichier d’en-tête doit être suffisamment clair et informatif pour que l’utilisateur n’ait pas à regarder le fichier séparé lui-même a . 13. Or. le débutant découvre souvent des problèmes non prévus lors du cours. quand les opérandes sont de nouveaux types. Coin des collégiens : vous ne savez pas ce qu’est un vecteur. le créateur du fichier séparé et de l’en-tête peut par la suite être amené à changer dans son source la façon dont il a programmé les fonctions sans pour autant changer leurs fonctionnalités. a. Il arrive que les fichiers d’en-tête aient besoin d’en inclure d’autres eux-mêmes. Nous laissons au lecteur le soin de découvrir seul quels sont les opérateurs qu’il est possible de définir. A elle de ne pas tricher ! 6.6.h inclut B.7 Inclusions mutuelles En passant à l’action. qui n’est pas nécessairement celui qui les a programmées. L’utilisateur qui a "triché" en allant regarder dans le fichier séparé peut alors voir ses programmes ne plus marcher. Alors regardez les sources qui suivent et vous saurez ce qu’est un vecteur 2D ! 80 .2 Opérateurs Le C++ permet de définir les opérateurs +. Il n’a pas respecté la règle du jeu qui était de n’utiliser que les fonctions de l’en-tête sans savoir comment elles sont implémentées.h. On utilise alors une astuce que nous donnons sans explication : — Choisir un nom unique propre au fichier d’en-tête. si l’utilisateur le regarde. si le fichier A.h se solde par une phénomène d’inclusions sans fin qui provoque une erreur 12 . d’inclure l’en-tête dans ses sources et de profiter de ce que l’en-tête déclare. De toute façon.1. 6. on utilise une instruction du pré-processeur signalant qu’un fichier déjà inclus ne doit plus l’être à nouveau : on ajoute #pragma once au début de chaque fichier d’en-tête.. D’ailleurs. Il est même en général imbattable pour cela ! Le problème le plus fréquent qui survient avec les fichiers d’en-tête est celui de l’inclusion mutuelle. Opérateurs 6. Par exemple VECT_H pour le fichier vect. le créateur et l’utilisateur sont une seule et même personne. -.h inclut A. implémentant un certain nombre de fonctionnalités.. Voici très succinctement comment faire.h ou de B.. Nous reparlerons de tout ça avec les objets.h alors toute inclusion de A.

Plusieurs fichiers ! 6. v e c t c=a+b . le point n’étant pas définissable car réservé à l’accès aux champs de la structure 15.2.m.6. v e c t plus ( v e c t m. } Voici comment définir le + entre deux vect et ainsi remplacer la fonction plus() : s t r u c t vect { double x .2} . 4 } . v e c t n ) { v e c t p={m. x . On peut en fait définir ce qui existe déjà sur les types de base.2} . y } . y } . v e c t n ) { v e c t p={m. x∗n . v e c t c=plus ( a . } i n t main ( ) { vect a ={1 . return 0. v e c t c =2∗a . }. s ∗m. y } . x+n . v e c t n ) { r e t u r n m.2} . y . } / / Produit s c a l a i r e double o p e r a t o r ∗ ( v e c t m. 4 } . v e c t o p e r a t o r +( v e c t m.2. x+n . x . Opérateurs s t r u c t vect { double x .6). 4 } . Attention. } Remarquez que les deux fonctions ainsi définies sont différentes bien que de même nom (operator∗) car elles prennent des paramètres différents (cf surcharge section 3. v e c t m) { v e c t p={ s ∗m. return 0. return p . y+n . 81 . etc 15 . y+n . b = { 3 . return p . }. Dans ce cas. return 0. y . } Nous pouvons aussi définir un produit par un scalaire. un produit scalaire 14 . } i n t main ( ) { vect a ={1 . il est impossible de redéfinir les opérations des types de base ! Pas question de donner un sens différent à 1+1. y . 14. return p . b = { 3 . y∗n . b = { 3 . } i n t main ( ) { vect a ={1 .b. x . double s=a ∗b . x+m.m. b ) . / / P r o d u i t p a r un s c a l a i r e v e c t o p e r a t o r ∗ ( double s . on utilise a*b et non a.

double x=12.3. if (t) j=1.. Fiche de référence (1/3) Variables — Définition : int i.3).l.. while(i<=100) { . complex<double> z(2.j=j-3) . Plusieurs fichiers ! Récréation : TP suite et fin Le programme du TP précédent étant un exemple parfait de besoin de fichiers séparés (structures bien identifiées. // OK! if (j>1) { int k=3. — if (i==0) j=1.3. bool t=true.... float y=1.5 de convertir (et terminer ?) notre programme de simulation de gravitation et de duel dans l’espace ! 6.4 Fiche de référence La fiche habituelle. . — Portée : int i.. nous vous proposons. char c=’A’. — Initialisation : int n=5. } 82 — bool t=(i==0). k=l=3.. j=i. j=k.. else j=2. int k.3 6. . break. } — for(int i=1.. // OK . unsigned char d=25.. partagées par deux projets).j=10. const int m=12. float x=float(i)/j... default: . — switch (i) { case 1: . — for(int i=1.j>i. break...... interdit! int j=2. — Affectation : i=2. string s="hop". unsigned int j=4.2f. interdit! — Types : int i=3..o=n.j. Récréation : TP suite et fin 6.i++) . — if (i==0) { j=1..6. } Boucles — do { . // i=j. i=i+2. — Conversion : int i=int(x). // OK! } //i=k.. k=2. i=i+1.. i=j. Tests — Comparaison : == != < > <= >= — Négation : ! — Combinaisons : && || — if (i==0) j=1..i<=10. // OK int i=m.m. — Constantes : const int s=12.. dans le TP A.. } while(!ok). case 2: case 3: . signed char d=-128. — Variables globales : int n. void f() { n=10. — int i=1.

— #include <iostream> — Surcharge : a.. int c=a+b.3. } char c=s[0].4}. int t[4]={1. ..j=g(). void affiche(int a) { — Ne déclarer dans le ..4.c=Red.i<5.2. t[i]=0. Color c. — double x[5]. double cos(double x). // Modulo — Affectation : void afficher(int x..Blue}. — #include <ctime> for(int i=0. — void init(int t[4]) { DrawPoint(x.x=2.i++) } ."c"}. a.h". t[i]=0.y=3. — i++. } — #include <cmath> int n) { . for(int i=0. double hasard(). Fiche de référence Fiche de référence (2/3) Fonctions — Opérateurs : — #include "vect.t[3].... return 0.j[2*n]. y vect operator+( compris dans vect. string s="hop". } — void init(int t[]. void swap(int& a.size(). int hasard(int a. } — Types : définitions dans le . } — j=i%n. }.4.6. cout <<"I="<<i<<endl.. int g() { . le . Compilation séparée cin >> i >> j.. int x=3. — struct Point { a=b.. int f(int a) { . — Retour : y[i]=2*x[i]. i-=2. — #include <cstdlib> int y) { . using namespace std. return -1. for(int i=0. srand((unsigned int) — Appel : } time(0)). if (x>0) i--.i<n. } — Définition : — #pragma once au début du — Déclaration : fichier. 83 .RED)..i<3... — Initialisation : return 1.y.5.. . Point a. Plusieurs fichiers ! 6. double sqrt(double x).h vect C=A+B.i++) — Ne pas trop découper...int b) { . Tableaux fonctions utiles.b=tmp..y. Entrées/Sorties swap(x. return. .h que les cout << a << endl.h.i++) if (x<0 || y<0) i=rand()%n. return..i++) int i=f(2). x=rand()/ if (x>=w || y>=h) — En paramètre : double(RAND_MAX). int plus(int a.cpp .cpp — Définition : vect A. — Références : } double acos(double x)..y=2. j+=3..2. . for (int i=0. int s[3]={1.3.vect B) { — Fonctions : déclarations dans int plus(int a. double x. int signe(double x) { Divers if (x<0) — const int n=5. int l=s. définitions dans le } return c. using namespace std. int i[n]...y).2. int hasard(int n). double sin(double x).i<4. Point b={1.3}. int& b){ Structures — #include <string> int tmp=a. a. int b).y[5].int b). t[i]=s[i]. string s[2]={"ab".

Tableaux : pas pour transcrire une formule mathématique ! Faire des structures.i<=10. dans une fonction ! return t.t[3]. Utiliser le debuggeur. // NON! Erreurs fréquentes — Debug : F5 — Step over : F10 — Step inside : F11 — Indent : Ctrl+K. // NON! — x=i/j. // NON! — int q=r=4. // NON! — — Point a. // NON! — } // NON! — double x=1/3. Fiche de référence 6. // NON! x=double(i/j). — Pas de définition de fonction . int t[n]. — if (i=2) // NON! if i==2 // NON! — int s[3]={1. a={1. Imagine++ for (int i=1.cpp) .4. t=f().2. int i=f.. Faire des fichiers séparés. Plusieurs fichiers ! Fiche de référence (3/3) — int f()[4] { // NON! int t[4]...i<100.cpp"//NON — — double x[10].. Le . Faire des fonctions. Indenter.2}.y[10].} — double x. // NON! } int t[4].6.h doit suffire à l’utilisateur (qui ne doit pas regarder le . — t={1. // NON — Build : F7 84 Nettoyer en quittant. int i. //NON — Clavier — int n=5..2}. Ne pas laisser de warning. Erreurs et warnings : cliquer.j. // NON! // NON! — — struct Point { — int f() {..3}. if (i==2) then // NON! t=s. //NON! — #include "vec.y. y[i]=2*x[i].i++) — — Voir documentation.Ctrl+F Conseils — — for (int i=0.i++) — int t[2].

La mémoire Chapitre 7 La mémoire Il est grand temps de revenir sur la mémoire et son utilisation.. r=a−q∗b . Nous pourrons alors mieux comprendre les variables locales. c ’ e s t b i z a r r e ! " << endl .b. Après cela. return q .1 L’appel d’une fonction Il s’agit là d’une nouvelle occasion pour vous de comprendre enfin ce qui se passe dans un programme. 7. les fonctions récursives. etc. } i n t d i v i s e ( i n t a . r ). denom .. void v e r i f i e ( i n t p . comment marche exactement l’appel d’une fonction. i n t q .q. q=a/b . i n t quo . nous pourrons enfin utiliser des tableaux de taille variable (sans pour autant rentrer vraiment dans la notion délicate de pointeur).1.1 Exemple Considérons le programme suivant : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # i n c l u d e <iostream > using namespace s t d . verifie (a . i n t& r ) { int q . . i n t b . } i n t main ( ) { i n t num. i n t r e s ) { i f ( re s <0 || re s >=q || q∗quo+ r e s ! = p ) cout << " Tiens .7. do { cout << " E n t r e z deux e n t i e r s p o s i t i f s : " . — 7.

return 0. denom . nous allons utiliser le debuggeur. Calculant le quotient et le reste d’une division entière. cela donne : Ligne 18 21 23 24a 9 10 11 12 13a 4 5 7 13b 14 15 24b 25 28 num ? 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 denom ? 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 quotient reste ? ? ? ? ? ? ? ? ? ? ? ? ? 7 7 ? ? ? ? ? 2 2 2 2 2 2 2 2 2 2 a b r qd 23 23 23 23 23 23 23 23 23 23 3 3 3 3 3 3 3 3 3 3 [reste] [reste] [reste] [reste] [reste] [reste] [reste] [reste] [reste] [reste] ? 7 7 7 7 7 7 7 7 retd pv qv quo res 23 23 3 3 7 7 2 2 7 7 7 A posteriori. i n t quotient . Pour mieux comprendre cette pile.2. par exemple 24a et 24b 2. Avant cela. et en indiçant avec des lettres les différentes étapes d’une même ligne 1 . La mémoire c i n >> num >> denom . 86 . En ne mettant que les lignes où les variables changent. Les fonctions s’appelant les unes les autres. déjà rencontré au TP A. L’appel d’une fonction 21 22 23 24 25 26 27 28 } 7. on constate qu’on a implicitement supposé que lorsque le programme est en train d’exécuter divise () . cout << num << " / " << denom << " = " << q u o t i e n t << " ( I l r e s t e " << r e s t e << " ) " << endl . la fonction main() et ses variables existent encore et qu’elles attendent simplement la fin de divise () . Plus précisément. Une bonne façon d’expliquer exhaustivement son déroulement est de remplir le tableau suivant. on se retrouve avec des appels de fonctions imbriqués les uns dans les autres : main() appelle divise () qui lui-même appelle verifie () 2 . cette imbrication est un empilement et on parle de pile des appels. précisons ce qu’un informaticien entend par pile. } while (num<=0 || denom< = 0 ) . q u o t i e n t = d i v i s e (num. Et d’ailleurs main() a lui-même été appelé par une fonction a laquelle il renvoie un int. 1.1. et vérifiant qu’ils sont corrects. en supposant que l’utilisateur rentre 23 et 3 au clavier.7. Autrement dit : Un appel de fonction est un mécanisme qui permet de partir exécuter momentanément cette fonction puis de retrouver la suite des instructions et les variables qu’on avait provisoirement quittées. r e s t e ) . ce sont juste les lignes 11 et 12 qui font tout !). il n’est pas passionnant et surtout inutilement long (en fait. r e s t e . Il s’agit par contre d’un bon exemple pour illustrer notre propos.

et la ligne 24 de main() est descendue d’un cran dans la pile des appels. qui vaut 3 et non pas celle de divise () . c’est la même chose mais le premier arrivé est le premier sorti (FIFO). (f) Nous exécutons maintenant la suite jusqu’à nous retrouver en ligne 24 au retour de divise () . après un push(1). Simplement. Entre autres. après un push(1). un push(2) et un push(3). on peut faire du pas-à-pas détaillé. nous sommes ligne 24 de la fonction main(). Vérifiez au passage que la variable q affichée est bien celle de verifie () . 87 . en cliquant sur la ligne de divise () dans la fenêtre de la ligne d’appel.1. nous voyons apparaître la ligne 13 et ses variables dans leur état alors que le programme est en 5. Plus profond dans la pile. Nous sommes dans la fonction divise () . notamment pour afficher les instructions et les variables de ce niveau. Ici.1. Dernier rentré. divise () est en attente à la ligne 13 et main() toujours en 24.7. La pile des appels à un niveau de plus. En anglais. L’appel d’une fonction Pile/File — Une pile est une structure permettant de mémoriser des données dans laquelle celles-ci s’empilent de telle sorte que celui qui est rangé en dernier dans la pile en est extrait en premier. le deuxième pop() donnera 2 et un dernier pop() donnera 1. l’exécution du programme n’a pas progressé et nous en sommes toujours à la ligne 5. ou simplement 3. reste est bien passé à 2 depuis la ligne 12 de divise () . Par exemple. paramètres et valeurs de retour dont nous pouvons constater la cohérence avec le tableau précédent. une pile (stack) est aussi appelée LIFO (last in first out 3 ). premier sorti. (b) Avançons en pas-à-pas détaillé (touche F11) jusqu’à la ligne 12.dll 4 . — Pour une file (en anglais queue). 4. nous voyons que main() a été appelé par une fonction mainCRTStartup() elle-même appelée par un étrange kernel32. Ces deux fonctions sont respectivement : (i) la fonction que le compilateur crée pour faire un certain nombre de choses avant et après main() et (ii) la partie de Windows qui lance le programme lui-même. le deuxième pop() donnera 2 et un dernier pop() donnera 3. le premier pop() donnera 3. Par exemple. un push(2) et un push(3).1 obtenue en lançant notre programme d’exemple sous debuggeur. voici l’état du main() et de ses variables (entre autres. (d) Ici. (a) Comme l’indique la pile des appels. le premier pop() donnera 1. La partie droite affiche le contenu des variables. En regardant la partie gauche de chaque étape. qui se trouve en haut de la pile. Visual offre la possibilité en double-cliquant sur la pile d’appel de regarder ce qui se passe à un des niveaux inférieurs. 7. q vient de valoir 7. Vérifiez aussi les variables et le fait qu’elles valent n’importe quoi (ici -858993460 !) tant qu’elles ne sont pas initialisées ou affectées. La mémoire 7. le q affiché est celui de divise () et vaut 7. Pour cela. nous pouvons voir la pile des appels. (e) Toujours sans avancer. On y empile (push) et on y dépile (pop) les données. (c) Nous sommes maintenant à la ligne 5 dans verifie () .2 Pile des appels et débuggeur Observons donc la figure 7.

1 – Appels de fontions 88 . La mémoire (a) (b) (c) (d) (e) (f) (g) F IGURE 7.1. L’appel d’une fonction 7.7.

.. La mémoire pile top→ 7.7... .2 – Pile et variables locales. . non encore affectée à quotient. pris par les les fonctions . Notez aussi possibilité de continuer le programme jusqu’à une certaine ligne sans avoir besoin de mettre un point d’arrêt temporaire sur cette ligne mais simplement en cliquant sur la ligne avec le bouton de droite et en choisissant "exécuter jusqu’à cette ligne" ( ) 89 ..2 Variables Locales Il va être important pour la suite de savoir comment les paramètres et les variables locales sont stockés en mémoire... 7. .. deux fois de suite un pas-à-pas sortant 5 (Maj-F11) pour relancer jusqu’à sortir de verifie () . (g) Un pas-à-pas de plus et nous sommes en 25/26. place libre top→ denom num quotient reste pris par les les fonctions avant main() 3 23 7 2 .. F IGURE 7. les fonctions avant main() . 5.. avant main() pile variable valeur variable pile valeur valeur 23 3 7 2 23 3 7 [reste] 3 23 ? 2 ... qui est encore non défini. et aussi la valeur de retour de divise () . On voit bien quotient... La variable quotient vaut enfin 7... puis jusqu’à sortir de divise () . étape (c) (ligne 5) et étape (g) (ligne 25/26). Step Out ou Maj-F11 ou . De gauche à droite : étape (b) (ligne 12). .. Variables Locales variable place libre top→ pv place libre qv quo res a 23 a b 3 b qd 7 qd r [reste] r denom 3 denom num 23 num quotient ? quotient reste ? reste pris par les .2..

3 Fonctions récursives Un fonction récursive est une fonction qui s’appelle elle-même.2 La pile Les variables locales (et donc les paramètres) ne sont pas mémorisées à des adresses fixes en mémoire 6 .2. Voici une façon simple et récursive de la programmer : 5 int fact1 ( int n) 6 { 7 i f ( n==1) 8 return 1.1 7.2 montre trois étapes de la pile pendant l’exécution de notre exemple. La fonction la plus classique pour illustrer la récursivité est la factorielle 8 . La solution retenue est beaucoup plus économe en mémoire 7 : Les variables locales sont mémorisées dans un pile : — Quand une variables locale est créée. La mémoire Paramètres Pour les paramètres. 7. Si on faisait ça.. c’est simple : Les paramètres sont en fait des variables locales ! Leur seule spécificité est d’être initialisés dès le début de la fonction avec les valeurs passées à l’appel de la fonction. 8. les variables locales s’empilent : la mémoire est utilisée juste pendant le temps nécessaire. 7.7. Fonctions récursives 7. La figure 7. 9. et en tout cas avant l’appel récursif !) une condition d’arrêt : ici si n vaut 1. × n. — Quand elle meurt (en général quand on quitte sa fonction) elle est sortie de la pile.2. 10 } On remarque évidemment que les fonctions récursives contiennent (en général au début.3. elle est rajoutée en haut de cette pile. Ainsi.. cf section suivante ! Coin des collégiens : La factorielle d’un nombre entier n s’écrit n! et vaut n! = 1 × 2 × . Et permettra de faire des fonctions récursives. 9 r e t u r n n∗ f a c t 1 ( n − 1 ) . décidées à la compilation. 7. la fonction retourne directement 1 sans s’appeler elle-même 9 . 6. Le fait de pouvoir mettre des return au milieu des fonctions est ici bien commode ! 90 . les adresses mémoire en question devraient être réservées pendant toute l’exécution du programme : on ne pourrait y ranger les variables locales d’autres fonctions. au fur et à mesure des appels. Souvenons-nous du chapitre 2.

La mémoire 7. Il faut aussi savoir que la pile des appels n’est pas infinie et même relativement limitée. la pile ressemble à : pile variable valeur place libre top→ nf act1(1) 1 nf act1(2) 2 nf act1(3) 3 ce que l’on peut aisément vérifier avec le débuggeur. Finalement : Les fonctions récursives ne sont pas différentes des autres.2 Efficacité Une fonction récursive est simple et élégante à écrire quand le problème s’y prête 10 . Fonctions récursives Pourquoi ça marche ? Si les fonctions avaient mémorisé leurs variables locales à des adresses fixes. pour chaque numéro de ligne. arrivés en ligne 8 de fact1 (1) pour un appel initial à fact1 (3). 7. fact1 (3) aurait écrasé la valeur 3 mémorisée dans n par un 2 en appelant fact1 (2) ! C’est justement grâce à la pile que le n de fact1 (2) n’est pas le même que celui de fact1 (3). Ainsi. Si on visualise la pile. la récursivité n’aurait pas pu marcher : l’appel récursif aurait écrasé les valeurs des variables. Ainsi. le programme suivant 22 / / F a i t d é b o r d e r l a p i l e 23 i n t f a c t 3 ( i n t n ) 24 { 10.3. on comprend mieux pourquoi ça marche. Nous venons de voir qu’elle n’est toujours pas facile à suivre ou à debugger.7. C’est une erreur classique de débutant que de vouloir abuser du récursif. quel appel de fonction est concerné. l’appel à fact1 (3) donne-t’il le tableau suivant : Ligne 5f act1(3) 9af act1(3) 5f act1(2) 9af act1(2) 5f act1(1) 8f act1(1) 10f act1(1) 9bf act1(2) 10f act1(2) 9bf act1(3) 10f act1(3) nf act1(3) 3 3 3 3 3 3 3 3 3 3 retf act1(3) nf act1(2) 2 2 2 2 2 2 6 6 retf act1(2) nf act1(1) retf act1(1) 1 1 2 2 2 1 1 1 Ce tableau devient difficile à écrire maintenant qu’on sait que les variables locales ne dépendent pas que de la fonction mais changent à chaque appel ! On est aussi obligé de préciser.3. 91 . C’est le système d’appel des fonctions en général qui rend la récursivité possible. Par exemple. Ainsi.3.1 7.

} ce qui après tout n’est pas si terrible. etc.. on essaie donc s’il est nécessaire d’écrire une version dérécursivée (ou itérative) de la fonction. elle s’appelle déjà 300. Sous Visual. 1. Bref. Fonctions récursives 25 26 27 28 } 7. Mais la vraie raison qui fait qu’on évite parfois le récursif est qu’ appeler une fonction est un mécanisme coûteux ! Lorsque le corps d’une fonction est suffisamment petit pour que le fait d’appeler cette fonction ne soit pas négligeable devant le temps passé à exécuter la fonction elle-même. 36 r e t u r n f i b 1 ( n−2)+ f i b 1 ( n − 1 ) . 92 . cela donne : / / Version i t é r a t i v e int fact2 ( int n) { i n t f =1. il est préférable d’éviter ce mécanisme d’appel 12 . ce qui prend un certain temps ! Il est donc raisonnable d’en programmer une version dérécursivée : 39 / / D é r é c u r s i v é e 40 i n t f i b 2 ( i n t n ) { 11. il arrive qu’écrire une fonction sous forme récursive ne soit pas utilisable pour des raisons de complexité. r e t u r n n∗ f a c t 3 ( n + 1 ) . mais n = 9 appelle lui aussi n = 8 de son côté en plus de n = 7. 5. Ainsi..000 de fois ellemême. 2. Enfin. Pour notre factorielle. Une exemple classique est la suite de Fibonacci définie par :  f0 = f1 = 1 fn = fn−1 + fn−2 et qui donne : 1. f o r ( i n t i = 2 . 37 } cette fonction a la mauvaise idée de s’appeler très souvent : n = 10 appelle n = 9 et n = 8. n = 7 qui lui-même est appelé par tous les n = 8 lancés. 8. Dans le cas d’une fonction récursive. La mémoire i f ( n==1) return 1.3. i ++) f ∗= i . Nous verrons dans un autre chapitre les fonctions inline qui répondent à ce problème. pour n = 40. return f . 3. il s’arrête pour n = 5000 environ. cette fonction devient rapidement très lente. En version récursive : 32 / / T r è s l e n t ! 33 i n t f i b 1 ( i n t n ) { 34 i f ( n <2) 35 return 1.000. i <=n . 12. / / e r r e u r ! dans lequel une erreur s’est glissée va s’appeler théoriquement à l’infini et en pratique s’arrêtera avec une erreur de dépassement de la pile des appels 11 ..7.

1 Limites La pile est limitée en taille. } r e t u r n fnm1 . fnm1 = 1 . 37 } Il s’exécute avec une erreur : "stack overflow". Il est donc grand temps d’apprendre à utiliser le tas ! 7.4. La variable locale t n’est pas trop grande pour l’ordinateur 13 : elle est trop grande pour tenir dans la pile. fnm2=fnm1 .4 Le tas La pile n’est pas la seule zone de mémoire utilisée par les programmes. leur version récursive fait déborder la pile d’appels ! 7. Mentionnons aussi qu’il existe des fonctions suffisamment tordues pour que leur version récursive ne se contente pas de s’appeler un grand nombre de fois en tout. Et le débutant oublie toujours la deuxième. on est aussi limité aux petits tableaux. Jusqu’à présent. 93 . 7. on savait qu’on était limité aux tableaux de taille constante. 35 int t [n ] . f o r ( i n t i = 2 .4. i <=n . La mémoire 41 42 43 44 45 46 47 48 } 7. i ++) { i n t fn=fnm2+fnm1 . il n’y a que deux choses à faire. La pile d’appel n’étant pas infinie et les variables locales n’étant pas en nombre illimité.7. il est raisonnable de réserver une pile de relativement petite taille.. ce qui a pour conséquence des programmes qui grossissent en quantité de mémoire occupée. Lorsqu’on veut utiliser un tableau de taille variable.2 Tableaux de taille variable Nous fournissons ici une règle à appliquer en aveugle. mais un grand nombre de fois en même temps. Le tas i n t fnm2 =1 . ce qui fait qu’indépendamment des questions d’efficacité. 500000x4 soit 2Mo seulement ! 14. 36 .4... Sa compréhension viendra plus tard si nécessaire. Il y a aussi le tas (heap en anglais). mais elle sont essentielles toutes les deux 14 : 13.. Essayez donc le programme : 32 i n t main ( ) 33 { 34 const i n t n=500000. fnm1=fn . En réalité.

Remplacer int t[n] par int* t=new int[n] (ou l’équivalent pour un autre type que int) 2. Lorsque le tableau doit mourir (en général en fin de fonction). } i n t somme( i n t t [ ] . cout << s << " d e v r a i t v a l o i r " << n ∗ ( n+1)/2 << endl . Pour les tableaux de grande taille. ce qui entraine en général une croissance anarchique de la mémoire utilisée (on parle de fuite de mémoire). La mémoire 1. n ) . On fait donc ainsi : 1. n ) . c i n >> n . } void f i x e ( ) { const i n t n=5000. f o r ( i n t i = 0 . Pour les tableaux de taille variable. 94 . on ne change rien. / / A l l o c a t i o n remplit ( t . Le tas 7. Voici ce que cela donne sur un petit programme : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 # i n c l u d e <iostream > using namespace s t d . i n t ∗ t =new i n t [ n ] .7.4. i <n . i ++) s+= t [ i ] . Programmer un tableau de cette façon fait qu’il est mémorisé dans le tas et non plus dans la pile. i <n . return s . i n t s=somme( t . i n t s=somme( t . i n t n ) { i n t s =0. Le non respect de la règle 2 fait que le tableau reste en mémoire jusqu’à la fin du programme. i n t n ) { f o r ( i n t i = 0 . Pour le reste. rajouter la ligne delete[] t. } void v a r i a b l e ( ) { int n. n ) . cout << "Un e n t i e r SVP : " . void r e m p l i t ( i n t t [ ] . 2. int t [n ] . i ++) t [ i ]= i + 1 . remplit ( t . n ) .

S’il comprend. Il y a plusieurs façons de traduire en langage machine un source C++. on peut basculer le compilateur en 15. on appelle une fonction d’allocation à laquelle on demande de réserver en mémoire de la place pour un certain nombre de variables. / / D e s a l l o c a t i o n : ne p a s o u b l i e r ! } i n t main ( ) { fixe ( ) . Pour utiliser le tas.3 Essai d’explication Ce qui suit n’est pas essentiel pour un débutant mais peut éventuellement répondre à ses interrogations. l’optimisation nécessite un plus grand travail mais aussi des transformations qui font que le code produit n’est plus facilement débuggable. on peut aussi rechercher à produire un exécutable le plus rapide possible : on dit que le compilateur optimise le code. Il s’agit des pointeurs dont nous reparlerons plus tard. y compris comme paramètre d’une fonction. nous utilisions toujours le compilateur en mode "Debug". on utilise le tas.5 L’optimiseur Mentionnons ici un point important qui était négligé jusqu’ici. un pointeur peut s’utiliser comme un tableau. 95 . Jusqu’ici. il ne faut pas oublier de libérer la mémoire au moment où le tableau de taille constante aurait disparu : c’est ce que fait la fonction delete [] t qui libère la mémoire pointée par t. D’où le int∗ t pour mémoriser le retour du new. En général. return 0. La mémoire 35 36 37 38 39 40 41 42 43 44 7. Ensuite. tant mieux. L’optimiseur cout << s << " d e v r a i t v a l o i r " << n ∗ ( n+1)/2 << endl . 7. bien que beaucoup plus que la taille de la pile. Le tas est une zone mémoire que le programme possède et qui peut croître s’il en fait la demande au système d’exploitation (et s’il reste de la mémoire de libre évidemment). Nous n’avons jamais rencontré de type de variable capable de mémoriser une adresse. ce qui est en général réglable mais en tout cas moins que la mémoire totale. Cette fonction retourne l’adresse de l’emplacement mémoire qu’elle a réservé. } 7.5. C’est ce que fait new int[n]. Plus exactement à ce que le système d’exploitation veut bien attribuer au maximum à chaque programme.7. delete [ ] t .4. Le résultat de la compilation peut donc être différent d’un compilateur à l’autre. On choisit donc en pratique entre un code debuggable et un code optimisé. qu’il oublie et se contente pour l’instant de la règle précédente ! Pour avoir accès à toute la mémoire de l’ordinateur 15 . Un pointeur vers de la mémoire stockant des int est de type int∗. variable ( ) . mais que nous allons utiliser en TP. sinon. Enfin. Lorsqu’un programme est au point (et seulement lorsqu’il l’est). Au moment de compiler.

C’est une fonction peu connue des débutants. un tableau nul. int n. le tri sera visualisé graphiquement et chronométré (figure 7..6 Assertions Voici une fonction très utile pour faire des programmes moins buggés ! La fonction assert () prévient quand un test est faux. La mémoire mode "Release" pour avoir un programme plus performant. 96 .7.6 est une illustration des fonctions récursive et des tableaux de taille variable. Assertions 7.6. offre la possibilité de debugger le programme.. les gains peuvent être considérables. Il consiste en la programmation de quelques façons de trier des données : tri à bulle. assert (n>0). Il est toujours utile de se prémunir contre une telle exception en vérifiant que la valeur est raisonnable. etc. Elle précise le fichier et le numéro de ligne où elle se trouve. Ceci dit. Mais dans ce cas t [0] n’existe même pas ! La seule chose qu’on peut donc faire avec un tableau nul c’est le désallouer avec delete [] t . // Allocation Si l’utilisateur entre une valeur négative ou nulle. Un programmeur expérimenté fait même en sorte que l’optimiseur puisse efficacement faire son travail. l’allocation marche. c i n >> n .3). i n t ∗ t =new i n t [ n ] . il faut respecter certaines règles : — Ne pas debugger quand on est en mode Release ( !) — Rester en mode Debug le plus longtemps possible pour bien mettre au point le programme. 7. etc. il faut le faire pour chacun des deux modes. 7.. les conséquences pourraient être fâcheuses. — Savoir que les modes Debug et Release créent des fichiers objets et exécutables dans des répertoires différents et donc que lorsqu’on nettoie les solutions. Dans certains cas. ainsi -1 serait compris comme le plus grand int possible) et le new serait probablement un échec. Afin de rendre le tout plus attrayant. et c’est bien dommage ! Par exemple : # include <cassert > . Elle ne ralentit pas les programmes car elle disparaît à la compilation en mode Release. A noter que si n==0. En particulier une valeur négative de n serait interprétée comme un grand entier (car le [] attend un entier non signé.7 TP Le TP que nous proposons en A. Quicksort.

j>i.8 Fiche de référence Fiche de référence (1/3) Variables — Définition : int i.o=n. signed char d=-128. i=j.. interdit! int j=2. Fiche de référence F IGURE 7.j... — if (i==0) j=1. — for(int i=1.. if (t) j=1. . complex<double> z(2. int k.i++) . — Pile/Tas Tests — Comparaison : == != < > <= >= — Négation : ! — Combinaisons : && || — if (i==0) j=1. — Variables globales : int n. // OK! if (j>1) { int k=3. — switch (i) { case 1: . case 2: case 3: .l.. .. // OK ... // i=j.7. float y=1.. while(i<=100) { .3. } while(!ok).3 – Deux tris en cours d’exécution : tri à bulle et Quicksort. k=l=3.3). double x=12.i<=10..8. j=i.... j=k. break.... — int i=1. — Affectation : i=2.. // OK! } //i=k.m. } 97 — bool t=(i==0). bool t=true.. — Portée : int i.. k=2.. interdit! — Types : int i=3. i=i+2. — Constantes : const int s=12. const int m=12. La mémoire 7.. void f() { n=10.. // OK int i=m. i=i+1. — Initialisation : int n=5.j=10. default: . string s="hop". } Boucles — do { .2f. float x=float(i)/j. — if (i==0) { j=1. 7. unsigned char d=25.. char c=’A’. — Conversion : int i=int(x). } — for(int i=1.j=j-3) . unsigned int j=4.. break. else j=2.

} — Taille variable : string s="hop".. int i[n].. . .4}.. int signe(double x) { string s[2]={"ab". — Définition : — Itératif/Récursif int plus(int a..i<3. j+=3. void swap(int& a.7.y. — #include <ctime> int& b){ s=double(clock()) Structures int tmp=a.. t[i]=0. — Références : delete[] t. — #include <iostream> }. // Modulo int t[4]={1. Fiche de référence 7. — #include <cstdlib> if (x<0) return -1. y[i]=2*x[i].cpp — Step over : F10 vect A. .RED).b=tmp.Blue}.i<5.h — Step out : Maj+F11 vect C=A+B. — Surcharge : cout <<"I="<<i<<endl. DrawPoint(x.. double(RAND_MAX).y=3.8.x=2. return 1. double x.i<4. return. } — #include <ctime> — En paramètre : void afficher(int x.. int l=s. swap(x."c"}..h. return c.Ctrl+F . int x=3.. int hasard(int n).h que les fonctions utiles. /CLOCKS_PER_SEC.. } — double x[5].y). int y) { — void init(int t[4]) { srand((unsigned int) if (x<0 || y<0) for(int i=0. — Appel : t[i]=0. le . . y — Debug : F5 vect operator+( compris dans vect. } i--.cpp . int plus(int a. — #include <string> int f(int a) { . — const int n=5.. définitions dans le — Step inside : F11 } — Indent : Ctrl+K.3}.4. Entrées/Sorties } Color c.size(). 98 . Point a. fichier. return. — Définition : — Ne pas trop découper.j[2*n]... char c=s[0]. — void init(int t[].3. double cos(double x).2.. — struct Point { a=b.c=Red.int b) { — #pragma once au début du Tableaux int c=a+b. int hasard(int a. — Initialisation : — Retour : — j=i%n. . — #include <cmath> if (x>=w || y>=h) } double sqrt(double x).i++) return 0.y=2. Clavier int b).i++) Divers cout << a << endl... cin >> i >> j. using namespace std. int i=f(2).5.h". t[i]=s[i]. a. . } for(int i=0... int g() { ..int b).3. x=rand()/ for (int i=0. } } using namespace std..2. — Déclaration : i-=2.. — Affectation : if (x>0) i=rand()%n. Point b={1. void affiche(int a) { for(int i=0. int s[3]={1.y[5].y. La mémoire Fiche de référence (2/3) Fonctions — Pile des appels — Ne déclarer dans le . — i++. Compilation séparée — Build : F7 — Opérateurs : — #include "vect.. a.t[3].vect B) { — Fonctions : déclarations dans . a.i<n..j=g(). double hasard(). — Types : définitions dans le .2. . int* t=new int[n].i++) double acos(double x).i++) time(0)).. int n) { double sin(double x).

Le .} } // NON! int i=f.. Compiler régulièrement.9 Ne pas laisser de warning. 7.. — a={1. int t[4]. t=f(). // NON! — Pas de définition de fonction — } dans une fonction ! — int t[4]. — — Nettoyer en quittant. y[i]=2*x[i]..i++) — Voir documentation.j. Debug/Release : nettoyer les deux.3}. Utiliser le debuggeur.i<=10. Examens sur machine Fiche de référence (3/3) Erreurs fréquentes .2}. — t={1. La mémoire 7.7. — int f() {. // NON! — Point a. Vous avez toutes les connaissances nécessaires.y.. //NON Conseils — int n=5. — return t. — int f()[4] { // NON! — Indenter.9.t[3]. y=1/x. Examens sur machine Nous vous conseillons aussi de vous confronter aux examens proposés en annexe.cpp"//NON — x=double(i/j). — — if (i=2) // NON! t=s. // NON! int i.. #include <cassert> . int t[n]. // NON! — int s[3]={1..2}. // NON! — double x=1/3. — int q=r=4.h doit suffire à l’utilisateur (qui ne doit pas regarder le . x=i/j. assert(x!=0). // NON! — for (int i=0. — for (int i=1.i<100. Faire des fichiers séparés.. Faire des fonctions. Ne pas oublier delete. Tableaux : pas pour transcrire une formule mathématique ! Faire des structures. // NON — Erreurs et warnings : cliquer.y[10]. 99 . // NON! if i==2 // NON! if (i==2) then // NON! — int t[2].2. //NON! — Imagine++ — double x[10]. // NON! — #include "vec..cpp) Ne pas abuser du récursif.i++) — // NON! — struct Point { — double x.

.

Il s’agira là de notre structure de donnée la plus complexe avant l’arrivée tant attendue . i n t C[M] [N] . — L’initialisation est possible avec des accolades (ligne 5). — 8. du moins partiellement. i ++) f o r ( i n t j = 0 . La figure ci-dessus montre le tableau B. Notez que B[0] est le tableau 1D {1. nous détaillons l’allocation dynamique 1 déjà vue en 7.6} . Tableau 2D B [2][3]={{1.N= 3 . B[0] 1 2 3 i n t A[ 2 ] [ 3 ] . Attention : [ i ][ j ] et non [ i . Leur utilisation est similaire à celle des tableaux standards : — Il faut utiliser des crochets (lignes 1 et 4 du programme ci-dessous). Allocation dynamique Chapitre 8 Allocation dynamique Nous revenons une fois de plus sur l’utilisation du tas pour gérer des tableaux de taille variable.des objets. j ++) B[1] 4 5 6 A[ i ] [ j ]= i + j .2 .3}.3} .2. c’est-à-dire l’allocation de mémoire dans le tas avec new et delete.5. A noter que B[0] et B[1] sont des tableaux 1D représentant les lignes de B.{4. Attention : accolades imbriquées.. 1 2 3 4 5 6 7 1.{4 .2 et expliquons enfin les pointeurs.6}}.8. j ].2.4.1 8.. Après avoir mentionné l’existence de tableaux bidimensionnels de taille fixe.3} et B[1] le tableau 1D {4. i < 2 . c o n s t i n t M=2 . .1 Tableaux bidimensionnels Principe Il existe en C++ des tableaux à deux dimensions. — Leurs dimensions doivent être constantes (lignes 6 et 7).et maintenant justifiée . A travers l’exemple des matrices (et des images en TP) nous mélangeons structures et allocation dynamique.1. f o r ( i n t i = 0 .6}} .5.5 . int B[2][3]={{1 . j < 3 .

double m. double E [ 2 ] [ 3 ] . Allocation dynamique Limitations Vis-à-vis des fonctions. } / / Le p a s s a g e e s t t o u j o u r s p a r r é f é r e n c e . return t .1. j ++) A[ i ] [ j ]= i + j . j < 3 . double D[ 2 ] [ 2 ] = { { 1 . f o r ( i n t i = 0 . } . i ++) t +=A[ i ] [ i ] . i n t n . set (E ) . . Tableaux bidimensionnels 8. double x ) { f o r ( i n t i = 0 . double n . i <m. i ++) f o r ( i n t j = 0 . les particularités sont les mêmes qu’en 1D : — Impossible de retourner un tableau 2D..1). i ++) A[ i ]= x . . i < 2 . double x ) { f o r ( i n t i = 0 . mais il est impossible de programmer une fonction trace () ou set () qui marche pour différentes tailles de tableaux 2D comme on l’aurait fait en 1D : 1 2 3 4 5 6 7 8 9 10 11 12 / / OK void s e t ( double A[ ] . } 102 . C’est très restrictif et explique que les tableaux 2D ne sont pas toujours utilisés. j ++) A[ i ] [ j ]= x .8. void s e t ( double A [ 2 ] [ 3 ] ) { f o r ( i n t i = 0 . 4 } } . j <n . On peut donc avoir le programme suivant : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 / / Passage de paramètre double t r a c e ( double A [ 2 ] [ 2 ] ) { double t = 0 .3.. { 3 . . i < 2 .1. mais avec une restriction supplémentaire : On est obligé de préciser les dimensions d’un tableau 2D paramètre de fonction.2 8. — Passage uniquement par variable. double t = t r a c e (D ) . i <n .. 2 } . } / / NON ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! / / double A[ ] [ ] est refusé void s e t ( double A [ ] [ ] . Impossible donc de programmer des fonctions qui peuvent travailler sur des tableaux de différentes tailles comme dans le cas 1D (cf 4. i ++) f o r ( i n t j = 0 ..

3 . on les mémorise dans des tableaux 1D en stockant par exemple les colonnes les unes après les autres pour profiter des avantages des tableaux 1D. i <m... 15 p r o d u i t ( P . 5 ) . 6 f o r ( i n t j = 0 . 13 . 8 set (F . i <m. 10 s e t (G. x . i n t n . On peut alors écrire : 1 void s e t ( double A[ ] . 3 .1 – La matrice B de l’exemple précédent stockée en 1D. on stockera une matrice A de m lignes de n colonnes dans un tableau T de taille mn en plaçant l’élément A(i.1. y [ 2 ] . 7 double F [ 2 ∗ 3 ] . i n t m. double y [ ] ) 3 { 4 f o r ( i n t i = 0 . 5 } 6 . 12 double P [ 2 ∗ 3 ] ... 8 } 9 } 10 11 . i n t n ) { 2 f o r ( i n t i = 0 . y ) . 8. 2 . 8.3 Solution En pratique. Tableaux bidimensionnels B[0]=1 B[1]=4 B[2]=2 B[3]=5 B[4]=3 B[5]=6 F IGURE 8. x=. i n t m. j <n .. x [ 3 ] . i ++) 3 f o r ( i n t j = 0 .. 9 double G[ 3 ∗ 5 ] .. ou par exemple.1. 2 .1 montre le tableau B de l’exemple précédent stocké comme tableau 1D.. Ainsi. / / y=Px 103 . ce produit matrice vecteur dans lequel les vecteurs et les matrices sont stockés dans des tableaux 1D : 1 / / y=Ax 2 void p r o d u i t ( double A[ ] . 3 ) . j) en T (i + mj).8. double x [ ] . j ++) 7 y [ i ]+=A[ i +m∗ j ] ∗ x [ j ] . j <n . Allocation dynamique 8. La Fig. i ++) { 5 y[ i ]=0... dès que l’on doit manipuler des tableaux de dimension 2 (ou plus !) de différentes tailles. 14 // P=. j ++) 4 A[ i +m∗ j ]= i + j .

D’où le int∗ t=new int[n] 4....2 8. 3. double ∗ y=new double [m] .. delete [] t libère dans le tas l’adresse mémorisée dans t.2. Ce qui fait qu’en fin de compte : — une fonction f ( int s []) est conçue pour qu’on lui passe une adresse s — elle marche évidemment avec les tableaux alloués dynamiquement qui ne sont finalement que des adresses — c’est plutôt l’appel f ( t ). double ∗ A=new double [m∗n ] . new int[n] alloue dans le tas une zone mémoire pouvant stocker n int et renvoie l’adresse de cette zone. 8. qui s’adapte en passant à f l’adresse où se trouve le tableau. la syntaxe t [ i ] désigne bien ce qu’on veut. . nous pouvons utiliser des tableaux dynamiques exactement comme des tableaux de taille fixe. la syntaxe int s [] signifie en réalité que s est l’adresse du tableau.. 5.8. int t [n] définit une variable locale. une place est évidemment le nombre d’octets nécessaires au stockage d’un int. . Lorsque t est un pointeur d’int. n . après un int t [n] comme après un int∗ t=new int[n]. t [ i ] désigne la variable int stockée i places 2 plus loin en mémoire que celle située à l’adresse t. Allocation dynamique 8. la syntaxe t tout court désigne l’adresse (dans la pile) à laquelle le tableau est mémorisé. double ∗ x=new double [ n ] . / / A= . Allocation dynamique Allocation dynamique Il n’y a pas d’allocation dynamique possible pour les tableaux 2D. Lorsque t est un tableau de taille fixe t [ i ] désigne son i me élément. L’exemple suivant montre comment faire. De plus. d e l e t e [ ] A. avec t tableau de taille fixe. x . y ) . / / y=Ax . n .m. Il faut donc vraiment les mémoriser dans des tableaux 1D comme expliqué ci-dessus pour pouvoir les allouer dynamiquement dans le tas. delete [ ] x . 2. 2. Il utilise la fonction produit() donnée ci-dessus sans qu’il soit besoin de la redéfinir : 1 2 3 4 5 6 7 8 9 10 11 12 i n t m.. lorsqu’une fonction prend un tableau comme paramètre. capable de stocker n variables int. c’est-à-dire que t peut mémoriser l’adresse d’une zone mémoire contenant des int. 6. x = .2. p r o d u i t (A. delete [ ] y . donc de la mémoire dans la pile. Lorsque t est un tableau de taille fixe. int∗ t définit une variable de type "pointeur" d’int. 104 . Il suffit de comprendre les étapes suivantes : 1. . Ainsi. Ici.1 Pourquoi ça marche ? Il est maintenant temps d’expliquer pourquoi. . . . une fois alloués.

en comprenant.. i ++) t[ i ]=.. / / C a t a : Non s e u l e m e n t on ne l i b è r e p a s l a mémoire 105 .. i <n . f o r ( i n t i = 0 .. Oublier de désallouer : void f ( i n t n ) { i n t ∗ t =new i n t [ n ] .2 Erreurs classiques Vous comprenez maintenant aussi les erreurs classiques suivantes (que vous n’éviterez pas pour autant !).8. Vous pouvez donc maintenant programmer. Allocation dynamique — logiquement. 6 } 7 . i n t n ) { / / S y n t a x e " p o i n t e u r " 2 double s = 0 . 12 i n t ∗ t 2 =new i n t [ n ] . 15 .2. Allocation dynamique 8. . / / A i e ! Du coup . ce genre de choses : 1 double somme( double ∗ t . 1. 13 . 4 ) .. 11 . Ne pas désallouer ce qu’il faut : i n t ∗ t =new i n t [ n ] . 5 return s . Les deux sont en fait possibles et synonymes. d e l e t e [ ] t .. .. i <n . n ) .. i ++) 4 s+= t [ i ] ... / / OK d e l e t e [ ] s ... 16 delete [ ] t2 . s= t .. Oublier d’allouer : int ∗t .. 14 double s2=somme( t2 . s c o n t i e n t l a même a d r e s s e que t / / (On n ’ a p a s r e c o p i é l a z o n e p o i n t é e p a r t d a n s c e l l e // p o i n t é e par s ! ) . } / / On o u b l i e d e l e t e [ ] t . 9 . 8 int t1 [ 4 ] . / / Horreur : t vaut n ’ importe / / q u o i comme a d r e s s e 2. 8.. / / Chaque a p p e l à f ( ) va p e r d r e n i n t d a n s l e t a s ! 3. 10 double s1=somme( t1 . i n t ∗ s=new i n t [ n ] ...2. 3 f o r ( i n t i = 0 .. on devrait même déclarer f par f ( int∗ s) au lieu de f ( int s []) .

8. / / Une t e l l e l i g n e ne c h a n g e r a i t p a s ’ s ’ 5 / / dans l a f o n c t i o n a p p e l a n t e 6 } 7 .. vous imaginez bien qu’on n’attend pas toujours la fin de l’existence du tableau pour libérer la mémoire.2. 5 int ∗ t=alloue ( 1 0 ) . En fait. le tableau dont l’adresse est mémorisée dans s est alloué ligne 3 et libéré ligne 5. t a t t e n d c e t t e l i g n e pour mourir . 3 t [ i ] = .3 Conséquences Quand libérer ? Maintenant que vous avez compris new et delete. m a i s en p l u s on / / d é s a l l o u e à nouveau c e l l e q u i v i e n t d ’ ê t r e l i b é r é e ! 8.. . La variable s qui mémorise son adresse. — Un pointeur passé en paramètre à une fonction l’est par valeur. 3 } 4 . . . 6 . lorsque des fonctions manipulent des variables de type pointeur..2. 6 7 .. 106 .. Le plus tôt est le mieux et on libère la mémoire dès que le tableau n’est plus utilisé : 1 void f ( ) { 2 int t [10]. 8 } / / Par c o n t r e int [n ] . Il suffit de respecter la logique : — Une fonction qui retourne un pointeur se déclare int∗ f ().... . aux oublis !).m) . / / s i s ne s e r t p l u s d a n s l a s u i t e .. Ne pas mélanger avec le fait qu’un tableau est passé par référence ! Considérez le programme suivant : 1 void f ( i n t ∗ t . / / On m o d i f i e t [ i ] m a i s p a s t ! 4 t = . Allocation dynamique 8.. est créée ligne 3 et meurt ligne 8 ! Pointeurs et fonctions Il est fréquent que le new et le delete ne se fassent pas dans la même fonction (attention. . . 8 i n t ∗ s=new i n t [m] . .. . du coup.. 5 delete [ ] s . Allocation dynamique / / i n i t i a l e m e n t m é m o r i s é e d a n s s . / / Autant l i b é r e r m a i n t e n a n t . un certain nombre de questions peuvent se poser. 9 f ( s . Ils sont souvent intégrés dans des fonctions. i n t n ) { 2 .. 3 i n t ∗ s=new 4 . . 1 int ∗ alloue ( int n) { 2 r e t u r n new i n t [ n ] . elle. . A ce propos..

c’est parce qu’on passe l’adresse d’un tableau qu’on peut modifier ses éléments. Je vous laisse méditer l’exemple suivant qui pourrait être un passage d’un programme implémentant des matrices 3 et leur produit : 1 2 3 4 5 6 7 8 9 10 # i n c l u d e <iostream > # include <string > using namespace s t d . / / Ne p a s o u b l i e r p o u r a u t a n t ! Bizzarerie ? Les lignes 7 et 8 ci-dessus auraient pu s’écrire int∗ t . / / ================================================== / / f o n c t i o n s sur l e s m a t r i c e s / / p o u r r a i e n t e t r e d a n s un m a t r i c e . 5 } 6 . Par ignorance. Allocation dynamique 8. Ainsi. h e t un m a t r i c e . s.. 11 d e l e t e [ ] t ... i n t& n ) { 3 c i n >> n . 7 int ∗ t . 8. joue avec des images. / / n e s t c h o i s i au c l a v i e r 4 t =new i n t [ n ] . — Si on veut vraiment passer le pointeur par référence. Dire qu’un tableau est passé par référence était un abus de langage simplificateur. nous disions que les tableaux étaient passés par référence en annonçant cela comme une exception. 3. il faut remettre une étoile devant chaque variable lorsqu’on définit plusieurs pointeurs en même-temps. Il faut les réunir dans une structure. 107 .3 Structures et allocation dynamique Passer systématiquement un tableau et sa taille à toutes les fonctions est évidemment pénible. Comprenez le source quand même et rattrapez vous avec le TP qui. Structures et allocation dynamique En fait. Coin des enfants : les matrices et les vecteurs vous sont inconnus. 8 int n.3. Nous pouvons maintenant rectifier : Un tableau est en fait passé via son adresse. En fait. la syntaxe est logique : int∗& t. / / t e t n sont a f f e c t é s par a l l o u e ( ) 10 . n ) .. Un cas typique de besoin est : 1 / / t et n seront modifiés ( et plus seulement t [ i ] ) 2 void a l l o u e ( i n t ∗& t . Mais ce mécanisme permet à la fonction appelée de modifier le tableau.n. cpp s t r u c t Matrice { i n t m. lui..8. Cette adresse est passée par valeur.∗u. 9 alloue ( t . définit deux pointeurs d’int (les variables t et u) et un int (la variable s). n . Ca n’est pas grave. int ∗t .

8.3. Structures et allocation dynamique

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

8. Allocation dynamique

double ∗ t ;
};
M a t r i c e c r e e ( i n t m, i n t n ) {
M a t r i c e M;
M.m=m;
M. n=n ;
M. t =new double [m∗n ] ;
r e t u r n M;
}
void d e t r u i t ( M a t r i c e M) {
d e l e t e [ ] M. t ;
}
M a t r i c e p r o d u i t ( M a t r i c e A, M a t r i c e B ) {
i f (A. n ! =B .m) {
cout << " E r r e u r ! " << endl ;
exit (1);
}
M a t r i c e C= c r e e (A.m, B . n ) ;
f o r ( i n t i = 0 ; i <A.m; i ++)
f o r ( i n t j = 0 ; j <B . n ; j ++) {
/ / C i j =Ai0 ∗ B 0 j+Ai1 ∗ B 1 j + . . .
C . t [ i +C .m∗ j ] = 0 ;
f o r ( i n t k = 0 ; k<A. n ; k++)
C . t [ i +C .m∗ j ]+=A. t [ i +A.m∗k ] ∗ B . t [ k+B .m∗ j ] ;
}
return C;
}
void a f f i c h e ( s t r i n g s , M a t r i c e M) {
cout << s << " = " << endl ;
f o r ( i n t i = 0 ; i <M.m; i ++) {
f o r ( i n t j = 0 ; j <M. n ; j ++)
cout << M. t [ i +M.m∗ j ] << " " ;
cout << endl ;
}
}
/ / ==================================================
// Utilisateur
i n t main ( )
{
M a t r i c e A= c r e e ( 2 , 3 ) ;
f o r ( i n t i = 0 ; i < 2 ; i ++)
f o r ( i n t j = 0 ; j < 3 ; j ++)
108

8. Allocation dynamique

60
61
62
63
64
65
66
67
68
69
70
71
72
73 }

8.3. Structures et allocation dynamique

A. t [ i +2∗ j ]= i + j ;
a f f i c h e ( "A" ,A ) ;
M a t r i c e B= c r e e ( 3 , 5 ) ;
f o r ( i n t i = 0 ; i < 3 ; i ++)
f o r ( i n t j = 0 ; j < 5 ; j ++)
B . t [ i +3∗ j ]= i + j ;
a f f i c h e ( "B" ,B ) ;
M a t r i c e C= p r o d u i t (A, B ) ;
a f f i c h e ( "C" ,C ) ;
d e t r u i t (C ) ;
detruit (B ) ;
d e t r u i t (A ) ;
return 0;

L’utilisateur n’a maintenant plus qu’à savoir qu’il faut allouer et libérer les matrices
en appelant des fonctions mais il n’a pas à savoir ce que font ces fonctions. Dans cette
logique, on pourra rajouter des fonctions pour qu’il n’ait pas non plus besoin de savoir
comment les éléments de la matrice sont mémorisés. Il n’a alors même plus besoin de
savoir que les matrices sont des structures qui ont un champ t ! (Nous nous rapprochons vraiment de la programmation objet...) Bref, on rajoutera en général :
10
11
12
13
14
15
16

double g e t ( M a t r i c e M, i n t i , i n t j ) {
r e t u r n M. t [ i +M.m∗ j ] ;
}
void s e t ( M a t r i c e M, i n t i , i n t j , double x ) {
M. t [ i +M.m∗ j ]= x ;
}
que l’utilisateur pourra appeler ainsi :

51
52
53

f o r ( i n t i = 0 ; i < 2 ; i ++)
f o r ( i n t j = 0 ; j < 3 ; j ++)
s e t (A, i , j , i + j ) ;
et que celui qui programme les matrices pourra aussi utiliser pour lui :

39 void a f f i c h e ( s t r i n g s , M a t r i c e M) {
40
cout << s << " = " << endl ;
41
f o r ( i n t i = 0 ; i <M.m; i ++) {
42
f o r ( i n t j = 0 ; j <M. n ; j ++)
43
cout << g e t (M, i , j ) << " " ;
44
cout << endl ;
45
}
46 }
Attention, il reste facile dans ce contexte :
— D’oublier d’allouer.
— D’oublier de désallouer.
— De ne pas désallouer ce qu’il faut si on fait A=B entre deux matrices. (C’est alors
deux fois la zone allouée initialement pour B qui est désallouée lorsqu’on libère
109

8.4. Boucles et continue

8. Allocation dynamique

A.t

...

A.t

...

A.t

...

A.t

...

B.t

...

B.t

...

B.t

...

B.t

...

A=cree(2,3);
B=cree(2,3);

A=B;

detruit(A);

detruit(B);

F IGURE 8.2 – Attention au double delete : le code A=B fait pointer deux fois sur la
même zone mémoire alors qu’il n’y a plus de pointeur sur le tableau du haut (donc
une fuite mémoire puisqu’il n’est plus possible de la libérer). Le detruit(B) libère
une zone mémoire qui l’avait déjà été, avec des conséquences fâcheuses...
A et B tandis que la mémoire initiale de A ne le sera jamais, comme on peut le
voir sur la Fig. 8.2).
La programmation objet essaiera de faire en sorte qu’on ne puisse plus faire ces erreurs.
Elle essaiera aussi de faire en sorte que l’utilisateur ne puisse plus savoir ce qu’il n’a pas
besoin de savoir, de façon à rendre vraiment indépendantes la conception des matrices
et leur utilisation.

8.4

Boucles et continue

Nous utiliserons dans le TP l’instruction continue qui est bien pratique. Voici ce
qu’elle fait : lorsqu’on la rencontre dans une boucle, toute la fin de la boucle est sautée
et on passe au tour suivant. Ainsi :
for ( . . . ) {
...
i f (A)
continue ;
...
i f (B)
continue ;
...
}
est équivalent à (et remplace avantageusement au niveau clarté et mise en page) :
for ( . . . ) {
...
i f ( ! A) {
...
i f ( ! B) {
...
}
}
}
Ceci est à rapprocher de l’utilisation du return en milieu de fonction pour évacuer les
cas particuliers (section 7.3).
110

8. Allocation dynamique

8.5. TP

F IGURE 8.3 – Deux images et différents traitements de la deuxième (négatif, flou, relief,
déformation, contraste et contours).

8.5

TP

Le TP que nous proposons en A.7 est une illustration de cette façon de manipuler
des tableaux bidimensionnels dynamiques à travers des structures de données. Pour
changer de nos passionnantes matrices, nous travaillerons avec des images (figure 8.3).

8.6

Fiche de référence

Fiche de référence (1/3)
— Définition :
int i;
int k,l,m;
— Affectation :
i=2;
— int i=1;
j=i;
while(i<=100) {
k=l=3;
...
— Initialisation :
i=i+1;
int n=5,o=n;
}

Constantes :
— for(int i=1;i<=10;i++)
const
int s=12;
...
— for(int i=1,j=10;j>i; — Portée :
int i;
i=i+2,j=j-3)
// i=j; interdit!
...
int j=2;
— for (int i=...)
i=j; // OK!
for (int j=...) {
if (j>1) {
//saute cas i==j
int k=3;
if (i==j)
j=k; // OK!
continue;
}
...
//i=k; interdit!
}
— Types :
Variables
int i=3;
Boucles

— do {
...
} while(!ok);

111

double x=12.3;
char c=’A’;
string s="hop";
bool t=true;
float y=1.2f;
unsigned int j=4;
signed char d=-128;
unsigned char d=25;
complex<double>
z(2,3);
— Variables globales :
int n;
const int m=12;
void f() {
n=10;
// OK
int i=m; // OK
...
— Conversion :
int i=int(x),j;
float x=float(i)/j;
— Pile/Tas

8.6. Fiche de référence

8. Allocation dynamique

Fiche de référence (2/3)
Clavier
— Build : F7
— Debug : F5
— Step over : F10

— Step inside : F11
— Indent : Ctrl+K,Ctrl+F
— Step out : Maj+F11
— Gest. tâches : Ctrl+Maj+Ech
Fonctions

}
— En paramètre (suite) :
...
— void f(int* t,int n){
int x=3,y=2;
t[i]=...
swap(x,y);
}
Surcharge :
int hasard(int n);
— void alloue(int*& t){
int hasard(int a,
t=new int[n];
int b);
}
double hasard();
— 2D :
Opérateurs :
int A[2][3];
vect operator+(
A[i][j]=...;
vect A,vect B) {
int A[2][3]=
...
{{1,2,3},{4,5,6}};
}
void
f(int A[2][2]);
...
vect C=A+B;
— 2D dans 1D :
int A[2*3];
Pile des appels
A[i+2*j]=...;
Itératif/Récursif

— Définition :
int plus(int a,int b) {
int c=a+b;
return c;
}

void affiche(int a) {
cout << a << endl; —
— Taille variable (suite) :
}
Tableaux
int *t,*s,n;
— Déclaration :
— Définition :
int plus(int a,int b);
— double x[5],y[5];
Structures
— Retour :
for(int i=0;i<5;i++)
— struct Point {
int signe(double x) {
y[i]=2*x[i];
double x,y;
if (x<0)
— const int n=5;
Color c;
return -1;
int i[n],j[2*n];
};
if (x>0)
— Initialisation :
...
return 1;
int t[4]={1,2,3,4};
Point a;
return 0;
string s[2]={"ab","c"};
a.x=2.3; a.y=3.4;
}
— Affectation :
a.c=Red;
void afficher(int x,
int s[3]={1,2,3},t[3];
Point b={1,2.5,Blue};
int y) {
for (int i=0;i<3;i++)
if (x<0 || y<0)
t[i]=s[i];
Compilation séparée
return;
if (x>=w || y>=h) — En paramètre :
— #include "vect.h",
y
return;
— void init(int t[4]) {
compris dans vect.cpp
DrawPoint(x,y,RED);
for(int i=0;i<4;i++)
}
t[i]=0;
— Fonctions : déclarations dans
}
le .h, définitions dans le
— Appel :
.cpp
int f(int a) { ... }
— void init(int t[],
int g() { ... }
int n) {
— Types : définitions dans le .h
...
for(int i=0;i<n;i++)
int i=f(2),j=g();
— Ne déclarer dans le .h que les
t[i]=0;
fonctions utiles.
}
— Références :
— Taille variable :
void swap(int& a,
— #pragma once au début du
int* t=new int[n];
int& b){
fichier.
...
int tmp=a;
delete[] t;
— Ne pas trop découper...
a=b;b=tmp;

112

size(). //NON — — int n=5.. a={1. // NON! t[i. // NON! int* t=new int[2].cpp) Ne pas abuser du récursif.j]=. //Déja fait int *t.3].2. delete[] s... // On perd s! delete[] t.. — for (int i=0. Le .// s est int // et non int* t=new int[n].. — double sin(double x). — if (i=2) // NON! if i==2 // NON! Conseils if (i==2) then // NON! — Nettoyer en quittant... Point a. srand((unsigned int) time(0)). } — bool t=(i==0). — char c=s[0].. i-=2.t[3]. s=new int[n].. break. // NON! — x=double(i/j). y=1/x. ... default: . cout <<"I="<<i<<endl. //NON! — — double x[10].. Allocation dynamique 8..i++) — Erreurs et warnings : cliquer. — #include <ctime> . case 2: case 3: . Faire des fonctions. Fiche de référence Fiche de référence (3/3) Tests — Comparaison : == != < > <= >= — Négation : ! — Combinaisons : && || — if (i==0) j=1. — if (i==0) { j=1.i<100...y[10].2}.. Compiler régulièrement. x=i/j. Ne pas laisser de warning.// NON! Erreurs fréquentes — Pas de définition de fonction dans une fonction ! Imagine++ — int q=r=4.i++) y[i]=2*x[i].. // NON! — int t[2]. k=2... — — #include <ctime> s=double(clock()) /CLOCKS_PER_SEC. // NON! — Voir documentation. t[1]=. . — #include <cmath> — double sqrt(double x).3}. — j=i%n.} — int i=f. i=rand()%n. — #define _USE_MATH_DEFINES #include <cmath> — double pi=M_PI.. — if (i==0) j=1. break. } Entrées/Sorties — #include <iostream> using namespace std..cpp"//NON void f(int t[][]).2}... int l=s. #include <cassert> . — — int s[3]={1. // NON! — — struct Point { double x. // NON! — } — int t[4].. — int t[n]. . // NON! — — double x=1/3.s. — — #include <string> using namespace std. } // NON! 113 Indenter.6... assert(x!=0). Faire des fichiers séparés... // NON! int* t.8. return t. else j=2.h doit suffire à l’utilisateur (qui ne doit pas regarder le .. Utiliser le debuggeur. — t=s. if (t) j=1. for (int i=1.j. Divers — i++. s=t. — switch (i) { case 1: ... cin >> i >> j. t={1. x=rand()/ double(RAND_MAX). Tableaux : pas pour transcrire une formule mathématique ! Faire des structures. // NON! — int i..i<=10. t=f(). // NON — — int f()[4] { // NON! int t[4]. Debug/Release : nettoyer les deux. string s="hop". double cos(double x). . i--. j+=3. // Modulo — #include <cstdlib> . // NON! #include "vec. double acos(double x).//NON int t[2. int* s=new int[2].y. // NON! — int f() {. Ne pas oublier delete.

.

C’est dans ce cas qu’il faut faire des objets. préférant nous consacrer à d’autres aspects du C++ plus indispensables et négligés jusqu’ici. En pratique. nous justifierons l’emploi des objets par la notion d’"interface" 2 . . tantôt nous nous intéressions aux données (structures. C’est d’ailleurs ce que nous avons constaté naturellement dans les exemples des chapitres précédents. Tantôt nous structurions davantage les instructions (fonctions.. fichiers). 2. dont le débutant pourrait rapidement abuser. Il arrive que les deux soient liés. l’utilisation d’un objet remplacera ce genre d’instructions : obj a . Nous allons maintenant penser données et instructions simultanément : c’est là l’idée première des objets. tableaux). i n t i =a . — 9. Nous n’utiliserons pas ici cette façon de présenter les choses. int i=f ( a ) . que nous ne verrons pas dans ce cours. ce qui dépasse largement ce cours ! 3. dans lesquels un fichier regroupait souvent une structure et un certain nombre de fonctions s’y rapportant. Enfin. même s’ils possèdent de nombreux autres aspects 1 . Nous exposerons une façon simple de créer des interfaces. Ainsi : Ce ne sont plus les fonctions qui travaillent sur des données. Premiers objets Chapitre 9 Premiers objets Nous abordons maintenant notre dernière étape dans la direction d’une meilleure organisation des programmes. f ( ) .. Réunir les données en tableaux ou structures aussi.1 Philosophie Réunir les instructions en fonctions ou fichiers est une bonne chose. L’idée est simple : un objet est un type de donnée possédant un certain nombre de fonctionnalités propres 3 . / / a p p e l à l a méthode f ( ) de a 1. Le plus important étant l’héritage. Il arrive même parfois qu’un objet regroupe des fonctionnalités sans pour autant stocker la moindre donnée. Ce sont les données qui possèdent des fonctionnalités.9. Ces "fonctionnalités" sont souvent appelées les méthodes de l’objet. // fonction f () appliquée à a par : obj a . Un programmeur C++ expérimenté utilisera plutôt de l’héritage et des fonctions virtuelles pures.

crions tout de suite haut et fort qu’ il ne faut pas abuser des objets. abandonnez les objets. surtout lorsqu’on est débutant. Ce qui ne veut pas dire qu’un débutant ne doit pas faire d’objets. int f ( ) . il s’agit ni plus ni moins de "ranger" les fonctions dans les objets. les champs d’un objet sont parfois appelés membres de l’objet. Un exemple simple : lorsqu’une fonction travaille sur deux types de données. a . Instructions et données ne sont pas toujours liées. Premiers objets Vous l’avez compris. f ( ) . i n t i =a . Mais seule l’expérience permet de correctement organiser son programme. etc. — de mal penser l’organisation des données ou des instructions en objets. int i=f (a .2 Exemple simple On l’aura compris dans les exemples précédents. Le premier code était le plus logique : la fonction f () n’a souvent rien à faire chez a. D’ailleurs.9. Des petits objets dans des cas simples sont toujours une bonne idée. i n t main ( ) { obj a . les méthodes des objets sont considérées comme faisant partie du type de l’objet. avec les bons objets. 9. obj2 b . }. le débutant voudra souvent s’acharner à en faire malgré tout une méthode de l’un des deux objets. les bonnes fonctions. . f ( b ) . / / méthode f ( ) de a a p p l i q u é e à b / / Est−c e b i e n l a c h o s e à f a i r e ???? Seuls un peu de recul et d’expérience permettent de rester simple quand il le faut. // f () appliquée à a et b en : obj1 a . b ) . Exemple simple 9. / / champ x / / méthode f ( ) / / méthode g ( ) 116 . au même titre que ses champs. Attention... Un conseil donc : quand ça devient trop compliqué pour vous. ni chez b. x =3. Voici ce que cela donne en C++ : s t r u c t obj { int x . Les dangers sont en effet : — de voir des objets là où il n’y en n’a pas.2. i n t i =a . obj2 b . et transformer : obj1 a . et ses méthodes des fonctions membres. int g( int y ) .

. / / c e l u i de b e s t d o u b l e i n t i =a . g ( 2 ) . 117 . Ce mécanisme existe aussi pour les fonctions usuelles. on fait comme pour les fonctions habituelles. / / méthode g ( ) ( d é c l a r a t i o n ) }... / / champ x int f ( ) . Exemple simple i n t j =a . return . obj2 b . .9. x = 3 . Voici comment cela s’écrit : s t r u c t obj1 { int x ... / / m é t h o d e f ( ) d e a ( donc o b j 1 : : f ( ) ) i n t j =a . sauf que pour permettre à plusieurs objets d’avoir les mêmes noms de méthodes.. s t r u c t obj2 { double x . on préfixe leur définition par le nom de l’objet suivi de :: a .. } double o b j 2 : : f ( ) { / / méthode f ( ) de o b j 2 ( d é f i n i t i o n ) . Ce sont les espaces de nom. mais d’importance : la définition de la structure obj ci-dessus ne fait que déclarer les méthodes. que nous avons rencontrés et contournés immédiatement avec using namespace std pour ne pas avoir à écrire std::cout . i n t main ( ) { obj1 a . 5 . . g ( 2 ) . Il y a juste un détail. Pour les définir. f ( ) . i n t obj1 : : f ( ) { / / méthode f ( ) de o b j 1 ( d é f i n i t i o n ) . x =3.. f ( ) . / / m é t h o d e f ( ) d e b ( donc o b j 2 : : f ( ) ) .2. Elles ne sont définies nulle part dans le code précédent. return .. a ... .... } i n t obj1 : : g ( i n t y ) { / / méthode g ( ) de o b j 1 ( d é f i n i t i o n ) . . return . } . . / / méthode f ( ) ( d é c l a r a t i o n ) }. Premiers objets 9.. . . / / champ x double f ( ) .. / / méthode f ( ) ( d é c l a r a t i o n ) int g( int y ) . / / m é t h o d e g ( ) d e a ( donc o b j 1 : : g ( ) ) double y=b . a. . / / l e champ x d e a e s t i n t b .

un objet accède directement à ses champs et à ses autres méthodes.. C’est suivant cette même logique. Vous verrez peut-être parfois traîner le mot clé this qui est utile à certains moment en C++ et que les programmeurs venant de Java mettent partout en se trompant d’ailleurs sur son type. on peut utiliser toutes les variables et fonctions de cet espace sans préciser l’espace en question. Premiers objets Visibilité Il y a une règle que nous n’avons pas vue sur les espaces de nom mais que nous pouvons facilement comprendre : quand on est "dans" un espace de nom. c’est probablement qu’on est en train de ranger dans cet objet une fonction qui n’a rien à voir avec lui (cf abus mentionné plus haut) 9.4 Exemple des matrices En programmation. un exemple de source vaut mieux qu’un long discours. endl et les autres. Si jusqu’ici vous naviguiez dans le vague. 7 } 8 . Ainsi. f 12 13 i n t i 2 =a2 . Visibilité 9. 11 i n t i 1 =a1 . que dans ses méthodes. cin.. ceux qui ont programmé cout et endl ont défini l’espace std puis se sont "placés à l’intérieur" de cet espace pour programmer sans avoir à mettre std:: partout devant cout. 9 i n t main ( ) { 10 o b j 1 a1 . 3 4 i n t j =x+ i . Par exemple. g ( ) l i g n e 2 4 u t i l i s e r l i g n e 2 a2 . ().3 9.9. / / méthode f ( ) de o b j 1 ( d é f i n i t i o n ) méthode g ( ) de l ’ o b j e t dont l a méthode f ( ) e s t en t r a i n d e s ’ e x é c u t e r champ x d e l ’ o b j e t d o n t l a m é t h o d e f ( ) e s t en t r a i n d e s ’ e x é c u t e r // // // // C e t a p p e l va e t a1 .. car si un objet n’utilise pas ses champs dans une méthode. f 14 { // // // // ().. / / ================================================== / / f o n c t i o n s sur l e s m a t r i c e s 118 . c’est-à-dire sans rien mettre devant a ! a. les choses devraient maintenant s’éclaircir ! Voilà donc ce que devient notre exemple du chapitre 8 avec des objets : # i n c l u d e <iostream > # include <string > using namespace s t d . la fonction obj1::f() ci-dessus pourrait s’écrire : 1 i n t obj1 : : f ( ) 2 i n t i =g ( 3 ) . Vous n’en n’aurez en général pas besoin. g ( ) 4 Il est d’ailleurs normal qu’un objet accède simplement à ses champs depuis ses méthodes. 5 6 return j .3. x l i g n e u t i l i s e r a1 . a2 . x l i g n e C e t a p p e l va e t a2 .

} void M a t r i c e : : a f f i c h e ( s t r i n g s ) { cout << s << " = " << endl . } 119 . } } M a t r i c e o p e r a t o r ∗ ( M a t r i c e A. i n t j ) { r e t u r n t [ i +m∗ j ] . n=n1 . cout << endl . }. f o r ( i n t i = 0 . } double M a t r i c e : : g e t ( i n t i . double x ) . i <m. exit (1).4. Exemple des matrices / / p o u r r a i e n t e t r e d a n s un m a t r i c e . M a t r i c e B ) { i f (A. void c r e e ( i n t m1. t =new double [m∗n ] . i n t j ) . M a t r i c e B ) . j <n . void a f f i c h e ( s t r i n g s ) . void d e t r u i t ( ) . M a t r i c e o p e r a t o r ∗ ( M a t r i c e A. i n t n1 ) . Premiers objets 9. cpp ) void M a t r i c e : : c r e e ( i n t m1. / / ========= d é f i n i t i o n s ( d a n s l e . double ∗ t . i n t n1 ) { / / N o t e z que l e s p a r a m e t r e s ne s ’ a p p e l l e n t p l u s m e t n / / p o u r ne p a s m é l a n g e r a v e c l e s champs ! m=m1 . h e t m a t r i c e . i n t j . double g e t ( i n t i . i n t j . i ++) { f o r ( i n t j = 0 . j ) << " " . void s e t ( i n t i . } void M a t r i c e : : d e t r u i t ( ) { delete [ ] t . n . double x ) { t [ i +m∗ j ]= x . cpp / / ========= d e c l a r a t i o n s ( d a n s l e . } void M a t r i c e : : s e t ( i n t i . h ) s t r u c t Matrice { i n t m. n ! =B . j ++) cout << g e t ( i .m) { cout << " E r r e u r ! " << endl .9.

j <B . En clair. n . a f f i c h e ( "A" ) . d e t r u i t ( ) . i ++) f o r ( i n t j = 0 . detruit ( ) . S’il possède une méthode operatorop(objB B). i < 2 . j ++) B.. Pour y remédier.m. Matrice B . } 9. k ) ∗ B . g e t ( i . i ++) f o r ( i n t j = 0 . set ( i . C .. . A. alors AopB appellera cette méthode pour tout B de type objB. j . return 0. n . cree ( 3 .9. 0 ) . M a t r i c e C=A∗B . j ++) A. k++) C. j ) ) . j < 3 . s e t ( i . } / / ==================== main =========== i n t main ( ) { M a t r i c e A. j < 5 . j . j . a f f i c h e ( "C" ) . k<A. Cas des opérateurs 9. 120 . } return C. B. detruit ( ) . A. j . C .5 Cas des opérateurs Il est un peu dommage que l’opérateur ∗ ne soit pas dans l’objet Matrice. f o r ( i n t i = 0 . f o r ( i n t i = 0 . a f f i c h e ( "B" ) . i < 3 . j )+A. B . B .m. c r e e (A. C. i ++) f o r ( i n t j = 0 . c r e e ( 2 . f o r ( i n t i = 0 . i <A. on adopte la convention suivante : Soit A un objet. g e t ( i . A. g e t ( k . n ) . .5. 5 ) . 3 ) . j ++) { / / C i j =Ai0 ∗ B 0 j+Ai1 ∗ B 1 j + . Premiers objets Matrice C. set ( i . set ( i . i+j ) . le programme : s t r u c t objA { . C. i + j ) . B . C . f o r ( i n t k = 0 .

. objB B ) { . o p e r a t o r +(B ) . }... c r e e (m. i n t main ( ) { objA A.9. objB B . / / A∗B a p p e l l e A . }.. / / a p p e l l e o p e r a t o r +(A. B ) . }. peut aussi s’écrire : s t r u c t objA { . . i n t objA : : o p e r a t o r +( objB B ) { . } . i n t main ( ) { objA A. s t r u c t objB { .. }... } Matrice C.. ce qui pour nos matrices donne : s t r u c t Matrice { . / / a p p e l l e m a i n t e n a n t A .. s t r u c t objB { . o p e r a t o r ∗ ( B ) donc t o u s / / l e s champs e t f o n c t i o n s u t i l i s é s d i r e c t e m e n t / / c o n c e r n e n t ce qui é t a i t p r é f i x é précédemment par A.. i n t i =A+B ... Matrice Matrice : : operator ∗( Matrice B) { / / On e s t d a n s l ’ o b j e t A du A∗B a p p e l é i f ( n ! =B . 121 ... objB B . C .. i n t o p e r a t o r +( objA A... Matrice operator ∗( Matrice B ) . i n t o p e r a t o r +( objB B ) . B . Cas des opérateurs }.m) { / / Le n d e A cout << " E r r e u r ! " << endl ... i n t i =A+B .. } . exit (1).5. Premiers objets 9. n ) ...

qui n’est pas un objet 4 . sera bien inspiré d’appeler la méthode Matrice::operator∗(double lambda) si elle est déjà programmée : M a t r i c e o p e r a t o r ∗ ( double lambda . k++) / / g e t ( i . } Notez aussi que l’argument de l’opérateur n’a en fait pas besoin d’être un objet.A) q u i a p p e l l e à s o n t o u r / / A. on s’aperçoit qu’il n’utilise plus les champs des Matrice mais seulement leurs méthodes. Il faudra simplement se contenter d’un opérateur standard. Interface 9. i n t j . j .6 Interface Si on regarde bien le main() de notre exemple de matrice. . } return C. o p e r a t o r ∗(2) Nous verrons au chapitre suivant d’autres opérateurs utiles dans le cas des objets. / / A p p e l l e A . 9.. et de toute façon n’appartient pas au programmeur ! 122 . void d e t r u i t ( ) . set ( i . f o r ( i n t k = 0 . j <B ... seule la partie s t r u c t Matrice { void c r e e ( i n t m1.. j )+ g e t ( i . C. 0 ) . . Ainsi pour écrire le produit B=A∗2. donc r i e n à r e p r o g r a m m e r ! } .. n .9. B=2∗A. k<n . } . Premiers objets f o r ( i n t i = 0 .6. En fait. void s e t ( i n t i .. i <m. M a t r i c e A) { r e t u r n A∗lambda .. j ) ) . il suffira de créer la méthode : M a t r i c e M a t r i c e : : o p e r a t o r ∗ ( double lambda ) { . i ++) f o r ( i n t j = 0 . o p e r a t o r ∗ ( 2 ) Par contre. g e t ( k . j . j ) s e r a c e l u i de A C. set ( i . j ++) { / / C i j =Ai0 ∗ B 0 j+Ai1 ∗ B 1 j + . pour écrire B=2∗A. g e t ( i . on ne pourra pas créer : M a t r i c e double : : o p e r a t o r ∗ ( M a t r i c e A) / / IMPOSSIBLE / / d o u b l e n ’ e s t p a s un o b j e t ! car cela reviendrait à définir une méthode pour le type double. / / a p p e l l e o p e r a t o r ∗ ( 2 .. qui. double g e t ( i n t i . B=A∗ 2 . / / d é f i n i précé demm ent . d’ailleurs. i n t j ) . C . 4. i n t n1 ) . k ) ∗ B . double x ) .

}. Et il en existe ! Par exemple pour stocker efficacement des matrices creuses. Etc. etc. a. Si ce dernier trouve un autre moyen 5 de stocker un tableau bidimensionnel de double. Il peut même les reprogrammer ensuite d’une autre façon : les programmes de l’utilisateur marcheront toujours ! C’est le concept même d’une interface : — Le concepteur et l’utilisateur des objets se mettent d’accord sur les méthodes qui doivent exister. — L’utilisateur les utilise de son côté. Du coup.7. ou bien consommer plus ou moins de mémoire. Premiers objets 9. comme il en existe déjà en C++ standard ou dans des bibliothèques complémentaires disponibles sur le WEB. reliées uniquement par l’interface. libre à lui de le faire. C’est lui qui précise l’interface. c’est lui qui doit gérer les problèmes d’algorithmique. Bref. mais les détails d’implémentation ne sont pas entièrement cachés : la définition de la structure dans le fichier d’en-tête fait apparaître les champs utilisés pour l’implémentation. A.m= 4 . l’utilisateur peut-être tenté des les utiliser ! Rien ne l’empêche en effet des faire des bêtises : M a t r i c e A. Bref. Ou bien. 123 . leur concepteur peut les programmer comme il l’entend. En fait Si l’utilisateur des Matrice se conforme aux déclarations des méthodes ci-dessus. utilisation et implémentation deviennent indépendantes b . c’est-à-dire celles dont la plupart des éléments sont nuls. l’utilisateur peut changer pour une implémentation concurrente sans avoir à retoucher son programme.7. c r e e ( 3 . — Le concepteur peut y retoucher sans gêner l’utilisateur.1 Protection Principe Tout cela est bien beau. Matrice operator ∗( Matrice B ) . c’est que les deux y gagnent : le concepteur peut améliorer son implémentation sans gêner l’utilisateur. en utilisant des objets implémentant déjà des tableaux de façon sûre et efficace. — Le concepteur les programme : il implémente a l’interface. sans rentrer dans les détails d’implémentation. A.9. En particulier le fichier d’en-tête de l’objet est le seul qui intéresse l’utilisateur. C’est aussi en général ce qui fait que. / / A i e ! L e s a c c è s v o n t ê t r e f a u x ! ou tout simplement de préférer ne pas s’embêter en remplaçant 5. 9. Protection void a f f i c h e ( s t r i n g s ) . Ce qui est sûr. 2 ) . intéresse l’utilisateur. etc.7 9. Que les dimensions soient dans des champs int m et int n et que les éléments soient dans un champ double∗ t ne le concerne plus : c’est le problème de celui qui programme les matrices. pour une même interface. Il se trouve en général face au difficile problème du choix de l’implémentation : certaines façons de stocker les données peuvent rendre efficaces certaines méthodes au détriment de certaines autres. un utilisateur préférera telle ou telle implémentation : le concepteur devra aussi faire face à la concurrence ! b.

. / / OK } . Remplacer struct par class : tous les champs et les méthodes deviennent privés : seules les méthodes de l’objet lui-même ou de tout autre objet du même type a peuvent les utiliser. les méthodes de la classe en question ! b. . / / OK z=. 0 ) . Bref. Il existe aussi des passages protégés.. / / OK A. . On pourrait à nouveau déclarer des passages privés avec private:...=y. / / OK } void o b j : : une_autre ( o b j A) { x=A. / / OK } void o b j : : pour_tous ( ) { x=. }. void a_moi ( ) . Placer la déclaration public: dans la définition de l’objet pour débuter la zone b à partir de laquelle seront déclarés les champs et méthodes publics. puis publics...9. / / NON! 124 .7. / / OK a_moi ( ) . par f o r ( i n t i = 0 . j ++) A. i ++) f o r ( i n t j = 0 . l’utilisation n’est plus indépendante de l’implémentation et on a perdu une grande partie de l’intérêt de la programmation objet. notion qui dépasse ce cours. Protection 9. x = . a. Pour cela : 1. / / OK . i < 6 .... void o b j : : a_moi ( ) { x=. public : int z . a_moi ( ) . A.. void une_autre ( o b j A ) . void pour_tous ( ) .. y . Voici un exemple : c l a s s obj { int x . c’est-à-dire accessibles à tous. B . i < 3 . etc. C’est ici qu’intervient la possibilité d’empêcher l’utilisateur d’accéder à certains champs ou même à certaines méthodes. Premiers objets f o r ( i n t i = 0 . j . i n t main ( ) { o b j A. s e t ( i . / / H o r r e u r ! Et s i on i m p l é m e n t e a u t r e m e n t ? Dans ce cas. x . t [ i ] = 0 .. 2. i ++) A. j < 2 ..

void a f f i c h e ( s t r i n g s ) . c’est-à-dire qu’elles ne sont pas des objets et qu’elles n’ont pas de méthode 6 . i n t j ) . i n t n1 ) .7. A. .7. Premiers objets A. void d e t r u i t ( ) . i <A.. une structure est une classe où tout est public.. pour empêcher une utilisation dépendante de l’implémentation. public : void c r e e ( i n t m1. Matrice operator ∗( Matrice B ) . nbLin ( ) .9. A. double g e t ( i n t i . finalement.2 Structures vs Classes Notez que. i n t main ( ) { . s e t ( i . j <A. 9. ceci ne devrait pas vous concerner ! 125 .. nbCol ( ) . Mais bon. il suffit de les définir comme suit : c l a s s Matrice { i n t m.. 9. j ++) A. a_moi ( ) . 9. l’utilisateur n’a plus la possibilité de retrouver les dimensions d’une matrice.3 Accesseurs Les méthodes get () et set () qui permettent d’accéder en lecture (get) ou en écriture (set) à notre classe. sans compter qu’ils les déclarent souvent comme en C avec d’inutiles typedef. 0 ) . que nous avions déjà bien programmées. z = .. une_autre ( B ) .7. }. f o r ( i n t i = 0 ... Les anciens programmeurs C pensent souvent à tort que les structures du C++ sont les mêmes qu’en C. Maintenant que nos champs sont tous privés. i ++) f o r ( i n t j = 0 . A. i n t j . 6. sont appelées accesseurs. On rajoutera donc deux accesseurs en lecture vers ces dimensions : i n t M a t r i c e : : nbLin ( ) { r e t u r n m. } . n . Protection // // // // OK NON! OK OK Dans le cas de nos matrices. double ∗ t . j . double x ) . void s e t ( i n t i . pour_tous ( ) . . } i n t M a t r i c e : : nbCol ( ) { return n .. .

i++) } .j=10..8 TP Vous devriez maintenant pouvoir faire le TP en A. Premiers objets mais pas en écriture. ce qui est cohérent avec le fait que changer m en cours de route rendrait fausses les fonctions utilisant t [ i+m∗j] ! F IGURE 9.Ctrl+F — Step out : Maj+F11 — Gest.i<=10. } ... — for(int i=1.8...j=j-3) — do { ... . if (i==j) i=i+1.) — int i=1.1 – Fractales 9. TP 9. for (int j=.j>i.9. } while(!ok). tâches : Ctrl+Maj+Ech ..... 126 Clavier — Build : F7 — Debug : F5 — Step over : F10 — Step inside : F11 — Indent : Ctrl+K..) { while(i<=100) { //saute cas i==j .9 Fiche de référence Fiche de référence (1/4) Boucles — for(int i=1. i=i+2..1) en illustrant le concept d’objet. continue.. — for (int i=..8 qui dessine quelques courbes fractales (figure 9. 9.

vect operator+( vect A.. a. const int m=12. Tableaux — Définition : Point a. — Taille variable : int* t=new int[n]. float x=float(i)/j.9. unsigned int j=4. int hasard(int a."c"}. void affiche(int a) { — Une structure est un objet en— const int n=5.2..i++) if (x>0) t[i]=s[i]..4.int b) { — Définition : a. — Retour : int signe(double x) { — Affectation : if (x<0) int s[3]={1.{4. vect C=A+B. — Surcharge : int hasard(int n). } — void alloue(int*& t){ t=new int[n]. } .. — struct Point { — Pile des appels double x. string s="hop". } jets !) 127 .2. — Constantes : const int s=12..i++) Point b={1.. j=i.Blue}. — void init(int t[4]) { } for(int i=0. if (x>=w || y>=h) int n) { return. — Références : void swap(int& a. int i[n]. a... — En paramètre (suite) : — void f(int* t.. — double x[5]. Color c. } — 2D : int A[2][3]. int A[2][3]= {{1. bool t=true. } . float y=1. int *t. // OK! } //i=k. for(int i=0. swap(x.4}..j. a=b.... k=l=3. t[i]=0. int c=a+b. A[i][j]=. int t[4]={1. void f() { n=10.9. unsigned char d=25. j=k.6}}.5. int x=3. — Portée : int i. interdit! — Types : int i=3.3). // OK int i=m. // i=j.RED). } y[i]=2*x[i]. i=j. — Variables globales : int n.. return -1.i++) DrawPoint(x. int plus(int a. int b).int n){ t[i]=.t[3]..x=2.3. void f(int A[2][2]). } } — Appel : int f(int a) { . — Initialisation : int n=5. char c=’A’. for (int i=0.y. Premiers objets 9. } Structures . interdit! int j=2.y. Fonctions .3}.*s. tièrement public (→ cf obcout << a << endl.y=3. — Pile/Tas — Déclaration : — Initialisation : int plus(int a.j=g(). — 2D dans 1D : int A[2*3].i++) void afficher(int x.n. — Opérateurs : A[i+2*j]=.3. delete[] t. int k.m. // OK ..l. .3. } int g() { ..y). — Affectation : i=2. string s[2]={"ab". double x=12.j[2*n].. — Itératif/Récursif }.2. — Conversion : int i=int(x)..int b). for(int i=0.i<5..b=tmp. int y) { } if (x<0 || y<0) return. int i=f(2).. complex<double> z(2.i<3.. t[i]=0. double hasard(). return 1. — En paramètre : return 0.3}. int& b){ int tmp=a. signed char d=-128. // OK! if (j>1) { int k=3..2..i<n.5. — void init(int t[]..i<4.c=Red.o=n.2f.y=2. Fiche de référence Fiche de référence (2/4) Variables — Définition : int i.vect B) { — Taille variable (suite) : .. return c.y[5].

.. // NON! } } void obj::pour_tous() { — int t[2].. — struct Point { } double x. //OK } A.. int obj::f() { int i=g(3)..une_autre(B).h. void une_autre(obj A) { — switch (i) { } // NON! case 1: x=A. int i=f. // OK .. A.pour_tous().. — Négation : ! — int n=5. t=f(). // NON! if (t) a_moi(). . — Comparaison : for (int i=1. // OK t={1. — int s[3]={1..y[10]. // On perd s! obj operator+(obj B). x=i/j.. }. //OK default: — int* t. Fiche de référence 9...j.//NON case 3: obj A.cpp"//NON case 2: int main() { — void f(int t[][]). // OK — if (i==0) { int t[4]. . int main() { obj a.2. s=new int[n]. C=A+B.s. void un_autre(obj A).} fonctions utiles.. // NON! void obj::a_moi() { } x=. int i=a. // NON — Combinaisons : && || int z. A.. //OK — int* t=new int[2].. // champ int f().cpp — — Types : définitions dans le ... using namespace std. // OK k=2.. delete[] s. — class obj { Entrées/Sorties s=t.C.. x=. définitions dans le .. x=double(i/j). — if (i==0) j=1. //NON! Tests — double x[10].2}. a. Compilation séparée — #include "vect. t[1]=. // NON! .=y..9.. int t[4]. void pour_tous(). // OK ..h que les — int f() {..i++) // NON! — Ne déclarer dans le . else j=2.i<=10. int main() { cout <<"I="<<i<<endl.x=. — int f()[4] { // NON! — if (i==0) j=1. } .cpp — Pas de définition de fonction dans une fonction ! y — int q=r=4.. int* s=new int[2].t[3].y.. //NON int x.. }. //NON . // mon g int j=x+i.B. // NON! break. // OK j=1. A. // OK j=1.f().3}.// NON! // C=A.9. compris dans vect.y. void a_moi().. // NON! fichier.. Premiers objets Fiche de référence (3/4) Objets — struct obj { int x.operator+(B) Erreurs fréquentes 128 ...B. cin >> i >> j. // NON! — — Fonctions : déclarations dans le . }.2}. //NON t[i.h". return t.i++) — class obj { == != < > <= >= y[i]=2*x[i].i<100.. int i..// s est int // et non int* obj A. // NON! A. int t[2.a_moi().3]. // méthode int g(int y). z=. . // NON! — #pragma once au début du — double x=1/3.j]=.x.. — #include "vec.x=3. // mon x return j.z=. // NON! } break..a_moi().. — Point a.. — int *t. a={1..h if (i=2) // NON! if i==2 // NON! if (i==2) then // NON! for (int i=0.. t=s. t=new int[n]... //Déja fait .... // NON! — Ne pas trop découper. public: int t[n]. — bool t=(i==0). A. . — #include <iostream> delete[] t.

. — Ne pas laisser de warning. double acos(double x). Fiche de référence Fiche de référence (4/4) Divers — i++.h doit suffire à l’utilisateur (qui ne doit pas regarder le .9. — Tableaux : pas pour transcrire une formule mathématique ! 129 Faire des structures. — #include <cmath> double sqrt(double x). Ne pas oublier delete. Le . Compiler régulièrement. i=rand()%n.. j+=3.size(). — Ne pas toujours faire des objets ! — Penser interface / implémentation / utilisation. — Erreurs et warnings : cliquer. Faire des fichiers séparés. — char c=s[0]. — — #include <ctime> s=double(clock()) /CLOCKS_PER_SEC. i-=2. Premiers objets 9. assert(x!=0).. — — #define _USE_MATH_DEFINES #include <cmath> — double pi=M_PI. string s="hop". y=1/x.. — #include <cassert> . — Utiliser le debuggeur.. — Faire des fonctions. Conseils — Nettoyer en quittant..9.cpp) Ne pas abuser du récursif. double cos(double x)... — #include <string> using namespace std. — Indenter. x=rand()/ double(RAND_MAX). // Modulo — #include <cstdlib> . — j=i%n. double sin(double x). — int l=s. — Imagine++ — — Voir documentation. — #include <ctime> . — Faire des objets. i--. . Debug/Release : nettoyer les deux. srand((unsigned int) time(0)).

.

}. . en : c l a s s point { int x .. a .10.. . Conséquence : point a = { 2 .. Enfin. j ) . void s e t ( i n t X . nous avons transformé : s t r u c t point { int x . y=3. tant pour l’efficacité des programmes que pour la découverte de bugs à la compilation : une autre utilisation du const. même pour le débutant qui devra au moins connaître leur forme la plus simple. y . set (2 . — 10. i =a . a .. public : void g e t ( i n t&X . point a . a . pour les plus avancés.3). a . nous allons voir comment le C++ offre la possibilité d’intervenir sur ce qui se passe à la naissance et à la mort d’un objet. get ( i . point a . i n t&Y ) . x =2. Nous poursuivrons par un aspect bien pratique du C++. Ces notions sont très utiles. Constructeurs et Destructeurs Chapitre 10 Constructeurs et Destructeurs Dans ce long chapitre. y . nous expliquerons aussi comment les problèmes de gestion du tas peuvent être ainsi automatisés. Ce mécanisme essentiel repose sur la notion de constructeur et de destructeur. }. j =a . i n t Y ) .1 Le problème Avec l’apparition des objets. . 3 } . y . x .

i n t Y ) . On ne peut remplir les champs privés d’un objet. 4 ) . cette façon d’initialiser devient impossible. En réalité. public : point ( i n t X.3 Cas général 10. } . qui fait qu’en général. p o i n t ( 3 .2. / / OK! V a l e u r s i n i t i a l e s / / On ne f a i t p a s comme ç a p o u r c h a n g e r l e s champs d e a . point a ( 2 . a . Le constructeur est appelé à la création de l’objet et ses paramètres sont passés avec la syntaxe ci-dessus.. Notez bien qu’il est impossible d’appeler un constructeur sur un objet déjà contruit : p o i n t a ( 1 . Ce qui explique qu’il n’est pas besoin de lui préciser un type de retour. il y a une autre raison. Constructeurs et Destructeurs est maintenant impossible. le programme : c l a s s obj { public : obj ( ) .. 2 ) . 1.10. Il est impossible d’appeler un constructeur sur un objet déjà créé a . 132 .4). Il ne retourne rien mais son type de retour n’est pas void : il n’a pas de type de retour. dès qu’on programme des objets. plus profonde et trop difficile à expliquer ici. set (3 . La solution 10. / / ERREUR! / / Mais p l u t ô t comme ç a . Ici. a . Ainsi. c’est le construteur vide qui est appelé. / / OK! 10.int Y) qui est défini. a. y=Y . c’est le constructeur point :: point(int X. car cela permettrait d’accéder en écriture à une partie privée 1 ! 10. Un constructeur est une méthode dont le nom est le nom de la classe ellemême. i n t Y) { x=X . p o i n t : : p o i n t ( i n t X .2 La solution La solution est la notion de constructeur : c l a s s point { int x . y . }.3. c’est-à-dire celui sans paramètre. 3 ) . même à l’initialisation.1 Constructeur vide Lorsqu’un objet est crée sans rien préciser.

a=g ( ) . obj : : obj ( ) { cout << " h e l l o " << endl . i ++) { obj b . Font exception les paramètres des fonctions et leur valeur de retour qui. cout << 5 << " " . a. Nous allons voir plus loin cette construction par copie. Ainsi.10.3. / / a p p e l l e l e c o n s t r u c t e u r par d é f a u t affiche "hello". c l a s s obj { public : obj ( ) . Cas général }.. }. obj a . cout << i << " " . eux. 133 .. } . f o r ( i n t i = 2 . } i n t main ( ) { cout << 0 << " " . cout << 1 << " " . Constructeurs et Destructeurs 10. Le constructeur vide obj::obj() est appelé à chaque fois qu’on construit un objet sans préciser de paramètre. obj a . return e . sont construits comme des recopies des objets passés en paramètre ou retournés a . le programme : # i n c l u d e <iostream > using namespace s t d . i <=4. cout << 6 << " " . } f (a ). obj : : obj ( ) { cout << " o b j " . } void f ( o b j d ) { } obj g ( ) { obj e .

}. i n t Y) { x=X . le constructeur vide n’existe plus. un programme qui ne se compile plus : c l a s s point { int x . y . p o i n t : : p o i n t ( i n t X . Y) point b ( 4 ) . } affiche : 0 obj 1 obj 2 obj 3 obj 4 5 obj 6 Bien repérer les deux objets non construits avec obj :: obj () : le paramètre d de f () . a . y=Y .3). point b . / / OK devient. / / c o n s t r u i t a v e c p o i n t (X. copie de e. et la valeur de retour de g().2 Plusieurs constructeurs Un objet peut avoir plusieurs constructeurs. sauf si on le redéfinit soimême. c l a s s point { int x . public : point ( i n t X. 10. point ( i n t V ) . copie de a.. i n t Y ) . p o i n t a ( 2 . } p o i n t : : p o i n t ( i n t V) { x=y=V .. i n t Y ) . avec un constructeur. y . Mais attention : dès qu’on définit soi-même un constructeur. } .10. 134 . le programme : c l a s s point { int x . y . / / c o n s t r u i t a v e c p o i n t (V) Il faut cependant retenir la chose suivante : Si on ne définit aucun constructeur. Constructeurs et Destructeurs return 0.3.. set (2 . 3 ) .. Par exemple. public : point ( i n t X. point a . Cas général 10. tout se passe comme s’il n’y avait qu’un constructeur vide ne faisant rien. . }.3.

/ / ERREUR e t HORREUR! / / Un e s s a i d e c o n s t r u i r e l e s u [ i ] / / avec point ( 1 . 3 ) . y . 3 ) . f o r ( i n t i = 0 . n f o i s p o i n t ∗ u=new p o i n t ( 1 . / / c o n s t r u i t a v e c p o i n t (X. 135 . ce qui n’est pas vraiment identique car on construit alors les points à vide puis on les affecte. i ++) u[ i ] . i n t Y) { x=X . 2 ) [ n ] . 2 ) . set ( 1 . 2 ) . Y) point b . point t [ 3 ] .. ce qui n’est évidemment pas faisable pour un tableau de taille variable. 3 ) . / / C o n s t r u i t 3 f o i s avec l e c o n s t r u c t e u r v i d e / / s u r c h a c u n d e s é l é m e n t s du t a b l e a u p o i n t ∗ s=new p o i n t [ n ] . Par contre... point ( i n t X.3. Y) point b . i n t Y) { x=X . Cas général }. il est possible d’écrire : point t [ 3 ] = { point ( 1 . point ( 3 . 2 ) . point ( 2 . public : point ( ) . i <n . point : : point ( ) { } p o i n t : : p o i n t ( i n t X .. / / OK! c o n s t r u i t a v e c p o i n t ( ) 10. p o i n t : : p o i n t ( i n t X . }. 4 ) } . p o i n t a ( 2 . y=Y .3. / / Idem . p o i n t a ( 2 . / / ERREUR! p o i n t ( ) n ’ e x i s t e p l u s et il faut alors rajouter un constructeur vide. i n t Y ) .3 Tableaux d’objets Il n’est pas possible de spécifier globalement quel constructeur est appelé pour les éléments d’un tableau. qui n ’ e x i s t e pas Il faudra donc écrire : p o i n t ∗ u=new p o i n t [ n ] .. C’est toujours le constructeur vide qui est appelé. Constructeurs et Destructeurs 10. } .10. y=Y . } .. même s’il ne fait rien : c l a s s point { int x . / / c o n s t r u i t a v e c p o i n t (X.

2 ) . / / a f f e c t e directement b à l ’ objet / / temporaire point (5 .. en ne stockant pas dans des variables les points pour lesquels ce n’était pas utile : 1 2 3 4 5 6 7 8 9 10 11 12 13 void f ( p o i n t p ) { .. return e . 4 ) . Attention aussi à l’erreur suivante. b= p o i n t ( 5 . construire un objet sans qu’il soit rangé dans une variable. / / NON! ! ! ! ! ! ! mais plutôt point p ( 1 .10. f (a ). point b .6) . f ( p o i n t ( 3 . b=g ( ) . Objets temporaires 10. On ne remplit pas b directement avec (5. Il ne faut pas écrire p o i n t p= p o i n t ( 1 . / / r e t o u r n e d i r e c t e m e n t / / l ’ objet temporaire point (1 . 2 ) .2) } . a.6) Attention à la ligne 12 : elle est utile quand b existe déjà mais bien comprendre qu’on construit un point (5. Ici. 6 ) peut largement s’alléger. { / / pour l e r e t o u r n e r / / uniquement pour p o u v o i r a p p e l e r f ( ) / / on p o u r r a i t a v o i r e n v i e d e f a i r e / / ça pour m e t t r e b à ( 5 . 4 ) ) . p o i n t ( 3 . en appelant soi-même un constructeur a . point a ( 3 . le programme : void f ( p o i n t p ) . 2 ) . / / OUI ! 136 .6) temporaire qui est ensuite affecté à b. } .4 10. 6 ) . En fait il s’agit d’un objet temporaire sans nom de variable et qui meurt le plus tôt possible.. nous avions déjà dit qu’on ne pouvait pas appeler un constructeur d’un objet déjà construit.. } point g ( ) { point e ( 1 .. Constructeurs et Destructeurs Objets temporaires On peut. 2 ) . 6 ) .4. 4 ) point b .. c’est autre chose : on appelle un constructeur sans préciser d’objet ! Ainsi. temp ..6) comme on le ferait avec un b. b=c .. très fréquente. } point g ( ) { return point ( 1 . b=g ( ) . set (5. / / P a s s e d i r e c t e m e n t l ’ o b j . point c ( 5 . Attention.

. 2 ) .. s’écrira plutôt : p o i n t p o i n t : : o p e r a t o r +( p o i n t b ) { r e t u r n p o i n t ( x+b . dans le programme suivant : c o n s t i n t N= 1 0 0 0 . Constructeurs et Destructeurs 10. c= p o i n t ( 1 . y+b . F IGURE 10.10. }. return c .1 Principe Lorsqu’on passe un objet en paramètre à une fonction.. . x .. } . 10. y ) . TP L’utilité de ces objets temporaires est visible sur un exemple réel : p o i n t p o i n t : : o p e r a t o r +( p o i n t b ) { p o i n t c ( x+b . 10.1 – Jeu de Tron. y+b ..1).5 TP Nous pouvons faire une pause et aller faire le TP que nous proposons en A. 137 . Il s’agit de programmer le jeu de motos de Tron (figure 10. b ( 2 . point a ( 1 ..6 Références Constantes 10. c l a s s matrice { double t [N] [N] . } . x . Cette recopie est source d’inefficacité. y ) .. 3 ) ) . .5. 2 ) + f ( p o i n t ( 2 . c=a+ f ( b ) .9. il est recopié.. c l a s s vecteur { double t [N] . 3 ) .6. Ainsi.

La variable a fait dans notre cas pas moins de 8 millions d’octets : les recopier dans A prend du temps ! Même pour des objets un peu moins volumineux.. le paramètre X n’est pas une copie car il s’agit juste d’un lien vers la variable x. il n’est pas rare non plus que ce temps de recopie soit supérieur à celui passé dans la fonction ! L’idée est alors.6. suivant la façon dont solve est programmée. / / r é s o u t ax=b les variables A et B de la fonction solve() sont des copies des objets a et b de la fonction appelante. alors il y aura erreur de compilation. v e c t e u r& X) { . cette recopie peut ralentir le programme. Rien ne garantit en effet que solve ne modifie pas ses paramètres A et B. C’est évidemment gênant ! Le C++ offre heureusement la possibilité de demander au compilateur de vérifier qu’une variable passée par référence n’est pas modifiée par la fonction. b . a et b eux-mêmes aient été modifiés. En réalité.10. c’est une bonne idée de le remplacer par const obj& o. vecteur b ..6. c o n s t v e c t e u r& B . x ) . 10.. v e c t e u r& X) { . v e c t e u r& X) { . matrice a .. qu’en sortie de solve(a. passé par référence.b. s o l v e ( a . Il suffit de rajouter const au bon endroit : void s o l v e ( c o n s t m a t r i c e& A. v e c t e u r B ... alors que précédemment c’étaient leurs copies A et B qui l’étaient. Notez bien que. de les passer eux-aussi par référence. x . Constructeurs et Destructeurs }. / / r é s o u t AX=B void s o l v e ( m a t r i c e A. .2 Méthodes constantes Considérons le programme suivant : void g ( i n t& x ) { cout << x << endl . pour accélérer le programme... La recopie de a dans A n’est pas une très bonne chose. le programme s’en trouvera accéléré pour la plupart des objets courants. Si quelque part dans solve (ou dans les sous-fonctions appelées par solve !). Lorsqu’une fonction est courte. } . } 138 . Il est donc possible. a. Cependant.. si une fonction est appelée souvent. Références Constantes 10.x). v e c t e u r& B . pour des objets volumineux. la variable A ou la variable B est modifiée. même si la fonction n’a pas à les modifier ! Il suffit donc de définir la fonction solve() ainsi : void s o l v e ( m a t r i c e& A. cette solution n’est pas sans danger. La règle est donc : Lorsqu’un paramètre obj o d’une fonction est de taille importante a ..

i n t a =1.6. Constructeurs et Destructeurs 10. a. g ( ) .. Bref. } void f ( c o n s t i n t& y ) { double z=y ... Imaginons une deuxième version de g() : void g ( i n t& x ) { x ++.10. et que le compilateur vérifie que le programme est bien cohérent. En effet. / / OK ne m o d i f i e p a s y g(y ) . la première version de g() serait refusée elle aussi car pour savoir si une sous-fonction modifie ou non un des paramètres d’une fonction. Le bon programme est donc : void g ( c o n s t i n t& x ) { cout << x << endl . i n t a =1.. notre premier programme ne se compilerait pas non plus car l’appel g(y) avec const int& y impose que g() soit déclarée void g(const int& x). / / OK ne m o d i f i e p a s y g(y ) . En réalité. le compilateur ne se base que sur la déclaration de cette sousfonction et non sur sa définition complète a . Cela se fait avec la syntaxe suivante : c l a s s obj { . La fonction f () ne modifie pas son paramètre y et tout va bien. f (a ). nous avons besoin d’une nouvelle notion. 139 . Le programme ne se compilerait évidemment pas.. Avec les objets. void g ( ) c o n s t . f (a ). considérons maintenant : void f ( c o n s t o b j& o ) { o .. / / OK! Pas b e s o i n d ’ a l l e r r e g a r d e r d a n s g ( ) } . Le C++ n’essaie pas de deviner lui-même si une fonction modifie ses paramètres puisque la logique est que le programmeur indique lui-même avec const ce qu’il veut faire. / / OK? } Il faut indiquer au compilateur si la méthode g() modifie ou non l’objet o.. / / OK? } . } Alors y serait modifiée dans f () à cause de l’appel à g(). Références Constantes void f ( c o n s t i n t& y ) { double z=y ..

— n’a pas de paramètres (Il n’y a donc qu’un seul destructeur par classe. Lorsque le compilateur sait qu’un objet reste constant pendant une partie du programme.10. même si la maîtrise et la mise en application de ce qui s’y trouve est laissée aux plus avancés. g ( ) . il peut éviter d’aller le relire à chaque fois.) — Optimisation du programme 2 . une autre de ses méthodes est appelée : le destructeur. n’a pas de type. Le const est donc une information précieuse pour la partie optimisation du compilateur. — 10.. Rajoutons un destructeur au programme de la section 10. Destructeur 10.7 Destructeur Lorsqu’un objet meurt. — porte le nom de la classe précédé de ˜. placer des const dans les méthodes est une très bonne chose.) Un exemple sera plus parlant. c’est-à-dire qu’elle ne modifie pas son objet. — La fin du chapitre peut être considérée comme difficile. en plaçant const derrière les parenthèses de sa déclaration et de sa définition... On pourrait se demander si toutes ces complications sont bien nécessaires. } void f ( c o n s t o b j& o ) { o . Il ne faut pas le vivre comme une corvée de plus. Constructeurs et Destructeurs .. Ceci a deux effets importants : — Découverte de bugs à la compilation. (On pensait qu’un objet n’était pas modifié et il l’est. — comme les constructeurs. Le destructeur : — est appelé quand l’objet meurt. Le compilateur va ensuite vérifier pour nous la cohérence de ce const avec tout le reste. }. mais comme une façon de préciser sa pensée : "suis-je ou non en train d’ajouter une méthode qui modifie l’objets ?". 140 .7. void o b j : : g ( ) c o n s t { . En réalité. Il est toutefois recommandé de la comprendre. / / OK! Méthode c o n s t a n t e } Cela n’est finalement pas compliqué : On précise qu’une méthode est constante.3 : 2. notre point de départ étant juste le passage rapide de paramètres en utilisant les références.

i ++) { obj b . } void f ( o b j d ) { } obj g ( ) { obj e . } obj : : ~ obj ( ) { cout << " d e s t " .. 141 . ~obj ( ) .10.7. return 0. } Il affiche maintenant : 0 obj 1 obj 2 dest obj 3 dest obj 4 dest dest 5 obj 6 dest dest dest Repérez bien à quel moment les objets sont détruits. cout << 5 << " " . f o r ( i n t i = 2 . obj : : obj ( ) { cout << " o b j " . Constructeurs et Destructeurs 10. }. Destructeur # i n c l u d e <iostream > using namespace s t d . c l a s s obj { public : obj ( ) . a=g ( ) . cout << 6 << " " . } f (a ). Constatez aussi qu’il y a des appels au destructeur pour les objets qui sont construits par copie et pour lesquels nous n’avons pas encore parlé du constructeur. cout << 1 << " " .. } i n t main ( ) { cout << 0 << " " . obj a . cout << i << " " . return e . i <=4.

8 10. 10. — Est utilisé évidemment par : obj a.. Notre programme exemple est enfin complet. // b à partir de a — Mais aussi par : obj a. i f ( a==b ) { o b j ∗ t =new o b j [ n ] . Dans le cas d’un tableau dynamique...8. } / / n appels à obj () / / n appels à ~obj ( ) . Il s’agit d’un constructeur prenant en paramètre un autre objet. Il n’a rien de mystérieux. c’est au moment du delete [] que les destructeurs sont appelés (avant la désallocation du tableau !). delete [ ] t .10. en général en référence constante. Destructeurs et tableaux 10. // ceci n’est pas un constructeur! — Et aussi pour construire les paramètres des fonctions et leur valeur de retour. } { 142 . synonyme de b(a) à ne pas confondre avec : obj a. b=a. En rajoutant : o b j : : o b j ( c o n s t o b j& o ) cout << " copy " . 1 2 3 4 i f ( a==b ) { obj t [ 1 0 ] . L’utiliser ici a pour conséquence de bien désallouer le tas. Ainsi..9 Constructeur de copie Voyons enfin ce fameux constructeur. obj b=a. Le constructeur de copie : — Se déclare : obj::obj(const obj& o). Attention : il est possible d’écrire delete t sans les []. Constructeurs et Destructeurs Destructeurs et tableaux Le destructeur est appelé pour tous les éléments du tableau. . // b à partir de a. mais d’oublier d’appeler les destructeurs sur les t[i] .b. } appellera 10 fois le constructeur vide en ligne 2 et dix fois le destructeur en ligne 4. obj b(a). . C’est une erreur ! Cette syntaxe est réservée à une autre utilisation du new/delete.

Ainsi a=b.10 Affectation Il reste en fait une dernière chose qu’il est possible de reprogrammer pour un objet : l’affectation. obj a . / / OK c a r a =( b=c ) ou même ainsi. se lit a. Affectation il affiche : 0 o b j 1 o b j 2 d e s t o b j 3 d e s t o b j 4 d e s t copy d e s t 5 o b j 6 copy d e s t dest dest Nous avons enfin autant d’appels aux constructeurs qu’au destructeur ! Il reste malgré tout à savoir une chose sur ce constructeur.. Si l’affectation n’est pas reprogrammée. on a recours à l’opérateur =. entre trois entiers marche pour deux raisons : — Elle se lit a=(b=c). return o . et il affiche : 0 o b j 1 o b j 2 d e s t o b j 3 d e s t o b j 4 d e s t copy d e s t 5 o b j 6 copy d e s t = dest dest On raffine en général un peu. } . le constructeur par copie recopie tous les champs de l’objet à copier dans l’objet construit. — L’instruction b=c affecte c à b et retourne la valeur de c Pour pouvoir faire la même chose entre trois objets. c . mais que nous préconisons car cela évite de recopier un objet au moment du return : c o n s t o b j& o b j : : o p e r a t o r =( c o n s t o b j&o ) { cout << " = " . return o .10.operator=(b) si jamais celui-ci existe. dont nous comprendrons l’importance par la suite : Lorsqu’il n’est pas programmé explicitement.10. Rajoutons donc : void o b j : : o p e r a t o r =( c o n s t o b j&o ) { cout << " = " . Remarquez aussi que lorsqu’on définit soi-même un constructeur. } 143 .. Constructeurs et Destructeurs 10. L’instruction a=b=c. ce qui dépasse nos connaissances actuelles. } à notre programme. on reprogrammera plutôt l’affectation ainsi : o b j o b j : : o p e r a t o r =( c o n s t o b j&o ) { cout << " = " . a=b=c . alors elle se fait naturellement par recopie des champs. b . Pour la reprogrammer. le constructeur vide par défaut n’existe plus mais le constructeur de copie par défaut existe toujours ! 10.

b . et qu’il est donc en général indispensable de reprogrammer. n’existe plus dès qu’on définit un autre constructeur. void l i b e r e ( ) . Nous allons enfin découvrir à quoi ça sert. Constructeurs et Destructeurs . Objets avec allocation dynamique 10.11 Objets avec allocation dynamique Tout ce que nous venons de voir est un peu abstrait. c l as s vect { int n. lui. } 144 . t =new double [ n ] .. public : void a l l o u e ( i n t N) . return 0.11. } i n t main ( ) { vect v . } void v e c t : : l i b e r e ( ) { delete [ ] t . Considérons le programme suivant : # i n c l u d e <iostream > using namespace s t d . Contrairement au constructeur vide. }. obj a . libere ( ) . / / OK c a r a =( b=c ) Un dernier conseil : Attention à ne pas abuser ! Il n’est utile de reprogrammer le constructeur par copie et l’opérateur d’affectation que lorsqu’on veut qu’ils fassent autre chose que leur comportement par défaut a ! a...10. qui. même pour reproduire son comportement par défaut 10. c . v.. alloue ( 1 0 ) . a=b=c . double ∗ t . void v e c t : : a l l o u e ( i n t N) { n=N. v . .

2 Problèmes ! Le malheur est que cette façon de faire va nous entraîner assez loin pour des débutants. c l as s vect { int n. Nous allons devoir affronter deux types de problèmes.. Objets avec allocation dynamique Construction et destruction Il apparaît évidemment que les constructeurs et les destructeurs sont là pour nous aider : # i n c l u d e <iostream > using namespace s t d . Rajoutons par exemple un constructeur vide : vect : : vect ( ) { } alors la destruction d’un objet créé à vide va vouloir désallouer un champ t absurde. nous pouvons enfin laisser les allocations et les désallocations se faire toutes seules ! 10. t =new double [ n ] . } vect : : ~ vect ( ) { delete [ ] t . Un problème simple Puisqu’il n’y a qu’un seul destructeur pour plusieurs constructeurs. il va falloir faire attention à ce qui se passe dans le destructeur.11. } Grâce aux constructeurs et au destructeur. public : v e c t ( i n t N) . ~vect ( ) . Il faudra donc faire. double ∗ t .1 10.10. return 0.11. par exemple : 145 . . v e c t : : v e c t ( i n t N) { n=N.. Constructeurs et Destructeurs 10.11. } i n t main ( ) { vect v ( 1 0 ) . }.

Ne pas désallouer provoque évidemment des fuites de mémoire. Il suffit de rajouter un test (&v==this) pour repérer ce cas.11.3 Solution ! Des problèmes identiques se posent pour le constructeur de copie. v et w se retrouvent avec les mêmes champs t ! Non seulement ils iront utiliser les mêmes valeurs.10. } Cette version ne marche d’ailleurs pas si on fait v=v car alors v est désalloué avant d’être recopié dans lui-même. } Pourquoi ? Parce que l’affectation par défaut recopie les champs de v dans ceux de w. n . en factorisant le travail à faire dans quelques petites fonctions privées. } Des problèmes compliqués Le programme suivant ne marche pas : i n t main ( ) { v e c t v ( 1 0 ) . i ++) t [ i ]= v .. ce qui n’est pas trivial. i <n .w( 1 0 ) . ce qui nous dépasse un petit peu.. w=v . C’est le cas en mode Debug sous Visual. i f (n!=0) { t =new double [ n ] . Désallouer deux fois provoque dans certains cas une erreur. } return v . } vect : : ~ vect ( ) { i f (n!=0) delete [ ] t . Ceci dit. Constructeurs et Destructeurs vect : : vect ( ) { n=0. Objets avec allocation dynamique 10. / / R e a l l o c a t i o n e t r e c o p i e f o r ( i n t i = 0 . On décide en général de réallouer la mémoire et de recopier les éléments du tableau : c o n s t v e c t& v e c t : : o p e r a t o r =( c o n s t v e c t& v ) { i f (n!=0) d e l e t e [ ] t . 146 . mais en plus une même zone du tas va être désallouée deux fois. return 0. 10. / / On s e d e s a l l o u e s i n e c e s s a i r e n=v . d’où certainement des résultats faux. ce qui aide à repérer les bugs ! 4. tandis qu’une autre ne le sera pas 3 ! Il faut alors reprogrammer l’affectation. la solution n’est 3.11... t [ i ] . ce qui provoque une lecture dans une zone qui vient d’être désallouée 4 . Du coup.

void copy ( c o n s t v e c t& v ) .n ) . Nous vous la soumettons en bloc. Doit-on recopier les tableaux ? Les partager en faisant en sorte que le dernier utilisateur soit chargé de désallouer ? Etc. / / constructeurs supplémentaires v e c t ( i n t N) . 147 . Elle peut même servir de schéma pour la plupart des objets similaires 5 : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 # i n c l u d e <iostream > using namespace s t d . Objets avec allocation dynamique pas si compliquée. } void v e c t : : copy ( c o n s t v e c t& v ) { alloc (v. f o r ( i n t i = 0 . void k i l l ( ) . void v e c t : : a l l o c ( i n t N) { n=N. } 5.11. Ceci n’est que le premier pas vers une série de façon de gérer les objets. t [ i ] . }. c l a s s vect { / / champs int n. i f (n!=0) t =new double [ n ] . // destructeur ~vect ( ) . i ++) / / OK même s i n==0 t [ i ]= v . public : // constructeurs " obligatoires " vect ( ) . } vect : : vect ( ) { alloc (0).10. etc. v e c t ( c o n s t v e c t& v ) . // affectation c o n s t v e c t& o p e r a t o r =( c o n s t v e c t& v ) . } void v e c t : : k i l l ( ) { i f (n!=0) delete [ ] t . // fonctions privées void a l l o c ( i n t N) . i <n . double ∗ t . Constructeurs et Destructeurs 10.

b= f ( b ) . } / / Pour t e s t e r c o n s t r u c t e u r d e c o p i e vect f ( vect a ) { return a .11. } vect : : ~ vect ( ) { kill (). return 0. } / / Pour t e s t e r l e r e s t e i n t main ( ) { vect a . a=b . } v e c t : : v e c t ( i n t N) { a l l o c (N) . b ( 1 0 ) . Objets avec allocation dynamique 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 10. a=c . a= f ( a ) . a=a . } return v .10. copy ( v ) . } c o n s t v e c t& v e c t : : o p e r a t o r =( c o n s t v e c t& v ) { i f ( t h i s !=&v ) { kill (). Constructeurs et Destructeurs v e c t : : v e c t ( c o n s t v e c t& v ) { copy ( v ) . a=d .d . c ( 1 2 ) . } 148 .

10. Objets avec allocation dynamique 149 . Constructeurs et Destructeurs 10.11.

Ctrl+F — Step out : Maj+F11 — Gest..3.) } for (int j=.3.12. — Opérateurs : vect operator+( vect A. Fonctions — — Définition : int plus(int a..2f. } ..y). // OK . — tièrement public (→ cf obreturn c. } while(!ok). } string s="hop". Clavier float y=1. } void g(const obj& x){ f(x). int j=2. while(i<=100) { — Constantes : ..) { //i=k. } void afficher(int x.j=g(). } — Définition : int i. // OK! — for(int i=1. Color c..RED)..3). if (x>=w || y>=h) return.j=10. Fiche de référence 10. DrawPoint(x..... int i=f(2).m. } . Constructeurs et Destructeurs Fiche de référence Fiche de référence (1/4) — Affectation : i=2.j>i.j=j-3) int k=3. jets !) } void affiche(int a) { Variables cout << a << endl. // OK! — for (int i=. if (x>0) return 1. double x=12... tâches : Ctrl+Maj+Ech Structures — struct Point { double x. const int m=12. int x=3. — Références : void swap(int& a. swap(x.. — Variables globales : int n.. j=k..c=Red. } . interdit! //saute cas i==j — Types : if (i==j) int i=3. int b).. } int g() { .x=2. . — Déclaration : int k. — Pile/Tas — Retour : int signe(double x) { if (x<0) return -1.y. double hasard(). — Initialisation : — int i=1. 150 Pile des appels Itératif/Récursif Références constantes (pour un passage rapide) : void f(const obj& x){ .5. interdit! . — Surcharge : int hasard(int n). — Step over : F10 complex<double> z(2. i=i+1..y=2. Point a.b=tmp.. — Debug : F5 signed char d=-128.int b). void f() { n=10. — Portée : } int i. float x=float(i)/j.12 10.o=n. Point b={1.j.2. — Step inside : F11 Boucles — Indent : Ctrl+K. int& b){ int tmp=a....4. a. int hasard(int a. — for(int i=1... i=j. int y) { if (x<0 || y<0) return.10. // OK int i=m. bool t=true. unsigned char d=25.i<=10. vect C=A+B. a.vect B) { ..... — Build : F7 unsigned int j=4.int b) { — — Une structure est un objet enint c=a+b. return 0. } — Appel : int f(int a) { . ... — Conversion : int i=int(x).y=3. char c=’A’.. k=l=3. . a=b. — do { j=i. const int s=12. int plus(int a. a. // OK } . continue. }.. .l. int n=5.. if (j>1) { i=i+2.i++) // i=j.y.Blue}.

x=. — Méthodes constantes : } Objets void obj::f() const{ — struct obj { int x.x=.. {{1.. } . y=Y.2.12. // OK int* t=new int[n]. .f(). //OK . A[i][j]=..3)). int x.. // OK — Taille variable : } a_moi(). public: — Constructeur vide : — En paramètre : int z.a_moi()..{4. C=A+B..i<3. // OK point point::operator+( for(int i=0. //OK . .2. obj A. int i[n].x.y[5]. y[i]=2*x[i]. // mon g class point { for(int i=0.i<5.n.i<4...y). ... int Y){ a. c=point(1. }.b=a..obj b(a)..*s..int Y). obj& obj::operator=( A[i+2*j]=. — class obj { ....a_moi(). return *this. int *t.. // OK point b) { t[i]=0.t[3].int n){ obj::~obj() { } t[i]=. x=X.3. .C. obj::obj() { — void init(int t[4]) { void pour_tous(). const obj&o){ — Taille variable (suite) : // C=A.une_autre(B). x=A. t[i]=s[i].3}..i++) void un_autre(obj A). — 2D dans 1D : int main() { — Affectation : int A[2*3]."c"}. x. // OK — En paramètre (suite) : — Destructeur : A.B..paramètres des fonctions void f(int A[2][2]). int A[2][3]= — class obj { // mieux que obj b. // OK — Objets temporaires : int n) { .pour_tous().obj b=a.y. } void obj::pour_tous() { y+b.2) void une_autre(obj A) { delete[] t. } void g(const obj& x){ 151 — Objets avec allocation dynamique automatique : cf section 10. // champ int f(). } t[i]=0.valeur de retour . — Affectation : } int s[3]={1. string s[2]={"ab".i++) point a(2...i++) int j=x+i.B.6}}. } } int main() { — void alloue(int*& t){ obj A.. //NON } — 2D : A.f(). Constructeurs et Destructeurs 10....y.. .. ... A.. } point(int X.. obj a.=y. x=.11 . public: — const int n=5.z=.4}. // méthode .. //OK Utilisé par : int A[2][3].i<n. void a_moi()... } — Définition : int obj::f() { — Constructeur : — double x[5]. obj operator+(obj B). // OK }.. }.i++) z=.operator+(B) . } return point(x+b.j[2*n].. int t[4]={1... — void init(int t[]. +f(point(2. }. . — Constructeur de copie : t=new int[n].3}.3).. //NON obj::obj(const obj& o){ } A. int i=a. int i=g(3). return j. Fiche de référence Fiche de référence (2/4) Tableaux int g(int y). for (int i=0. A. } void obj::a_moi() { obj a. int main() { — Initialisation : point::point(int X.5. ... for(int i=0.2..10. // mon x int x.x. // OK — void f(int* t. . A..x=3.

Imagine++ — Voir documentation. double(RAND_MAX). — Compiler régulièrement. — Erreurs et warnings : cliquer.h". — Ne pas laisser de warning. Fiche de référence 10. — #include <cmath> } double sqrt(double x).size(). — i++. int l=s. j+=3. double sin(double x)... } — Fonctions : déclarations dans le . // Modulo — #include <cstdlib> — Négation : ! . break. — if (i==0) j=1.. — Tableaux : pas pour transcrire une formule mathématique ! — Faire des structures. — #include <ctime> . — Faire des objets.10.. — Utiliser le debuggeur. case 1: string s="hop". char c=s[0].cpp) — Ne pas abuser du récursif. fonctions utiles... — #include <cassert> . — Indenter. — Ne pas trop découper. y=1/x. — Faire des fichiers séparés. — Le . i--. Tests i-=2. Constructeurs et Destructeurs Fiche de référence (3/4) Compilation séparée — #include "vect. définitions dans le Entrées/Sorties ...cpp y break. else j=2.. . j=1. — Ne déclarer dans le .h que les cout <<"I="<<i<<endl... . 152 — #define _USE_MATH_DEFINES #include <cmath> double pi=M_PI. assert(x!=0). default: .cpp — #include <iostream> using namespace std. x=rand()/ — if (i==0) j=1.. double cos(double x).. Conseils — Nettoyer en quittant. — Ne pas toujours faire des objets ! — Penser interface / implémentation / utilisation. — #include <string> — switch (i) { using namespace std.. — Types : définitions dans le . if (t) double acos(double x).h doit suffire à l’utilisateur (qui ne doit pas regarder le .12... — #include <ctime> case 2: s=double(clock()) case 3: /CLOCKS_PER_SEC.. cin >> i >> j. . — #pragma once au début du Divers fichier. — Ne pas oublier delete. k=2. — Combinaisons : && || i=rand()%n.h . compris dans vect. — bool t=(i==0)..h... srand((unsigned int) — if (i==0) { time(0)). — Comparaison : == != < > <= >= — j=i%n... .. — Debug/Release : nettoyer les deux.. — Faire des fonctions. j=1.

y. // NON! double x. // OUI obj* t=new obj[n].i++) int t[2.2). //NON! — #include "vec.13. x=i/j.. int* s=new int[2].3}. delete t. t=f(). // NON! int n=5. — void f(int t[][]).//NON — for (int i=1.i++) — int t[2]. — int* t. // NON point p=point(1.. // On perd s! delete[] t. 153 .. // NON! x=double(i/j). // NON! } if (i=2) // NON! int t[4]. s=t.2.// s est int // et non int* t=new int[n].2}...10. // NON! t={1.. return t. — int t[n].t[3]. // NON! for (int i=0. public: ....3}.. // NON! y[i]=2*x[i]. // NON! int q=r=4.. }.//NON point p(1. // manque [] Devoir écrit Vous pouvez maintenant vous confronter aux devoirs écrits proposés en annexe..y[10]. — Point a.13 int* t=new int[2]..i<=10. Vous avez toutes les connaissances nécessaires.2}.2). } // NON! double x=1/3.y. Devoir écrit Fiche de référence (4/4) — int f()[4] { // NON! — int t[4]. . if (i==2) then // NON! t=s. // NON! int i.// NON! class point { int x. — if i==2 // NON! — int s[3]={1.j. s=new int[n].. delete[] s.s. // NON t[1]=.3].cpp"//NON double x[10]... // NON! Erreurs fréquentes — Pas de définition de fonction dans une fonction ! — — — — — — — 10. point a={2. // NON! — int f() {.i<100.j]=. . //NON t[i. //Déja fait int *t.} — struct Point { int i=f. Constructeurs et Destructeurs 10. // NON! a={1.

.

C’est l’ordre alphabétique qui est évidemment utilisé : if if if if if if ( s1==s1 ) ( s1 ! = s2 ) ( s1 <s2 ) ( s1 >s2 ) ( s1 >=s2 ) ( s1 <=s2 ) . .. s i z e ( ) . Ce chapitre est là pour y remédier.11.. c’est la pratique. . ... Nous commençons avec ce chapitre un tour de tout ce qui est utile et même souvent indispensable et que nous n’avons pas encore vu : chaînes de caractères. c’est-àdire du texte... Après avoir affronté les exercices tout faits. Les chaînes peuvent êtres comparées.. vous réalisez que. Vous constaterez rapidement qu’il vous manque aussi quelques fonctions ou types de variables usuels... Chapitre 11 En vrac... — Vous en connaissez suffisamment pour réaliser de nombreux programmes. livrés à vous-même.. . i n t l =s .. Complétons : 1. s t r i n g s= " hop " .. . il vous est difficile de vous en sortir.. .. 11. mais les fonctions les plus couramment utilisées. Encore une fois. nous ne verrons pas tout de manière exhaustive.. fichiers. En vrac.1 Chaînes de caratères Les chaînes de caractères sont les variables stockant des suites de caractères. Nous les avons déjà rencontrées : # include <string > using namespace s t d . . etc. char c=s [ 0 ] ... Ce qui vous manque en général ici. Alors lancez-vous ! Tentez de programmer l’un des projets proposés sur la page WEB du cours..

/ / J u s q u ’ à " E n t r é e " ou un e s p a c e récupérera "bonjour" comme valeur de s (et éventuellement "les" puis "amis" si l’on programme d’autres cin>>t.11. 1. / / Tout j u s q u ’ à un ’ : ’ ( non c o m p r i s ) 7. Nous n’avons pas encore vu le rôle de const avec les tableaux. Ainsi. 3 ) .. Pour récupérer la ligne complète. ’ : ’ ) . Considérez-le comme un entier mais pour lequel C++ choisit lui-même sur combien d’octets il faut le mémoriser.. 2. c _ s t r ( ) . / / // // position position à partir position de de de 3. — Si le caractère n’est pas trouvé. il faudra faire un g e t l i n e ( cin . ’ h ’ dans s ? ’ h ’ dans s la en i g n o r a n t s [ 0 ] à s [ 2 ] — Attention c’est le type size_t 1 qui est utilisé et non int. / / où e s t " hop " d a n s s à p a r t i r d e l a / / p o s i t i o n 3? 4.. s t r i n g b= " ça va . 3.2. On peut aussi chercher une sous-chaîne : s i z e _ t i =s . On peut chercher un caractère dans un chaîne : s i z e _ t i =s . 5. f i n d ( " hop " ) . 3 ) . // s i z e _ t j =s . c o n s t char ∗ t =s . c_str () pour convertir une variable s de type string (cf section 11.. Ajouter une chaîne à la fin d’une autre : s t r i n g a= " comment " . ou tout simplement proposées par Visual quand vous utiliserez les string. 2. f i n d ( " hop " . Certaines fonctions prennent encore en paramètre un char∗ ou un const char∗ 2 . s t r i n g t x t =a+ " " +b . s . find retourne −1. / / où e s t " hop " d a n s s ? s i z e _ t j =s . il faut utiliser le type string::size_type. le programme : string s . f i n d ( ’ h ’ . s ) .2). Convertir une string en une chaîne au format C : le C mémorise ses chaînes dans des tableaux de caractères terminés par un 0. l e s amis ? " . Attention : la récupération d’une string au clavier coupe la chaîne si l’on appuie sur la touche "Entrée" mais aussi au premier espace rencontré. Vous trouverez d’autres fonctions dans l’aide en ligne de Visual. Il faudra alors leur passer s . / / Toute l a l i g n e jusqu ’ à " Entrée " On pourra éventuellement préciser un autre caractère que la fin de ligne : g e t l i n e ( cin . si l’on tape "bonjour les amis".. 156 . c i n >> s . s t r i n g s= " hop hop " .. Chaînes de caratères 11. En réalité. s t r i n g s2= s t r i n g ( s1 . 4 ) . 3 . f i n d ( ’ h ’ ) .1.). En vrac. Extraire une sous chaîne : s t r i n g s1= " un deux t r o i s " . espaces compris. / / s o u s c h a î n e d e l o n g u e u r 4 / / commençant en s 1 [ 3 ] ( i c i " deux " ) 6.

. 3 << ’ ’ << " s a l u t " << endl . 157 . t x t " ) . double y .2. Le fichier est alors moins volumineux. } On peut aussi avoir besoin de savoir si on est arrivé au bout du fichier : do { .. ou de type ifstream pour lire. 11.. } while ( ! ( g . 3. f . i f s t r e a m g ( " hop .2.. i f ( ! g . i o s : : b i n a r y ) . Voici comment faire : # i n c l u d e <fstream > using namespace s t d .. close ( ) . Une erreur fréquente est de préciser un mauvais nom de fichier : le fichier n’est alors pas ouvert : i f s t r e a m g ( " . w r i t e ( ( c o n s t char ∗ ) x .. On crée simplement une variable de type ofstream pour écrire dans un fichier. o f s t r e a m f ( " hop . t x t " ) . 1. f . int i . En vrac. Il est bon de vérifier que l’ouverture s’est bien passée.. mais très utile à connaître : on peut écrire dans un fichier directement la suite d’octets en mémoire qui correspond à une variable ou un tableau. . Un fichier peut s’ouvrir après construction : ofstream f . double x . o f s t r e a m f ( " hop .. close ( ) . g . 2. 1 0 ∗ s i z e o f ( double ) ) .. bin " .. f << 1 << ’ ’ << 2 . string s .11.1 Principe 11. t x t " ) . / data/hop . g >> i >> x >> s .2 Fichiers 11. Moins fréquent. Fichiers Pour lire et écrire dans un fichier. l’écriture et la lecture plus rapides (pas besoin de traduire un nombre en une suite de caractères ou l’inverse !) double x [ 1 0 ] . e o f ( ) ) . open ( " hop . 4. f . on procède exactement comme avec cout et cin. .. t x t " ) . return 1. is_open ( ) ) { cout << " help ! " << endl .

s ) . Ils ont l’air un peu pénibles à utiliser pour le programmeur habitué au printf et scanf du C. stringstream f . / / Chaîne v e r s e n t i e r f << s . g . / / On é c r i t l a c h a î n e f >> i . s i z e o f ( double ) ) .. Enfin. bin " . . i f s t r e a m g ( " hop . / / Ne p a s o u b l i e r s i on a d é j à u t i l i s é f f << i . En vrac. mais il suffit de recopier ! Voici comment : 3. void l i r e ( s t r i n g nom) { i f s t r e a m f (nom . même chose qu’avec cin : getline (g . s t r i n g s= " 12 " . / / Entier vers chaîne f . c l e a r ( ) . Fichiers 11. } / / Conversion o b l i g a t o i r e .. / / On r e l i t une c h a î n e ( s v a u t " 1 3 " ) 11. s .. read ( ( char ∗ ) x ..2.. On les utilise notamment pour convertir une chaîne en nombre ou l’inverse : # i n c l u d e <sstream > using namespace s t d . s i z e o f ( double ) ) . / / On r e l i t un e n t i e r ! ( i v a u t 1 2 ) i ++.2. Attention à ne pas oublier le "mode d’ouverture" ios :: binary 11.. D’où la conversion.3 Objets et fichiers Le grand intérêt des << et >> 3 est la possibilité de les redéfinir pour des objets ! C’est technique. . read ( ( c o n s t char ∗)&y . un peu technique mais très pratique : les stringstream qui sont des chaînes simulant des fichiers virtuels. On voit ici enfin leur puissance ! 158 . 1 0 ∗ s i z e o f ( double ) ) . close ( ) . ’ : ’ ) . . 2. f . g . g . Pour lire une chaîne avec des espaces. il faut préciser le nom avec une chaîne au format C. f . int i . w r i t e ( ( c o n s t char ∗)&y .2. close ( ) . .2 Chaînes et fichiers 1. / / On é c r i t un e n t i e r f >> s . 3. i o s : : b i n a r y ) . getline (g . Pour ouvrir un fichier.11... c _ s t r ( ) ) .

t x t " ) . / / Appelle f (12 . / / d é c l a r a t i o n void g ( ) { f (12). 11. c i n >> p .11.2). i n t c =0) { // . 159 . ) i s t r e a m& o p e r a t o r > >( i s t r e a m& f .2 . i n t b =0 . i f s t r e a m g ( " . . p o i n t& p ) { f >> p . f << p . . En vrac. } void g ( ) { f (12). y .2).3)..3).2 . / / ou q u o i que c e s o i t d ’ a u t r e ! return f . y . c o n s t p o i n t& p ) { ou q u o i que c e s o i t d ’ a u t r e ! ( on a d é c i d é i c i d ’ é c r i r e l e s deux c o o r d o n n é e s s é p a r é e s p a r un e s p a c e . }. o f s t r e a m f ( " .. t x t " ) . . f (1 .3. } S’il y a déclaration puis définition...0 ..0). on ne précise les valeurs par défaut que dans la déclaration : void f ( i n t a . 11. .. f (10 . ostream& o p e r a t o r < <( ostream& f f << p . Valeurs par défaut s t r u c t point { int x . cout << p ..0)..3. / hop . x << ’ ’ << p . / / Appelle f (1 . / / Appelle f (10 .2 . / hop . } . point p . valeurs qu’ils prendront s’ils ne sont pas précisés à l’appel : void f ( i n t a . g >> p . } . x >> p . .1 Principe Souvent utile ! On peut donner des valeurs par défaut aux derniers paramètres d’une fonction. / / // // return f . i n t b = 0 ) . / / Appelle f (12 . f (10 . / / Appelle f (10 . y .2).0).3 Valeurs par défaut 11.

} void f ( i n t a . } void f ( i n t a . i n t b =0) { / / P r o b l è m e d e s u r c h a r g e ! ... i n t b =3 . En vrac. // .) en f (. i n t b .. true) dans les futurs cas particuliers qui vont se présenter. / / On ne s a u r a p a s r é s o u d r e f ( 1 ) } 11. i n t b ) { / / ne p a s r e −p r é c i s e r i c i l e b p a r d é f a u t ..11. en cinq étapes.. . int b) { .. bool s p e c i a l = f a l s e ) { .. i n t c ) { / / NON! L e s d e r n i e r s p a r a m è t r e s / / Pas c e u x du m i l i e u ! } 2.. Accesseurs 11.. Engendrer des problèmes de surcharge : void f ( i n t a ) { . false ).3 Erreurs fréquentes Voici les erreurs fréquentes lorsqu’on veut utiliser des valeurs par défaut : 1. } Plutôt que de transformer tous les anciens appels à f (. on veut lui rajouter un comportement spécial dans un certain cas : i n t f ( i n t a . il suffit de faire : i n t f ( i n t a .. } 11..3.. } pour laisser les anciens appels inchangés. Vouloir en donner aux paramètres au milieu de la liste : void f ( i n t a .4... 160 . } Puis. ...3..4 Accesseurs Voici.2 Utilité En général.. les points utiles à connaître pour faire des accesseurs pratiques et efficaces.... 11. et uniquement appeler f (. i n t b .. bool s p e c i a l ) { . on part d’une fonction : int f ( int a .

/ / Var . / / OK! Met 3 d a n s i ! Attention : apprendre ça à un débutant est très dangereux. de même qu’on n’écrit pas 2=3 ! En fait.. partant du programme : c l a s s point { double x [N] . qui fait hurler ceux qui comprennent ce qui se passe : int i . point p .. f ( ) = 3 . En vrac. 11. public : void s e t ( i n t i . void p o i n t : : s e t ( i n t i . En général. 2 .4.. f ( ) = 3 .. / / NON! ! ! Le i n ’ e x i s t e p l u s . donc un "lien" vers une variable : int i . f ( ) = 3 .2 Utilisation Même si un objet n’est pas une variable globale.. double v ) .4. Accesseurs Référence comme type de retour Voici une erreur souvent rencontrée. Que va−t ’ i l s e p a s s e r ? ! 11. set ( 1 . } . 3 ) ...1 11.4. le transformer en : 161 . Mais uniquement si la fonction retourne une référence. } ..11. / / Variable globale i n t& f ( ) { return i . p. si ! C’est possible. } . / / Ne v e u t r i e n d i r e ( p a s p l u s que 2=3) On ne range pas une valeur dans le retour d’une fonction. / / r é f é r e n c e v e r s une v a r i a b l e q u i va m o u r i r ! / / C ’ EST GRAVE! } . il se dépêche de commettre l’horreur suivante : i n t& f ( ) { i n t i .. un champ de cet objet ne meurt pas en sortant d’une de ses méthodes ! On peut.. l o c a l e r e t u r n i . double v ) { x [ i ]= v . / / Variable globale int f () { return i . }.

. ce qui est utile par exemple pour les matrices : c l a s s mat { double x [M∗N] ..4 Surcharge et méthode constante Nous sommes maintenant face à un problème : le programme précédent ne permet pas d’écrire : void f ( mat& A) { 162 .. double& p o i n t : : element ( i n t i ) { return x [ i ] .4. } . double& p o i n t : : o p e r a t o r ( ) ( i n t i ) { return x [ i ] . p ( 1 ) = 2 .. } . p . public : double& o p e r a t o r ( ) ( i n t i ) . element ( 1 ) = 2 .. }. 3 . i n t j ) { r e t u r n x [ i +M∗ j ] .11. En vrac. }. / / J o l i . 2 ) = 2 .. i n t j ) . point p . mat A.. 11..3 operator() Etape suivante : ceci devient encore plus utile quand on connaît operator() qui permet de redéfinir les parenthèses : c l a s s point { double x [N] . c l a s s point { double x [N] . public : double& element ( i n t i ) . 11.4. non ? Notez que l’on peut passer plusieurs paramètres. double& mat : : o p e r a t o r ( ) ( i n t i . 3 . 3 . A( 1 . }. } . point p .4. public : double& o p e r a t o r ( ) ( i n t i . Accesseurs 11.

en profitant du fait qu’entre une méthode et une méthode constante. / / OK. donc long. }. public : / / Même nom . i n t j ) c o n s t . ce que nous n’avons pas appris à faire). 11. i n t j ) c o n s t { r e t u r n x [ i +M∗ j ] . / / NON! Le c o m p i l a t e u r ne s a i t p a s que / / c e t t e l i g n e ne m o d i f i e r a p a s A! } car la méthode operator() n’est pas constante.11. il y a surcharge possible. 1 ) = 2 . a p p e l l e l e d e u x i è m e } 11. même si elles ont les mêmes paramètres ! Cela donne : c l a s s mat { double x [M∗N] . Celles-ci sont moins puissantes que les inline car elles ne vérifient pas les types. / / OK } void f ( c o n s t mat& A) { double x=A( 1 . i n t j ) . Pour cela. il faut déclarer la fonction inline.4.x[i+M∗j] est une grande perte de temps : on passe plus de temps à appeler la fonction A. } void f ( mat& A) { A( 1 . Accesseurs A( 1 . Les programmeurs C pourraient aussi être tentés de programmer des "macros" (ie. etc. mêmes p a r a m è t r e s . double mat : : o p e r a t o r ( ) ( i n t i . i n t j ) { r e t u r n x [ i +M∗ j ] . double o p e r a t o r ( ) ( i n t i . Appeler A(i. 1 ) = 2 . En vrac.. j ) et à récupérer sa valeur de retour. } double& mat : : o p e r a t o r ( ) ( i n t i . Le programmeur C++ les utilisera avec parcimonie ! 163 . des raccourcis avec des #define. 1 ) .. a p p e l l e l e p r e m i e r o p e r a t o r ( ) } void f ( c o n s t mat& A) { double x=A( 1 . Il y a heureusement une solution : programmer deux accesseurs. 1 ) .operator()(i . ne permettent pas d’accéder aux champs privés. qu’à exécuter la fonction elle-même ! Cela pourrait nous conduire à retourner aux structures en oubliant les classes ! 4 Il existe un moyen de supprimer ce mécanisme d’appel en faisant en sorte que le corps de la fonction soit recopié dans le code appelant lui-même.4. j ) au lieu de faire A. Par exemple : 4.5 "inline" Principe Dernière étape : appeler une fonction et récupérer sa valeur de retour est un mécanisme complexe. / / OK. m a i s l ’ une e s t ’ c o n s t ’ ! / / Donc s u r c h a r g e p o s s i b l e double& o p e r a t o r ( ) ( i n t i .

il faut bien penser à la mettre dans le ficher . Contrairement à ce qu’il faut faire en Java ! Encore une source de mauvaises habitudes pour le programmeur Java qui se met à C++. en général de type inline. C’est le moment de révéler ce que nous gardions caché : Il est possible de DÉFINIR UNE MÉTHODE ENTIÈREMENT DANS LA DÉFINI - TION DE LA CLASSE . Cependant.cpp.. Voici ce que cela donne en pratique : c l a s s mat { double x [M∗N] . Cas des méthodes Dans le cas d’une méthode. ce qui ralentit la compilation et augmente la taille du programme ! — inline est donc réservé aux fonctions courtes pour lesquelles l’appel est pénalisant par rapport au corps de la fonction ! — Si la fonction était déclarée dans un . En vrac. } .11. au lieu de seulement l’y déclarer puis placer sa définition en dehors de celle de la classe.h et définie dans un . ralentit la compilation et va à l’encontre de l’idée qu’il faut masquer le contenu des méthodes à l’utilisateur d’une classe. Accesseurs 11. i n t j ) c o n s t { r e t u r n x [ i +M∗ j ] . C’est donc RÉSERVÉ AUX PETITES FONCTIONS. } }.4. sans qu’il n’y ait d’appel de fonction ! Précautions Bien comprendre ce qui suit : — Une fonction inline est recompilée à chaque ligne qui l’appelle. 164 .h car l’utilisateur de la fonction a besoin de la définition pour remplacer l’appel de la fonction par son corps ! — Pour pouvoir exécuter les fonctions pas à pas sous debuggeur. double y= s q r ( z − 3 ) . les fonctions inline sont compilées comme des fonctions normales en mode Debug. } i n l i n e double o p e r a t o r ( ) ( i n t i . i n t j ) { r e t u r n x [ i +M∗ j ] . ceci n’est pas obligatoire a . a.h si la classe était définie en plusieurs fichiers. i n l i n e double s q r ( double x ) { r e t u r n x∗x . Seul le mode Release profitera donc de l’accélération... public : i n l i n e double& o p e r a t o r ( ) ( i n t i .... fait exactement comme si on avait écrit y=(z−3)(z−3). il faut maintenant la mettre entièrement dans le .

Comme ceci : enum Code { C10 =200 . Voici par exemple comment rendre sûrs nos accesseurs : # include <cassert > c l a s s mat { double x [M∗N] . 11. 11. } i n l i n e double o p e r a t o r ( ) ( i n t i .6 Types énumérés C’est une bonne idée de passer par des constantes pour rendre un programme plus lisible : c o n s t i n t nord =0 .6. masque des entiers.. il ne faut pas se priver de l’utiliser. Une précision : on peut forcer certaines valeurs si besoin. i n t j ) c o n s t { a s s e r t ( i >=0 && i <M && j >=0 && j <N) . mais il est maladroit de faire ainsi ! Il vaut mieux connaître l’existence des types énumérés : enum Dir { nord . sud . sud =2 . Il ne faut pas hésiter à s’en servir car elle facilite la compréhension du code (répond à la question “quels sont les présupposés à ce point du programme ?”) et facilite le diagnostic des erreurs. o u e s t } . public : i n l i n e double& o p e r a t o r ( ) ( i n t i . C12 =240 . / / Vaudra 241 C14 } . En vrac. qui. } }. C’est tout pour aujourd’hui ! Nous continuerons au prochain chapitre.11.. Sachant qu’elle ne coûte rien en mode Release (car non compilée).5. o u e s t = 3 . C13 . C11 =231 . r e t u r n x [ i +M∗ j ] .. en réalité. i n t j ) { a s s e r t ( i >=0 && i <M && j >=0 && j <N) .5 11. void avance ( Dir d i r e c t i o n ) . Assertions Assertions Rappelons l’existence de la fonction assert () vue en 7. r e t u r n x [ i +M∗ j ] . Il s’agit bien de définir un nouveau type.. e s t =1 . Il est donc temps de retrouver notre célèbre fiche de référence. 165 . void avance ( i n t d i r e c t i o n ) . / / " 242 — Voilà. e s t .

En vrac.. 166 .. Types énumérés 11.11.6.

Color c..7 11. 11. if (t) j=1. — Tableaux : pas pour transcrire une formule mathématique ! — Faire des structures... Point a. // OK! if (j>1) { int k=3. .est.ouest}.. // OK! } //i=k. — int i=1. while(i<=100) { .. — Debug/Release : nettoyer les deux.. a..j. Fiche de référence Fiche de référence Fiche de référence (1/4) Boucles — do { . i=i+2.. — Initialisation : int n=5. — Utiliser le debuggeur. else j=2. — Affectation : i=2.j=10. — if (i==0) { j=1. } while(!ok). — Erreurs et warnings : cliquer. — Portée : int i. — Négation : ! — #include <cassert> .y=3. int k.. // OK .11.. complex<double> z(2. Tests — Compiler régulièrement. float x=float(i)/j. . — Variables globales : int n.. case 2: case 3: ...2f..i<=10.j=j-3) . — Constantes : const int s=12. string s="hop". — Une structure est un objet entièrement public (→ cf objets !) Variables — Définition : int i.x=2. } — bool t=(i==0).Ctrl+F Step out : Maj+F11 Gest. — Indenter. break.... interdit! int j=2. 167 — Penser interface / implémentation / utilisation.l.. char c=’A’. assert(x!=0). — if (i==0) j=1. — for(int i=1.Blue}. j=i. unsigned char d=25. — for (int i=. const int m=12. bool t=true. } — for(int i=1. void f() { n=10. tâches : Ctrl+Maj+Ech Structures — struct Point { double x. a.3). — Faire des fonctions. double x=12..y...cpp) — Pile/Tas — Ne pas abuser du récursif. — Combinaisons : && || — Faire des objets..3.) { //saute cas i==j if (i==j) continue. — switch (i) { case 1: .2. — Ne pas toujours faire des objets ! — Comparaison : == != < > <= >= — if (i==0) j=1.. // OK int i=m.m. k=2. sud.. Point b={1...5. — Ne pas oublier delete..i++) .3. — Faire des fichiers séparés.. }. interdit! — Types : int i=3.o=n. ..) for (int j=.4. — Le . — Conversion : int i=int(x). a. — Ne pas laisser de warning. float y=1. unsigned int j=4. } Conseils — Nettoyer en quittant.c=Red.. En vrac.j>i.. j=k. break.h doit suffire à l’utilisateur (qui ne doit pas regarder le . void avance(Dir d).. } Clavier — Build : F7 — Debug : F5 — Step over : F10 — — — — Step inside : F11 Indent : Ctrl+K.. // i=j. y=1/x. i=i+1.7.. k=l=3. . default: . — Type énuméré : enum Dir{nord. signed char d=-128. i=j.

. — Opérateurs : string s[2]={"ab".RED).i++) t[i]=s[i].i<n. // f(12..int n){ } t[i]=. A[i+2*j]=...3}. // OK } for (int i=0... int t[4]={1. } — Appel : int f(int a) { ...2).vect B) { — Affectation : int s[3]={1. } A[i][j]=. int x=3.. } — Taille variable : Valeurs par défaut : int* t=new int[n].j[2*n].. } . — En paramètre : — void init(int t[4]) { for(int i=0. .. } void afficher(int x.. Pile des appels Itératif/Récursif Références constantes (pour un passage rapide) : void f(const obj& x){ . } — void init(int t[]. int i=f(2). return i.. — return c..i<3. void f(int a. int y) { if (x<0 || y<0) return..int b=0). .5.cpp — const int n=5. if (x>0) return 1. f()=3. vect A.j=g(). — Référence en retour : int i.. f(12).y[5]..b=tmp.int b) { } // ..11. return 0.. vect C=A+B.// f(10.h. — #pragma once au début du vect operator+( fichier. Inline (appel rapide) : } inline double sqr( double x) — { 2D : return x*x. 168 . DrawPoint(x.n.. void g() { delete[] t.6}}. — Ne déclarer dans le .y=2.*s. globale — 2D dans 1D : int& f() { int A[2*3]. compris dans vect.0).i<5. Fiche de référence 11... — Références : void swap(int& a. } — void alloue(int*& t){ t=new int[n]... for(int i=0. // Var..2.i<4..2. — En paramètre (suite) : f(10.7.h int i[n].4}. a=b. int& b){ int tmp=a. int *t.int b) { int c=a+b.3}.i++) t[i]=0. définitions dans le y[i]=2*x[i]. double hasard().{4. // i=3! Compilation séparée Tableaux — Définition : — #include "vect. — } void affiche(int a) { — cout << a << endl. } void g(const obj& x){ f(x). swap(x. } — Déclaration : int plus(int a."c"}. if (x>=w || y>=h) — return. void f(int a. int hasard(int a. — Surcharge : int hasard(int n).h que les — Initialisation : fonctions utiles. {{1.h".y).t[3]. } . } — Taille variable (suite) : .2. } . int A[2][3]. .. Fiche de référence (2/4) Fonctions — Définition : int plus(int a..2). int b)..i++) — Fonctions : déclarations dans le .cpp y — double x[5]. void f(int A[2][2])..3. — void f(int* t. .. — Retour : int signe(double x) { if (x<0) — return -1..int b). int A[2][3]= double y=sqr(z-3). int n) { for(int i=0. En vrac.. } int g() { . — Types : définitions dans le .. — Ne pas trop découper.i++) t[i]=0.y.

.une_autre(B). // OK . } } .. } double operator() (int i..3)... public: int z.int j){ . — c=point(1.obj b(a). k=s. obj a. s2=string(s1.). . x=rand()/ double(RAND_MAX). // OK } void une_autre(obj A) { — x=A. void a_moi().f(). size_t i=s.. ..x.s. // OK A. . j+=3.. return x[i+M*j]. .3..obj b=a. // OK } void obj::pour_tous() { — x=. //OK A. obj::obj() { — class obj { int x.. y=Y.. obj& obj::operator=( — class obj { txt=a+" "+b.s). Destructeur : obj::~obj() { .’:’). }. obj::obj(const obj& o){ int l=s..11. int obj::f() { point::point(int X..y).. A. Affectation : b="ça va?". // Modulo — #include <cstdlib> ..3).f(). obj a. } . // mon x x=X.=y.. double cos(double x). string s="hop".x=3. getline(cin. ..C. // OK (int169 i. //OK — ... . // méthode public: int g(int y). // champ int x. a. i-=2. // mieux que obj b. } .B.. int main() { obj A. // mon g int Y){ int j=x+i... //OK A.find(’h’).x. — #define _USE_MATH_DEFINES } public: #include <cmath> void g(const obj& x){ double& operator() double pi=M_PI.y.... Divers — i++. s1="un deux trois". — void pour_tous().B. — Objets avec allocation dynaconst char *t=s. } .3))...x=.. //NON A..z=..int Y).. //NON A. } int main() { getline(cin.find("hop")....4). }. — Constructeur vide : int i=a. .size().pour_tous(). — j=i%n. } #include <ctime> .find(’h’. — #include <string> using namespace std.y..7.valeur de retour a="comment".paramètres des fonctions l=s.. return j. .int j)const{ assert(i>=0 . } if (s1!=s2) .. return *this. // OK z=. obj A.c_str(). C=A+B.. i=rand()%n. int f(). if (s1==s1) . return x[i+M*j].. const obj&o){ obj operator+(obj B).a_moi()....find("hop". // OK a_moi(). x. mique automatique : cf sec// C=A. Constructeur de copie : char c=s[0]. Utilisé par : if (s1<s2) . void un_autre(obj A)... 11. y+b.a_moi(). int main() { point a(2.. point(int X..).. Objets temporaires : point point::operator+( point b) { — return point(x+b. // OK } . int i=g(3).2) +f(point(2... srand((unsigned int) time(0)). i--. double *x. #include <cmath> double sqrt(double x).. double acos(double x).3). double sin(double x). }. void obj::a_moi() { x=.11 — #include <ctime> — Méthodes constantes : — Accesseurs : s=double(clock()) void obj::f() const{ class mat { /CLOCKS_PER_SEC. En vrac.. j=s. .b=a. }. Fiche de référence Fiche de référence (3/4) Objets } — Constructeur : — struct obj { class point { int x.operator+(B) tion 10. assert(i>=0 .

— int f()[4] { // NON! g. — void f(int a. .i++) ofstream f. — int t[2].txt").// s est int int i. // NON! // Chaîne vers entier — struct Point { f << s. f... int t[2. using namespace std.y[10]. // Entier vers chaîne Entrées/Sorties — — — — — — — 170 .//NON ostream& f. #include <sstream> t=s. g.2).7.. public: if (i==2) then // NON! } while (!(g. // NON! } ofstream f("hop. #include <fstream> — int* t. return f.3}.// NON! — int q=r=4. // NON! — obj* t=new obj[n].. t[i.y.3.point& p){ int* s=new int[2]. void f(int a).int b=0). t=new int[n].. return f. s=new int[n].j]=.y.c_str()). // OUI int i.3]. return t. a={1.eof())..write((const char*)x. — int f() { sizeof(double)). //NON ifstream g("hop..y. s=t. int i=f.. istream& operator>>( — int* t=new int[2]. istream& f. // NON! f. . // NON double x[10].2}. // HORREUR! string s.} point a={2.. // NON! const point&p) { cin >> i >> j..bin". // NON! } } f()=3. } Erreurs fréquentes — int *t. point p(1. // NON! return i. for (int i=1. //Déja fait return 1. // et non int* — Pas de définition de fonction double x. dans une fonction ! g >> i >> x. }. — #include "vec.. //NON! delete t. int t[4].2).j. f>>p. } // NON! — Voir documentation.. — int& f() { — int s[3]={1. // NON! g. — for (int i=0.2}. // On perd s! ifstream g("hop.close().i<100. // NON! ofstream f("hop. f. — void f(int t[][]). int i. y[i]=2*x[i]. En vrac. ifstream f(s..read((char*)x. — //NON! sizeof(double)). // NON g.//NON ios::binary). if i==2 // NON! .i<=10.. — Ne pas tout mettre inline ! 10*sizeof(double))...3}. f << i..read((const char*)&y.t[3].s.. Fiche de référence 11. // NON! f()=3.cpp"//NON — ostream& operator<<( . — class point { — if (i=2) // NON! do { int x..11.txt"). x=double(i/j).int b)..bin". . using namespace std.y.i++) void f(int a=2.open("hop.write((const char*)&y.x>>p. // NON! — point p=point(1. x=i/j. ios::binary). — int n=5. // NON! #include <iostream> f >> s.close(). t={1. Imagine++ double x. // manque [] f.x<<’ ’<< p.y.close(). — int f() {. using namespace std.. if (!g.close(). — Point a. — double x[10]. t[1]=.txt").2.is_open()) { } delete[] s.. — double x=1/3. t=f(). // NON! f<<p. } stringstream f. f << 1 << ’ ’ << 2.clear(). int t[4]. delete[] t.. cout <<"I="<<i<<endl. 10*sizeof(double)). f. f >> i. Fiche de référence (4/4) f. ..// NON! int t[n].

1&0.1 Opérateur binaires Parmi les erreurs classiques. En vrac (suite) . mais nous nous limiterons toujours à quelques aspects pratiques du C++ souvent utilisés donc souvent rencontrés dans les programmes des autres ! La fin du chapitre est plus utile et plus fondamentale : nous y abordons les template. Cette erreur n’est possible que parce que & existe. Par exemple : 13&10 vaut 8 car en binaire 1101&1010 vaut 1000. Il est défini ainsi : effectuer a&b revient à considérer l’écriture de a et de b en binaire puis à effectuer un "ET" bit par bit (avec la table 1&1 donne 1 . qui range 0 dans i puis considère 0 comme un booléen..... ou programmation générique. au lieu de i f ( i ==0 && j ==2) ..12. c’est à dire false ... il y a évidemment celle qui consiste à remplacer i f ( i ==0) .. par i f ( i =0) / / NON! ! ! . les structures de données de la STL (Standard Template Library) nécessiteront la compréhension des template. Nous continuons dans ce chapitre un inventaire de diverses choses utiles. Parmi elles. Chapitre 12 En vrac (suite) . le début de ce chapitre sera un peu en vrac. 12. Il s’agit de opérateur binaire "ET" sur des entiers.. 0&1 et 0&0 donnent 0).. Il existe ainsi toute une panoplie d’opérateurs binaires : .. Une autre erreur fréquente consiste à écrire i f ( i ==0 & j ==2) / / NON! ! ! . Nous aborderons donc cet aspect intéressant du C++ — Toujours sous forme d’un inventaire..

Ainsi.n)) !) calcule i/2 rapidement calcule i/2n rapidement calcule i%256 rapidement (idem pour toute puissance de 2) Valeur conditionnelle Il arrive qu’on ait à choisir entre deux valeurs en fonction du résultat d’un test.. alors ~i vaut 242.12. tout cela ne sert pas à faire joli ou savant. mais à manipuler les nombres bit par bit. Une construction utile est alors : ( t e s t )? val1 : val2 qui vaut val1 si test est vrai et val2 sinon. — Le fait que a^b existe est aussi source de bugs (il ne s’agit pas de la fonction puissance !) — Le résultat de ~ dépend en fait du type : si par exemple i est un entier non signé sur 8 bits valant 13. Valeur conditionnelle symbole utilisation & a&b | a|b ^ a^b >> a>>n << a<<n ~ ~a 12. il arrive souvent qu’on utilise un int pour mémoriser un certain nombre de propriétés en utilisant le moins possible de mémoire avec la convention que la propriété n est vraie ssi le neme bit de l’entier est à 1.. 172 . car ~00001101 vaut 11110010. 1 sinon 1^0=0^1=1. En vrac (suite) . Voici comment on utilise les opérateurs ci-dessus pour manipuler les bits en question : i|=(1<<n) i&=~(1<<n) i^=(1<<n) if ( i&(1<<n)) passe à 1 le bit n de i passe à 0 le bit n de i inverse le bit n de i vrai ssi le bit n de i est à 1 Il existe aussi d’autres utilisations fréquentes des opérateurs binaires. mais pour des raisons de rapidité : (1<<n) ( i>>1) ( i>>n) ( i&255) 12. non pour des raisons de gain de place. Ainsi i f ( x>y ) maxi=x . ~0=1 exemple 13&10=8 13|10=15 13^10=7 13>>2=3 5<<2=20 ~13=−14 Remarques : — Ces instructions sont particulièrement rapides car simples pour le processeur.2. 0 sinon décale les bits de a n fois vers la droite et comble à gauche avec des 0 (les n premiers de droite sont perdus) décalage à décale les bits de a n fois vers la gauche gauche et comble à droite avec des 0 complément ~1=0. nom et ou ou exclusif décalage à droite résultat 1&1=1. En pratique. Un seul entier de 32 bits pourra par ainsi mémoriser 32 propriétés là où il aurait fallu utiliser 32 variables de type bool..2 vaut 2n (sinon il faudrait faire int (pow(2. 0 sinon 0|0=0. else maxi=y .

. i f (D) a r r e t e r=true . . . Boucles et break pourra être remplacé par : maxi =( x>y ) ? x : y . Ainsi le programme : bool a r r e t e r = f a l s e . . } Questions récurrentes des débutants : 1. break ne sort que de la boucle courante.. i ++) { A. pas des boucles autour : 173 .. . i f (B) a r r e t e r=true . la commande break sort de la boucle en ignorant tout ce qu’il restait à y faire. else { C.) { .12. } } } devient de façon plus lisible et plus naturelle : f o r ( i n t i = 0 . Il ne faut pas abuser de cette construction sous peine de programme illisible ! 12.. Très utile aussi. i f (B) break .. f o r ( i n t i = 0 . E. C. ) . i ++) { A. else { E.... ) break . 12.. if ( .4 l’instruction continue qui saute la fin d’une boucle et passe au tour d’après. / / NON! ! ! Ne s o r t p a s du i f ! ( m a i s é v e n t u e l l e m e n t / / d ’ un f o r q u i s e r a i t a u t o u r .3. i f (D) break .3 Boucles et break Nous avons déjà rencontré à la section 8. break ne sort pas d’un if ! if (. } 2. i <N. En vrac (suite) . i <N && ! a r r e t e r .

srand ( ( unsigned i n t ) time ( 0 ) ) . 3. . srand ( ( unsigned i n t ) time ( 0 ) ) . 12. Variables statiques 12. .. } r e t u r n double ( rand ( ) ) /RAND_MAX... a v e c s a v a r i a b l e g l o b a l e / / masquée à l ’ i n t é r i e u r double random ( ) { s t a t i c bool f i r s t = t r u e . j <M. } . i <N. double random ( ) { if ( first ) { first=false . ce qui dans l’exemple précédent donnerait un comportement non désiré ! 174 . j ++) { ... while de la même façon qu’avec for. } r e t u r n double ( rand ( ) ) /RAND_MAX.. / / t e r m i n e l a b o u c l e en j e t p a s s e donc / / en l i g n e 10 ( p a s en l i g n e 1 2 ) ..4 Variables statiques Il arrive souvent qu’on utilise une variable globale pour mémoriser de façon permanente une valeur qui n’intéresse qu’une seule fonction : / / F o n c t i o n random q u i a p p e l l e s r a n d ( ) t o u t e s e u l e / / au p r e m i e r a p p e l . / / Ne p a s o u b l i e r s t a t i c ! if ( first ) { first=false . } Le danger est alors que tout le reste du programme voie cette variable globale et l’utilise ou la confonde avec une autre variable globale. bool f i r s t = t r u e ..4.. Une variable locale mourrait à la sortie de la fonction. f o r ( i n t i = 0 ... } Attention : il s’agit bien d’une variable globale et non d’une variable locale.. Il est possible de cacher cette variable dans la fonction grâce au mot clé static placé devant la variable : / / F o n c t i o n random q u i a p p e l l e s r a n d ( ) t o u t e s e u l e / / au p r e m i e r a p p e l . if ( . . .12.. } . .. ) break . f o r ( i n t j = 0 . i ++) { . break et continue marchent évidemment avec while et do . . En vrac (suite) .

} .... on ne pourrait assurer cette préservation des valeurs : void f ( i n t t [ 4 ] ) { . 12... toujours grâce à static . } 175 .. / / ne m o d i f i e p a s a [ ] .12.. i n t& b ) { i n t tmp .. 12. f (a ). Cette possibilité de préciser qu’un tableau ne peut être modifié est d’autant plus importante qu’un tableau est toujours passé en référence : sans le const.1 Principe Considérons la fonction classique pour échanger deux variables : void echange ( i n t& a . const signifie que ce sont les éléments du tableau qui ne peuvent être modifiés. } void h ( c o n s t i n t ∗ t . const et tableaux NB : Il est aussi possible de cacher une variable globale dans une classe. int a [ 4 ] . / / ne m o d i f i e p a s a [ ] h ( a . b=tmp . En vrac (suite) .. 4 ) .6. 12. / / m o d i f i e p e u t −ê t r e a [ ] g(a ) ..6 template 12.. } void g ( c o n s t i n t t [ 4 ] ) { . a=b . tmp=a . i n t n ) { .5 const et tableaux Nous avons vu malgré nous const char ∗ comme paramètre de certaines fonctions (ouverture de fichier par exemple). Nous ne verrons pas comment et renvoyons le lecteur à la documentation du C++. Il nous faut donc l’expliquer : il ne s’agit pas d’un pointeur de char qui serait constant mais d’un pointeur vers des char qui sont constants ! Il faut donc retenir que : placé devant un tableau..5.

. b=tmp .. typename T2> bool cherche ( T1 e1 . b ) . i <n . } return f a l s e .. T& b ) { T tmp . b = 3 . s t r i n g noms [ 3 ] = { " j e a n " . double x = 2 . Si nous devions maintenant échanger deux variables de type double. c o n s t T1∗ tab1 . T2& e2 .. 176 . On peut en préciser plusieurs : / / C h e r c h e e 1 d a n s l e t a b l e a u t a b 1 e t met / / d a n s e 2 l ’ e l e m e n t d e t a b 2 d e meme i n d i c e / / R e n v o i e f a l s e s i non t r o u v é t e m p l a t e <typename T1 .12. } . En vrac (suite) . } La déclaration typename T précise le type générique. il faudrait réécrire une autre fonction echange(). y ) . / / " i n s t a n c i e " T en i n t echange ( x .. Heureusement. i n t ages [ 3 ] = { 2 1 .. " paul " } . int i . identique aux définitions de type près. Cette "programmation générique" se fait en définissant un "template" : / / Echange deux v a r i a b l e s d e n ’ i m p o r t e q u e l t y p e T t e m p l a t e <typename T> void echange ( T& a .. tmp=a .. } . template 12. i n t a =2 . . a=b . echange ( a . / / " i n s t a n c i e " T en d o u b l e . 3 . un peu comme un type variable. 2 5 . Autre exemple : / / Maximum d e deux v a r i a b l e s ( a c o n d i t i o n que o p e r a t o r > ( ) e x i s t e / / pour l e t y p e T) t e m p l a t e <typename T> T maxi ( T a . j . T b ) { r e t u r n ( a>b ) ? a : b . le C++ offre la possibilité de définir une fonction avec un type générique.. que le compilateur devra "instancier" au moment de l’appel de la fonction en un type précis. 1 . 1 5 } . i n t n ) { f o r ( i n t i = 0 .6. " p i e r r e " . i ++) i f ( t a b 1 [ i ]== e1 ) { e2= t a b 2 [ i ] . c o n s t T2∗ tab2 . return true . . y = 2 ... echange ( i . j ) ..

d’instanciations) Pour ces raisons : 1. 3 ) ) cout << nm << " a " << ag << " ans " << endl . i f ( cherche (nm. 12. T B ) . 12. t e m p l a t e <typename T> p a i r e <T > : : p a i r e ( ) { 1. ages . // accesseurs T operator ( ) ( i n t i ) const . En vrac (suite) . mais les compilateurs actuels n’implémentent pas encore cette fonctionnalité ! 177 .. c’est l’utilisateur qui doit préciser en permanence avec la syntaxe obj<type> le type utilisé : / / P a i r e d e deux v a r i a b l e s d e t y p e T t e m p l a t e <typename T> class paire { T x[2]..cpp. On ne peut plus mettre la déclaration dans un fichier d’en-tête et la définition dans un fichier .6. 12. noms .2 template et fichiers Il faut bien comprendre que Le compilateur ne fabrique pas une fonction "magique" qui arrive à travailler sur plusieurs types ! Il crée en réalité autant de fonctions qu’il y a d’utilisations de la fonction générique avec des types différents (ie.. . la règle est de tout mettre dans le fichier d’en-tête 1 . Dans le cas des classes. c’est le compilateur qui détermine tout seul quels types sont utilisés.cpp.3 Classes Il est fréquent qu’une définition de classe soit encore plus utile si elle est générique. Cette remarque a déjà été formulée pour les fonctions inline.. Ceci est gênant et va à l’encontre du principe consistant à mettre les déclarations dans le .h et à masquer les définitions dans le . T& o p e r a t o r ( ) ( i n t i ) .6. ag .12. }... template . Le langage prévoit une solution avec le mot clé export. s t r i n g nm= " p i e r r e " .6. Faire des fonctions template ralentit la compilation et augmente la taille des programmes. public : // constructeurs paire ( ) . p a i r e ( T A. Du coup. car tous les fichiers utilisateurs doivent connaître aussi la définition. 2. Mais attention ! Dans le cas des fonctions. i n t ag . C’est possible.

template 12. } // accesseurs i n l i n e T operator ( ) ( i n t i ) const { a s s e r t ( i ==0 || i = = 1 ) . p a i r e <double > q . x [ 1 ] = B .. 2 ) . Dans le cas de la classe très simple ci-dessus. T B ) { x [ 0 ] =A. } }..5 : / / P a i r e d e deux v a r i a b l e s d e t y p e T / / F o n c t i o n s c o u r t e s e t r a p i d e s en i n l i n e t e m p l a t e <typename T> class paire { T x[2]. } t e m p l a t e <typename T> T p a i r e <T > : : o p e r a t o r ( ) ( i n t i ) c o n s t { a s s e r t ( i ==0 || i = = 1 ) . En vrac (suite) . } i n l i n e T& o p e r a t o r ( ) ( i n t i ) { a s s e r t ( i ==0 || i = = 1 ) . } . paire <int > p ( 1 .. return x [ i ] . on les sépare par une virgule : / / P a i r e d e deux v a r i a b l e s d e t y p e s d i f f é r e n t s 178 . i n t i =p ( 1 ) . q(1)=2. r .2... return x [ i ] . return x [ i ] ..6. } t e m p l a t e <typename T> p a i r e <T > : : p a i r e ( T A. public : // constructeurs inline paire ( ) { } i n l i n e p a i r e ( T A. .4. return x [ i ] . Lorsque plusieurs types sont génériques. } t e m p l a t e <typename T> T& p a i r e <T > : : o p e r a t o r ( ) ( i n t i ) { a s s e r t ( i ==0 || i = = 1 ) . x [ 1 ] = B . on aura recours aux fonctions inline vues en 11.12. T B ) { x [ 0 ] =A.

. Q. x= " p i e r r e " . .. i n t N> T somme( nuplet <T . p a i r e < s t r i n g .6. i n t > Q. template t e m p l a t e <typename S . } }... public : // accesseurs i n l i n e T operator ( ) ( i n t i ) const { a s s e r t ( i >=0 && i <N) . double > P ( 1 .4 > A. N> s e r a un t y p e d i f f é r e n t t e m p l a t e <typename T . nuplet < i n t . T y. on peut aussi rendre générique le choix d’un entier : / / n−u p l e t d e v a r i a b l e s d e t y p e T / / A t t e n t i o n : c h a q u e n u p l e t <T .. return s . En vrac (suite) . . f o r ( i n t i = 1 . y = 2 5 .2 > B . i n t N> c l a s s nuplet { T x [N] . i <N. } }.. i ++) s+=u ( i ) . } i n l i n e T& o p e r a t o r ( ) ( i n t i ) { a s s e r t ( i >=0 && i <N) . return x [ i ] .12. typename T> class paire { public : / / Tout en p u b l i c p o u r s i m p l i f i e r S x..N> u ) { T s=u ( 0 ) . . Les fonctions doivent évidemment s’adapter : t e m p l a t e <typename T . y=Y . Enfin.. p a i r e < i n t . Q.. 3 ) .. } 179 . nuplet < s t r i n g . // constructeurs inline paire ( ) { } i n l i n e p a i r e ( S X . 12.. T Y) { x=X . A( 1 ) = 3 . B(1)= " pierre " . return x [ i ] . 2 .

3) : le compilateur l’interprète comme le max d’un int et d’un double ce qui provoque une erreur ! Il faut taper max(1.4 STL Les template sont délicats à programmer. template 12. z3 . On ne les rajoute que lorsqu’apparaît le besoin de réutiliser l’existant avec des types différents.. Et bien. 1 . cout << z3 << endl . longs à compiler. / / argument Les couples sont aussi offerts par la STL : p a i r < i n t . on pourrait être tenté de mettre des template partout. .. Nous exposons cidessous quelques exemples qui devraient pouvoir servir de point de départ et faciliter la compréhension de la documentation.2. . z2 ( 1 . Au regard de tout ça. double x=min ( 1 . double m=abs ( z3 ) . 2 . 180 . Les complexes sont eux-aussi génériques. .3 > C .. s t r i n g > P ( 2 . r e a l ( ) . Cet ensemble est communément désigné sous le nom de STL (Standard Template Library). En vrac (suite) .. a. P .. complex<double > z1 ( 1 . etc. imag ( ) . Vous en trouverez la documentation complète sous Visual ou à défaut sur Internet. .3). " hop " ) . 3 .. z3=z1+z2 ..12. double a=z3 . 3 ) . Tout se passe comme si on avait programmé des tableaux de taille constante pour plusieurs valeurs de la taille. / / module double th=arg ( z3 ) .2. Les nuplets ci-dessus.. 4 ) . mais pas à utiliser. f i r s t =3.6. 3 . second= " hop " . nuplet <double . 0 ) ...6. 4 ) . Attention : une erreur classique consiste à appeler max(1.. P . 12. Il ne faut pas en abuser ! Il vaut mieux plutôt commencer des classes ou des fonctions sans template. non ! Les templates sont délicats à programmer. n’ont donc rien-à-voir avec des tableaux de taille variables. laissant variable le choix du type de leurs parties réelle et imaginaire : # i n c l u d e <complex> using namespace s t d . b=z3 . cout << somme(C) << endl . Et répétons-le encore une fois : le compilateur crée une nouvelle classe ou une nouvelle fonction à chaque nouvelle valeur (instanciation) des types ou des entiers génériques a . Des fonctions simples comme min et max sont définies de façon générique : i n t i =max ( 1 . Le C++ offre un certain nombre de fonctions et de classes utilisant les template.

. affiche ( l ) . f i n d ( 3 ) . i t ++) cout << ∗ i t << ’ ’ ..6 . / / P o i n t e v e r s l ’ e n d r o i t ou s e t r o u v e / / l e premier 3 i f ( i t ! = l .4] l =[5 . } . i t = l . template Enfin. on utilise un itérateur. l i s t <int > l . 181 . T b ) { f o r ( l i s t <T > : : i t e r a t o r i t = l . l . i t ! = l . / / P o i n t e v e r s l e d é b u t d e l a l i s t e cout << ∗ i t << endl .. end ( ) ) cout << " 3 e s t dans l a l i s t e " << endl . } / / R e m p l a c e a p a r b d a n s une l i s t e t e m p l a t e <typename T> void remplace ( l i s t <T>& l . p u sh _ f ro n t ( 2 ) . i t ! = l . l ..2 . // // // // // // l =[] l =[2] l =[3 ..5 . f o r ( l i s t <T > : : c o n s t _ i t e r a t o r i t = l .2 .2] l =[3 .4] Pour désigner un emplacement dans une liste. begin ( ) . . / / P o i n t e v e r s l ’ e n d r o i t ou s e t r o u v e / / l e premier 3 ∗ i t =6.. En vrac (suite) . f i n d ( 3 ) .4] Les itérateurs servent également à parcourir les listes (d’où leur nom !) : / / P a r c o u r t e t a f f i c h e une l i s t e t e m p l a t e <typename T> void a f f i c h e ( l i s t <T> l ) { cout << " [ " . p u sh _ f ro n t ( 2 ) . end ( ) . p u sh _ f ro n t ( 3 ) . Pour désigner un emplacement en lecture seulement. begin ( ) . end ( ) . l i s t <int > : : i t e r a t o r i t 2 . on utilise un itérateur constant. Seule difficulté : le type de ces itérateurs est un peu compliqué à taper 2 : l i s t <int > : : c o n s t _ i t e r a t o r i t . l ... p u sh _ f ro n t ( 5 ) .6. un certain nombre de structures de données sont fournies et s’utilisent suivant un même schéma. push_back ( 4 ) . / / a f f i c h e 2 i t = l .5 . Nous n’avons pas vu comment définir de nouveaux types cachés dans des classes ! C’est ce qui est fait ici. Voyons l’exemple des listes : # include < l i s t > using namespace s t d . l .4] l =[2 .3 . / / maintenant l =[2 .3 . i t ++) i f ( ∗ i t ==a ) ∗ i t =b . begin ( ) . 2.12.2 . Le ’∗’ sert ensuite à accéder à l’élément situé à l’emplacement désigné par l’itérateur. 12.2 . cout << ’ ] ’ << endl . T a . l . i t 2 = l .

.. == != < > <= >= . 12.. while(i<=100) { . } — if (i==0) j=1.j=j-3) — Indent : Ctrl+K. case 2: case 3: .. — Les ensembles ou set (pas deux fois le même élément). on peut appeler des algorithmes comme le tri de la liste : l ..) { .. 5 . 3 . — mx=(x>y)?x:y.. — Step out : Maj+F11 — for (int i=... — Les vecteurs ou vector (tableaux de taille variable). break. — do { ... } while(!ok)... — Les tas ou heap (arbres binaires de recherche). 182 . — Combinaisons : && || — switch (i) { case 1: .) { //saute cas i==j Tests if (i==j) — Comparaison : continue. Fiche de référence 12..i<=10.Ctrl+F . .. } — bool t=(i==0).. remplace ( l . — int i=1. — Et quelques autres encore. — Step inside : F11 i=i+2. tâches : Ctrl+Maj+Ech for (int j=. — Les files ou queue (First In First Out).. / / m a i n t e n a n t l = [ 1 ....7.... Sur le même principe que les listes.. 1 ) .. 1 .) — Gest.. — Les tables ou map (table de correspondance clé/valeur). affiche ( l ) .. vous trouverez dans la STL : — Les piles ou stack (Last In First Out). 2 . break. — if (i==0) { j=1. 4 ] ...j=10. sort ( ) . else j=2. En vrac (suite) .i++) — Step over : F10 . — if (i==0) j=1. if (t) j=1. Enfin..7 Fiche de référence Fiche de référence (1/6) Boucles if (t[i]==s){ // quitte boucle break. — Négation : ! } — for (int i=. i=i+1......j>i. k=2...12.. } . — for(int i=1. default: . } } Clavier — Build : F7 — Debug : F5 — for(int i=1.

2). En vrac (suite) . int x=3. . int plus(int a. Conversion : int i=int(x). a.. double x) { int y) { return x x. } return 0.y.2). }. return c. } .. if (first) { first=false. // OK cout << a << endl. — Une structure est un objet enint hasard(int a.b=tmp...5. unsigned char d=25. a. string s="hop".12. Color c. // i=3! int i=f(2).. — Inline (appel rapide) : } inline double sqr( void afficher(int x. signed char d=-128.est.. } .. .. j=k. sud. interdit! int j=2. a.y=3. — a=b. if (x>=w || y>=h) double y=sqr(z-3). // OK! if (j>1) { int k=3. k=l=3. } } — Valeurs par défaut : — Déclaration : void f(int a.int b) { if (x>0) — // . ..3)... // i=j.0).x=2. tièrement public (→ cf obint b). .2f. bool t=true.ouest}.m. return. if (x<0) } return -1. 12..c=Red. const int m=12... complex<double> z(2. — struct Point { int& b){ double x. void avance(Dir d). — Références : Structures void swap(int& a.. DrawPoint(x.Blue}. void g() { — Retour : f(12). vect C=A+B. . — Variables — Opérateurs : vect operator+( — Définition : vect A.2. } .4. Constantes : const int s=12.. f()=3.3.. if (x<0 || y<0) * } return.. int signe(double x) { f(10.l. Portée : int i. .. int k.. interdit! Types : int i=3.RED). — Pile des appels 183 Initialisation : int n=5. void f(int a... Variables globales : int n..int b). float y=1.int b=0). } — } int g() { .. // Var.// f(10. Pile/Tas Type énuméré : enum Dir{nord..o=n. Fiche de référence Fiche de référence (2/6) Fonctions — Itératif/Récursif — — Références constantes (pour — Définition : un passage rapide) : int plus(int a.j=g(). — int hasard(int n). Variables statiques : int f() { static bool first=true. j=i.. char c=’A’.y). globale int& f() { — Appel : return i. } . int f(int a) { .y. // OK! } //i=k. // OK .vect B) { int i.7. } — Affectation : . jets !) double hasard(). int tmp=a. Point a. double x=12.y=2..j. // f(12.int b) { — void f(const obj& x){ int c=a+b. float x=float(i)/j. void f() { n=10. — Surcharge : Point b={1. } — } void g(const obj& x){ void affiche(int a) { f(x).3. return 1.. i=j. i=2.. unsigned int j=4. — Référence en retour : } int i. — swap(x. // OK int i=m..

int main() { obj A.paramètres des fonctions — list<int>::const_iterator . return x[i+M*j]..... set.. — }.7... // C=A.. // OK — } void une_autre(obj A) { x=A.push_front(1). Destructeur : obj::~obj() { — pair<int.. p. a. // mon g int j=x+i. return *this. //NON A. Fiche de référence (3/6) Objets — struct obj { int x. Utilisé par : l. vector. int main() { obj a..B..pour_tous(). — complex<double> z...operator+(B) — — Méthodes constantes : void obj::f() const{ .).. // OK z=. } . Affectation : for (it=l.end(). +f(point(2.... int obj::f() { int i=g(3). void obj::a_moi() { x=.3)).y). .3).it++) Objets avec allocation dynaif ( *it==2) mique automatique : cf secit=4.end()) // mieux que obj b. — list<int>::iterator it } for (it=l. — class obj { — int x.. // OK a_moi(). //NON A.cpp } . y point a(2.x. A. C=A+B. — Ne pas trop découper.h. //OK — class obj { obj operator+(obj B). } public: double operator() point(int X. int x..x=. . Constructeur de copie : — #include<list> obj::obj(const obj& o){ using namespace std.. Fiche de référence 12. — #include "vect.b=a. it!=l.begin()..z=.. public: int z. int Y){ } x=X. compris dans vect... . point::point(int X..x. // OK A.)..f().11 — stack. //OK A.y.a_moi()...find(3)!=l. .a_moi(). // OK . queue.. class point { return x[i+M*j]. // OK public: } double& operator() (int i. } Compilation séparée .begin(). — } void g(const obj& x){ double *x. — Types : définitions dans le .h obj a.. (int i. return point(x+b..2) — min.B.. assert(i>=0 ..une_autre(B). En vrac (suite) .string> p.h".obj b(a). .int j){ Constructeur : assert(i>=0 .. } p. int main() { obj A.. int i=a. . void un_autre(obj A). // mon x return j. // OK } void obj::pour_tous() { x=. // méthode — int g(int y). — Ne déclarer dans le .obj b=a.. ..=y.. — if (l. } list<int> l.. — }.. } .h que les Objets temporaires : fonctions utiles.. .. .int j)const{ }. //OK A. * tion 10.x=3.. // champ int f(). obj& obj::operator=( it!=l.. ..C.int Y).valeur de retour it. y+b.end().cpp Constructeur vide : — Fonctions : déclarations dans obj::obj() { le .. void a_moi(). . point point::operator+( point b) { — #pragma once au début du fichier.f().. y=Y. x.. Accesseurs : map. void pour_tous().second="hop".12. heap..it++) const obj&o){ s+= *it.. // OK — } ..first=2. STL c=point(1.. class mat { 184 .. . }.max.y. définitions dans le ..

c_str()). maxi("a". return f. — do { T add()const. istream& f..is_open()) { class paire { return 1. T x[2]. paire() {} double x.h template <typename T> Entrées/Sorties — ostream& operator<<( T maxi(T a.. — Faire des fichiers séparés. class hop { .y. paire(T a. ..x<<’ ’<< p.. assert(x!=0). . sizeof(double)).. paire<int> a(1. 10 sizeof(double)).txt").. — string s. — double x[10]. ios::binary). Fiche de référence Fiche de référence (4/6) Template . — Faire des objets.close().cpp) // précisé! 10*sizeof(double)).. . //double ofstream f("hop. — Entiers : template <int N> class hop { stringstream f.close(). template <typename T> Conseils if (!g. — Ne pas oublier delete. 12. *)x.. le .— Le . — Tableaux : pas pour transcrire . En vrac (suite) . }. ifstream f(s. hop<3> A.x>>p. — Compiler régulièrement.y. // Chaîne vers entier 185 — Penser interface / implémentation / utilisation.3). — Ne pas toujours faire des ob— #include <sstream> jets ! using namespace std. f. x[0]=a. template <typename T> f.read((char deux.12. y=1/x.. .txt"). int i. . }. } ofstream f("hop. — Ne pas laisser de warning.. sizeof(double)). } public: — Erreurs et warnings : cliquer.. int s=a. une formule mathématique ! — ofstream f. .T b) { g >> i >> x.— Ne pas abuser du récursif. f>>p.3.close().somme()... ifstream g("hop. f.. return f.. using namespace std.2. — Faire des fonctions..txt"). } — Utiliser le debuggeur. — Debug/Release : nettoyer les — Multiples : g. f << s. paire<double> b.clear(). g..T b) { — #include <iostream> ostream& f..//string f << 1 << ’ ’ << 2..eof()).y. . f >> i. f. //int using namespace std. — Nettoyer en quittant.7..h doit suffire à l’utilisateur (qui ne doit pas regarder // Le type doit être f. .. } // tout seul! — #include <fstream> istream& operator>>( maxi(1. } while (!(g.read((const char*)&y.. . return x[0]+x[1]. f<<p... hop<int. // Le type est trouvé cin >> i >> j. }. .open("hop...point& p){ maxi(. ios::binary).write((const char*)x. — Indenter. const point&p) { } . f >> s. * typename S> — #include <cassert> g..bin".2). . — Fonctions : // A mettre dans LE // fichier qui l’utilise // ou dans un .2). } — Objets : ifstream g("hop.string> A. // Entier vers chaîne f.write((const char*)&y. template <typename T. T paire<T>::add()const{ — Faire des structures. f << i. g.x[1]=b.."c").bin".. cout <<"I="<<i<<endl..close().

.. — int& f() { int i.j]=.. double acos(double x).1) : test(i. return t.. y[i]=2*x[i].3].// NON! — — double x[10].) int t[2].. // manque [] int f() {. // NON! // 0. int t[2. // Modulo — #include <cstdlib> .. if (i=2) // NON! point a={2.. // Non. .1) : Divers — i++. k=s. // NON! x=double(i/j).3}. — — — #define _USE_MATH_DEFINES — #include <cmath> double pi=M_PI.find("hop".. // OUI for (int i=0..find("hop")..... i=rand()%n.. — s1="un deux trois".j.0). char c=s[0].i<100. // NON! delete t..s). //NON! — Ne pas tout mettre inline ! Erreurs fréquentes — — #include <cmath> double sqrt(double x). int q=r=4.3). //NON } f()=3. size_t i=s.) } // NON! for (j .. getline(cin. // NON! } int t[4]. t={1. // NON! — if (i>0 & i<n) // NON if (i<0 | i>n) // NON int s[3]={1. i^=(1<<n) s=new int[n].3). if (s1==s1) . y=max(x.int b). j=s.size(). i--. // On perd s! Imagine++ delete[] t..t[3]. 186 . — for (i . delete[] s.3}.. En vrac (suite) .3. — b="ça va?".. double sin(double x).. a={1. // NON! double x. x=rand()/ double(RAND_MAX).1) : flip(i. Fiche de référence (5/6) set(i. return i.. // NON int f()[4] { // NON! int t[4]. txt=a+" "+b.y. // HORREUR! int n=5. // NON! void f(int a=2. t=s. Pas de définition de fonction public: dans une fonction ! .c_str(). quitte #include "vec..’:’). — int f() { for (int i=1. i-=2. int t[n]. — if (.find(’h’). const char *t=s.// NON! x=i/j.12.... // NON! — void f(int a.0 et non 0: max int* t=new int[2].. // NON if i==2 // NON! — point p=point(1. void f(int a).0).2. — getline(cin.//NON — int i. a="comment". — if (s1<s2) .s. //Déja fait — Voir documentation.2}. Fiche de référence 12. — #include <ctime> s=double(clock()) /CLOCKS_PER_SEC. break.. }.i++) .2}.} — //NON! int i=f. string s="hop".. — — — — — #include <string> — using namespace std... // NON! if (.) { . s=t.2). — int l=s.// s est int i&=~(1<<n) // et non int* if (i&(1<<n)) t=new int[n]. t=f().y. s2=string(s1.y[10]... // NON! j=max(i.. // NON! // boucles seulement struct Point { } double x. t[i.. i|=(1<<n) — int *t. — j=i%n.//NON..s.... .) break.cpp"//NON // juste la boucle j void f(int t[][]). — double cos(double x). } f()=3. // NON! ..i<=10.//OK int* t. — Opérateurs binaires and : a&b or : a|b xor : a^b right shift : a>>n left shift : a<<n complement : ~a exemples : — class point { int x. l=s.//NON! t[1]=. // NON! if (. if (s1!=s2) ..4). j+=3. — #include <ctime> . srand((unsigned int) time(0)).//NON if (i==2) then // NON! point p(1..i++) — obj* t=new obj[n].1) : reset(i.) { Point a...2). // est un template STL int* s=new int[2].int b=0).7.. double x=1/3.find(’h’. int i.

.. — void f(int* t.i++) void f(int A[2][2])...n...."c"}. Devoir final Fiche de référence (6/6) Tableaux — 2D : int A[2][3]..i<3.. } } — Taille variable : int* t=new int[n].... — En paramètre (fin) : — Affectation : — En paramètre (suite) : void f(const int* t. — Taille variable (suite) : int *t.4}. t[i]=s[i].{4.i<n.i++) } 12..8.12. — void init(int t[].i++) y[i]=2*x[i].j[2*n]. string s[2]={"ab". — 2D dans 1D : int A[2*3]. int A[2][3]= int n) { {{1.*s.. delete[] t. for(int i=0.t[3]..2. 12.i++) t[i]=.y[5]. — void init(int t[4]) { t[i]=.int n){ int n) { for (int i=0. En vrac (suite) . — const int n=5. for(int i=0.. // OK — En paramètre : — void alloue(int*& t){ .8 Devoir final Vous pouvez enfin vous confronter aux devoirs complets des annales.2.. .i<5. int s[3]={1. A[i+2*j]=.3}. — Initialisation : int t[4]={1. int i[n].5. 187 ..3}.2. t[i]=0. . } s+=t[i]. t[i]=0.i<4. — Définition : — double x[5].6}}.. A[i][j]=. // NON! } for(int i=0.3. t=new int[n].

.

Pour la plus grande généricité possible.). — les tableaux statiques doivent avoir une taille connue par le compilateur. — 13.13. etc. Ainsi. Nous savons déjà utiliser les tableaux C++. . il y a même plusieurs classes pour cela ! Mais pourquoi donc ? En fait ces classes s’utilisent presque de la même façon (interface proche) mais leur implémentation est totalement différente. dont les méthodes se chargent des traitements bas niveau (allocation mémoire. — aucune facilité n’est offerte pour copier un tableau. nous sommes souvent amenés à rassembler une collection de données dans un tableau. il faut presque tout faire soi-même ! Par exemple. c’est ce qui est proposé dans la Standard Template Library (STL). Suivant l’usage qu’on en fait. Ainsi. De fait. En réalité.. — les tableaux dynamiques peuvent avoir une taille qui n’est connue qu’à l’exécution. il n’y a pas une structure canonique satisfaisant tout le monde. pour insérer un élément il faut décaler tous les suivants tout en s’assurant que la place est suffisante. Mais les répétitions se retrouvent même dans nos programmes. dont nous avons déjà rapidement parlé. qui est partie intégrante du C++. Ce problème se rencontre si fréquemment que le C++ propose par défaut des classes. donc nous devons tout faire à la main. Même si c’est suffisant pour faire tout ce qu’on veut.1 Rappels sur les tableaux Nous connaissons maintenant bien les tableaux. Ce chapitre n’a pas pour but de documenter les détails de ces classes. En fait. copie. une manière de gérer la collection peut être préférable à une autre. mais rappelons quelques caractéristiques importantes : — les tableaux sont de taille fixe et ne peuvent être étendus après leur création. mais plutôt d’expliquer les forces et faiblesses de chacune et la façon dont elles ont pu être implémentées. L’idée dans ce cas est bien sûr de mettre toutes ces fonctionnalités dans une classe. mais il faut alors s’occuper soi-même de la mémoire. ces classes sont template et font partie de la STL. Suivant l’usage qu’on veut en faire.. mais notre usage en est primitif : ce ne sont pas des classes. Structure de données Chapitre 13 Structure de données Les ordinateurs sont là pour nous affranchir des tâches fastidieuses ou répétitives. On ne peut pas non plus demander sa taille à un tableau. ces limitations ne facilitent pas la tâche du programmeur et alourdissent le code. l’une sera plus efficace que les autres.

le temps est précieux”. i < 2 5 6 . 1 . i ++) f o r ( i n t j = 0 . i ++) f o r ( i n t j = 0 . c ++) { i n t h=0. i < image . h ) . Du coup. quand on ne peut pas réduire l’un et l’autre. Structure de données La complexité Avant de regarder de plus près les différentes classes. } La différence est qu’on a économisé en mémoire un tableau de 256 entiers. Dans ce cas le principe général est le suivant : “la mémoire est bon marché. drawRect ( i . On voit qu’en plus de parcourir deux fois le tableau histo (ce qui ne dépend pas de la taille de l’image).2 13. 0 . il convient de préciser ce qu’on entend par complexité. i ) == c ) ++h . 190 . width ( ) .2. 1 . f o r ( i n t i = 0 . c < 2 5 6 . i < 2 5 6 . ajouter de la mémoire à une machine est facile. h e i g h t ( ) . on ne lit qu’une fois chaque pixel de l’image. j < image . Considérons maintenant une variante : f o r ( i n t c = 0 . i ++) histo [ i ] = 0.2. j ++) ++ h i s t o [ image ( j .13. car la mêmoire n’est pas un facteur déterminant dans les problèmes que nous traiterons. Nous considérerons ici surtout la complexité en temps. car il faut réécrire le programme pour l’exploiter et beaucoup de problèmes ne sont pas ou difficilement parallélisables). h i s t o [ i ] ) . Prenons un exemple simple : tracer l’histogramme d’une image. 13. le nombre de pixels. i ++) drawRect ( i . j ++) i f ( image ( j . Sa complexité est donc de l’ordre de N . On préférera sans ambiguïté la première solution. Voici une façon de faire : int histo [256]. On parle de complexité en temps dans le premier cas et de complexité en espace dans le second. Plus concrètement. La complexité 13.1 Mais qu’est-ce donc que la complexité ? C’est le nombre d’opérations qu’il faut effectuer pour résoudre un problème et la taille de la mémoire nécessaire. par contre la puissance de calcul est limitée (et ajouter des processeurs n’est pas une option viable en général. f o r ( i n t i = 0 . i ) ] . soit un facteur 256 par rapport à l’implémentation précédente. i < image . f o r ( i n t i = 0 . On a une complexité en temps de 256N . j < image . h e i g h t ( ) . on lit chaque pixel de l’image 256 fois. f o r ( i n t i = 0 . width ( ) . 0 . On est souvent amené à trouver un équilibre entre les deux.

Ainsi la FFT 2 . Les algorithmes d’histogramme de la section ci-dessus sont tous deux en O(N ) bien que leur constante soit très différente.2. ou plus exactement s’ils le sont. Bien sûr la constante C a un rôle. nous n’avons pas besoin de préciser dans la notation O 2. peut tout à fait s’appliquer à une image de 10 Mpixels. Il peut rester acceptable pour des N pas trop grands. Fast Fourier Transform. notez le Fast 3. La complexité 13. c’est-à-dire quand N tend vers l’infini.2. on ne sait pas comment ! 191 .13. un algorithme célèbre de calcul de la transformée de Fourier. et ces algorithmes sont considérés comme rapides. mais on s’intéresse avant tout à la fonction f puisque c’est le facteur déterminant. Voici quelques exemples de complexité qu’on rencontre souvent par ordre de complexité croissante : — O(1) (f (N ) = 1 pour tout N ) signifie un algorithme qui s’effectue en temps constant indépendamment de la taille des données. Exemple : recherche par dichotomie dans un tableau trié. Les problèmes N P ne sont pas solubles exactement en temps raisonnable 3 lorsque N est grand. Il n’est possible que si celles-ci ont déjà une certaine structure imposée.4 P et N P Nous n’entrerons pas dans des considérations d’informatique théorique.2. tous les algorithmes se valent ou presque. 13. la classe P désigne des problèmes pour lesquels il existe un algorithme polynomial (f est un polynôme) et N P est un problème pour lequel on ne connaît pas de tel algorithme (ce qui ne veut pas dire qu’il n’en existe pas !) par exemple un problème de combinatoire pour lequel on ne sait pas faire mieux que tester toutes les combinaisons. On utilise dans ce cas la notation commode O(f (N )) où f est une fonction croissante. c’est légèrement plus que linéaire 1 . mais beaucoup ont entendu parler de la question P = N P ? et du million de dollars offert par l’institut Clay pour sa résolution. mais celui-ci ne différant du logarithme népérien que par un facteur constant. Structure de données 13. puisqu’il n’a pas besoin de lire toutes les données. Il s’agit souvent du logarithme en base 2. 1. proportionnel au nombre d’éléments. Mais cela ne veut pas dire qu’on ne peut pas trouver une solution approchée en temps polynômial. C’est un algorithme qui n’est pratique que pour des problèmes de petite dimension. — O(N ) (f est la fonction identité) désigne un algorithme linéaire. — O(N 2 ) est un algorithme quadratique. — O(2N ) est une complexité exponentielle (f (N ) = eN log 2 ). — O(N log N ) se rencontre fréquemment. On s’intéresse donc au cas où le nombre d’éléments devient grand. c’est-à-dire N = 107 .f (N ) où C est une constante qui ne dépend pas de N .3 La notation O La question de la complexité se pose quand on a des problèmes de grande dimension. — O(log N ) est un algorithme rapide.2 Comment la mesure-t-on ? 13. Cela signifie que le nombre d’opérations est borné par C.2. Pour faire bref. La fonction log augmentant très lentement. Sur des jeux de données petits.

cela participe à la lutte contre le réchauffement climatique ! Attention. on privilégiera celui de complexité minimale. et elle n’est pas toujours où l’on croit. ce qui est possible grâce à la définition de l’opérateur [] pour la classe vector. — On accède aux éléments du vecteur comme pour un tableau. Le vecteur : un tableau encapsulé dans une classe 13. car il nous fera gagner du temps 4 . sur le dégagement de chaleur du processeur.. traque des bugs et maintenance. v e c t .3 Le vecteur : un tableau encapsulé dans une classe 13. On l’obtient par un #include <vector>. 0 ) . — Nous pouvons retrouver la taille du tableau à tout moment avec la méthode size. v e c t [ 0 ] n ’ e x i s t e p a s v e c t . et notre temps sur Terre est limité. comme par exemple la technologie SpeedStep chez Intel. v e c t .3. et chercher à optimiser celle-ci. Remarquons que : — Nous n’avons pas eu besoin de déclarer le nombre d’éléments que nous mettrons dans vect lors de sa construction. donc consomment moins d’énergie quand le processeur a peu à faire. Structure de données Pourquoi est-ce important ? Un algorithme exponentiel est inutilisable pour de grandes données. cout << v e c t . / / OK. sur le bruit produit suivant que le ventilateur se met en marche..) et il s’agit d’un tableau encapsulé tout simplement dans une classe. Cela a des conséquences sur la batterie de votre ordinateur portable. Mais parmi les algorithmes polynômiaux. il faut quand même se garder de la tentation de vouloir optimiser trop tôt.5 13. v e c t [ 0 ] = 3 . il faut éviter de l’appeler inutilement : 4. De nombreuses architectures ajustent leur fréquence en fonction de l’utilisation du CPU. Un vecteur de double se construit ainsi et ajouter les éléments se fait par la méthode push_back : v e c t o r <double > v e c t . 1 4 1 6 ) . s i z e ( ) << " elements : " << v e c t [ 0 ] << " " << v e c t [ 1 ] <<endl . à une différence de taille : il se charge de gérer sa mémoire et l’ajuste dynamiquement à la demande. push_back ( 2 . sur la quantité d’énergie consommée.3. 13.. Il faut d’abord identifier la partie coûteuse en temps. mais justement. / / C ’ e s t bon m a i n t e n a n t . 192 . push_back ( 2 . 1 4 1 6 . Car malheureusement les algorithmes efficaces en temps sont souvent plus complexes du point de vue de la programmation et le temps gagné en calcul est perdu en temps de programmation.13.1 Usage Il s’agit de la classe std :: vector (ou vector après using namespace std. 1 4 1 6 . v e c t [ 0 ] = = 2 v e c t [ 0 ] = 3 . Utilisons donc des algorithmes efficaces.2. / / NON. Il y a cependant des erreurs à éviter : s t d : : v e c t o r <double > v e c t . v e c t [ 0 ] e x i s t e L’opérateur = et le constructeur par recopie sont définis comme il faut. 7 1 8 ) . que le processeur puisse se remettre rapidement en mode basse fréquence.. push_back ( 3 .

s t d : : cout << s t d : : endl . voir le chapitre suivant 193 .13. p a r c o p i e f o r ( i n t i = 0 . s i z e ( ) . Le retrait d’un élément (erase) également. 13. c’est-à-dire N −i. On voit que le vecteur est particulièrement efficace quand on ne fait qu’ajouter des éléments et que l’ordre n’a pas d’importance. mais c’est un choix assumé. on aura gaché N − 1 emplacements mémoire. On voit que la pile s’implémente tout à fait naturellement avec un vecteur. mais il est toujours possible de trier le vecteur après l’avoir rempli. le principe énoncé plus haut est appliqué : “la mémoire est bon marché. L’opération élémentaire est ici le nombre de lectures ou d’écritures dans une case du tableau. Cela se traduit par le choix suivant : chaque fois que la mémoire est pleine au moment où on veut ajouter un élément. Structure de données 13. Par contre la lecture ou l’écriture dans une case quelconque du tableau se fait en O(1). 13. si la fonction push_back a besoin de réallouer de la mémoire et de recopier dans le nouveau tableau les éléments déjà existants. on alloue une place mémoire double de la précédente. Last In First Out (LIFO) Rappelons le principe de la pile : le dernier élément ajouté est le premier enlevé. Il vaut bien mieux passer par référence. i < v e c t . mais constante pour bien indiquer au compilateur qu’on ne souhaite pas modifier le tableau : void p r i n t ( c o n s t s t d : : v e c t o r <double>& v e c t ) { / / T r è s b i e n . L’ajout d’un élément à la fin du tableau se fait en O(1) de même que le retrait du dernier élément (pop_back). Ainsi.. l’ajout d’un élément en position i ( insert ) nécessite de décaler tous les éléments qui le suivent.3. 5 ce qui est le cas dans la plupart des cas.. on a besoin de seulement log2 N allocations mémoires. Par contre. ce qui appelle donc un constructeur par recopie. L’ajout d’un élément se fait donc par push_back. } Le paramètre vect de la fonction print est passé par valeur.4 La pile. i ++) s t d : : cout << v e c t [ i ] << " " . si la mémoire est pleine et qu’on n’a besoin de n’ajouter qu’un élément. on peut se permettre d’en abuser (un peu)”. La pile. et le retrait du haut 5. C’est pourtant crucial pour son efficacité. 13. Last In First Out (LIFO) void p r i n t ( s t d : : v e c t o r <double > v e c t ) { / / E v i t e r : c o n s t r . celui d’indice size()−1. qu’on peut aussi consulter avec la méthode back(). Celui-ci alloue la mémoire nécessaire et recopie le paramètre passé en entrée de print. Ainsi si on veut ajouter N éléments. Le haut de la pile est le dernier élément.2 Complexité Intéressons-nous à la complexité des opérations élémentaires sur le vecteur.4. ce qui est inutile. Par contre. on passe du O(1) annoncé à O(N ).3.3 Gestion mémoire Nous avons passé sous silence la façon dont la classe gère sa mémoire. Pour éviter donc de réallouer trop souvent. donc jusqu’à O(N ).

13. push ( 3 . Structure de données 1 2 3 4 push(5) fin debut 5 1 2 3 4 pop() fin debut fin debut F IGURE 13.13. Ce principe est illustré Fig.5 La file. par exemple une image.5].1. empty ( ) ) . / / A f f i c h e 3 . Ce n’est donc pas une bonne solution. Voici une possibilité d’implémentation simplifiée : class File { s t d : : v e c t o r <double > v . il y a une légère différence avec l’interface de pile que nous avons déjà rencontrée : la méthode pop ne renvoie pas l’élément en haut de la pile. 7 1 8 ) . / / Ou b i e n a s s e r t ( s .3. void push ( double d ) .2. 7 1 8 s . i n t modulo ( i n t i ) c o n s t .4. Par contre. Pour raison d’efficacité 6 . public : File ( ) . 13. 1 4 1 6 ) . en fait elle ne renvoie rien (void). mais est ignoré puisqu’il n’est pas entre debut et fin. cout << s . de la pile par pop_back.5]. il suffit de se souvenir que le nouveau premier se trouve en fait être le deuxième etc. premier servi ! Nous pouvons stocker les éléments dans un vecteur. En d’autres termes. First In First Out (FIFO) 1 2 3 4 5 13.3. i n t debut . Cela n’est guère troublant pour un type de base. cout << s . bool empty ( ) c o n s t . mais on pouvait toujours la récupérer en appelant pop juste avant. 194 . a s s e r t ( s . le “pop_front” (cette méthode n’existe pas en fait) serait en O(N ). double f r o n t ( ) c o n s t . A noter que le 1 reste dans le tableau.4].2. la file correspond à la file d’attente habituelle : premier arrivé. s .1 – Fonctionnement d’une file. Milieu : après push(5) : [1. Cet élément est donc perdu après l’appel à pop. 1 4 1 6 s .5.4. push ( 2 . Gauche : le contenu de la file est [1. top ( ) << s t d : : endl . 6. Droite : après pop() : [2. f i n . pop ( ) . la STL propose quand même std :: stack : s t d : : s t a c k <double > s . opérations en O(1). La file. mais devient plus gênant pour un type complexe. s i z e ( ) = = 0 ) . En effet cela obligerait à créer une variable locale dans la méthode pour y recopier la valeur en haut de pile et retourner cette valeur à l’utilisateur. après avoir retiré le premier élément. le push de la file serait en O(1) et le pop en O(N ). top ( ) << s t d : : endl . s . mais si le push_back est en O(1). / / A f f i c h e 2 .3. donc une deuxième recopie. First In First Out (FIFO) Contrairement à la pile. Pour retrouver le vocabulaire spécifique et standard de la pile. double pop ( ) . }. pop ( ) .

Structure de données 13. } / / R e t r a i t de l ’ é l é m e n t de t ê t e double F i l e : : pop ( ) { double d = f r o n t ( ) . La file. push_back ( d ) . r e t u r n ( i%v . } / / Indique si la f i l e est vide bool F i l e : : empty ( ) c o n s t { r e t u r n ( debut== f i n ) . / / R e c o p i e du t a b l e a u f o r ( i n t i =debut . s i z e ( ) ) v . reserve (v . s i z e ( ) i n t F i l e : : modulo ( i n t i ) c o n s t { i f ( v . size ()+1. else v[ fin ] = d. debut = 0 . i ! = f i n . v = v2 . v .13. p r o c h a i n c l i e n t double F i l e : : f r o n t ( ) c o n s t { r e t u r n v [ debut ] . return d .5. } Notons que : 195 . capacity ( ) + 2 ) . } / / T êt e de l a f i l e . } / / A j o u t d ’ un é l é m e n t void F i l e : : push ( double d ) { i n t f i n 2 = modulo ( f i n + 1 ) . i =modulo ( i + 1 ) ) v2 . fin = fin2 . First In First Out (FIFO) / / Constructeur File : : File () { debut= f i n = 0 . } / / A r i t h m é t h i q u e modulo v . i f ( f i n 2 ==debut ) { / / Le s e r p e n t s e mord l a q u e u e ! s t d : : v e c t o r <double > v2 . debut = modulo ( debut + 1 ) . c a p a c i t y ( ) == 0 ) return 0. push_back ( v [ i ] ) . c a p a c i t y ( ) ) . } i f ( f i n == v . / / On r e m e t l a t ê t e à l ’ i n d i c e 0 fin2 = v .

next . supposant que l’indice du premier élément est i0. Cet indice doit être inférieur à v. Ceci est fait via la méthode modulo qui n’a pas besoin d’être publique puisque c’est un détail d’implémentation. prev ! = −1) t [ t [ i ] . next ! = −1) t [ t [ j ] . prev . Il existe une classe template std :: deque (pour double-ended queue) qui implémente complètement une file.capacity(). next . Pour insérer l’élement d qu’on stocke à l’indice j juste après l’élément stocké à l’indice i. pour trouver l’élément list[i] on doit faire 196 . prev = j .6 La liste chaînée Les structures que nous avons vues jusqu’ici n’étaient efficaces que pour les ajouts et retraits d’éléments au début et/ou fin de tableau. on stocke avec chaque élément les indices du suivant next et précédent prev dans le tableau. on réinitialise la tête à l’indice 0 du tableau. pas au milieu. Bien entendu. — L’implémentation est un peu technique. prev = t [ i ] . t [ j ] . next = t [ i ] . une implémentation générique serait template. next . — On fait toutes les opérations modulo v. l’accès à un élément d’indice donné se faisait en O(1).13. mais montre qu’il est possible de programmer la file efficacement en une cinquantaine de lignes de code : push et pop sont en O(1). i f ( t [ i ] . next = t [ i ] . Par contre.capacity(). i f ( t [ j ] .6. — La variable fin indique l’indice suivant la queue. next ! = −1) t [ t [ i ] . ce qui permet de réutiliser les cases libérées par un pop() précédent et économise de la mémoire. mais alors moins lisible. La liste chaînée 13. Pour cela. Structure de données — On utilise les méthodes reserve et capacity qui concernent le nombre d’éléments dans le tableau alloué (qui peut être plus grand que le nombre d’éléments réels). — La méthode push est compliquée par le fait qu’elle doit réinitialiser un tableau plus grand si v est plein. t [ j ] . next ] . — Nous avons présenté cette implémentation pour un tableau de double. bien que la gestion de la mémoire soit différente de celle proposée ici. s t r u c t chainon { i n t prev . il suffit de faire : t [ j ] . val = d . next ] . Si c’est le cas. d’où l’utilisation de reserve. Pour retirer l’élément stocké en i. il suffit de reconnecter ensemble les suivant et précédent : i f ( t [ i ] . Cette façon de faire laisse des “trous” dans le tableau t. L’indice −1 est un marqueur de début ou de fin de liste. next = j . t [ i ] . En fait. prev ] . il y a possibilité d’inverser les choses : la liste chaînée permet d’ajouter et retirer un élément n’importe où en O(1) mais ne permet l’accès à un élément quelconque qu’en O(N ). double v a l . 13. prev = i . }.

7. pour indiquer un emplacement dans le tableau. v a l . double d = t [ i i ] . Ils sont assez similaires à des pointeurs. les champs next et prev sont des pointeurs. L’intérêt de les utiliser c’est qu’il n’est pas nécessaire de définir tous les algorithmes sur toutes les classes. Nous avons évité au maximum de parler de pointeurs dans ce cours. 2 13. La méthode begin renvoie un itérateur sur le premier élément du tableau et end pointe une case plus loin que le dernier élément (donc attention de ne pas essayer d’accéder à l’élément pointé par cet itérateur). 13. mais peuvent être ajoutées avec complexité O(1) dans l’implémentation que nous avons vue. 4 Ces méthodes ne font pas partie de l’interface de la file ou de la pile. begin ( ) . j ++) i i = t [ i i ] . L’opérateur ++ fait passer à l’élément suivant et l’opérateur * retourne l’élément pointé par l’itérateur (ici un double).8 Les itérateurs En réalité. Il suffit de faire une fois l’algorithme et d’accéder aux éléments du tableau uniquement par l’intermédiaire des itérateurs. j < i .7 Récapitulatif des complexités push_back pop_back push_front pop_front tab[ i ] insert erase vecteur pile O(1) O(1)1 O(1) O(1)2 O(N)3 n/a O(N)3 n/a O(1) n/a4 O(N) n/a O(N) n/a file O(1)1 n/a n/a O(1)2 n/a4 n/a n/a liste O(1) O(1) O(1) O(1) O(N) O(1) O(1) 1 push_back s’appelle en fait push pour pile et file pop_back/pop_front s’appellent en fait pop pour pile et file 3 Ces méthodes ne font pas partie de l’interface du vecteur mais la fonctionnalité équivalente peut être obtenue par insert ou erase. La STL fournit une classe template std :: list qui implémente une liste. il suffit de savoir qu’un pointeur indique où trouver un élément en mémoire. next . comme par exemple pour l’insertion ou le retrait d’un élément. Récapitulatif des complexités int i i = i0 . ++ i t ) s t d : : cout << ∗ i t << " " . end ( ) . Structure de données 13. les classes de la STL utilisent des itérateurs. f o r ( i n t j = 0 . et non par un indice. s t d : : cout << s t d : : endl . i t ! = v e c t . c’est-à-dire des adresses en mémoire. En réalité. f o r ( . Voici un exemple d’utilisation de ces itérateurs : s t d : : v e c t o r <double > : : c o n s t _ i t e r a t o r i t = v e c t . Ceux-ci permettent d’énumérer les éléments ou de désigner un emplacement. 197 .13. Les itérateurs servent donc surtout de lien entre les structures et les algorithmes qu’on peut leur appliquer.

13. — La file de priorité. Citons entre autres : — Les ensembles (std :: set). il faut utiliser iterator au lieu de const_iterator .9. mais certaines pourraient l’être dans le futur. qui maintient un ensemble ordonné mais autorise l’insertion ou le retrait efficaces d’éléments. 13. qui associent à une clé (l’ensemble des clés doit être totalement ordonné) une valeur. typiquement une fonction associant un nombre entier à chaque élément. formés de sommets et d’arêtes (paires de sommets). qui sont semblables à ceux des maths dans le sens qu’une même valeur ne peut pas être présente plusieurs fois. — Les arbres. 198 . mais cela n’est possible que si vect n’est pas const. Nous en parlerons dans le prochain chapitre. — La table de hachache. — Les graphes. Beaucoup de problèmes se formulent facilement avec des graphes. Structure de données Si on veut modifier les éléments. Autres structures 13. — Les fonctions (std :: map). Elles ne sont pas toutes proposées par défaut dans la STL. qui permet une recherche efficace pourvu qu’on ait une méthode pour grouper les objets en un petit nombre de classes ordonnées. et permettent de retrouver efficacement la valeur associée à une clé.9 Autres structures D’autres structures très classiques car souvent utiles ont été passées sous silence dans ce chapitre. La STL en propose une implémentation avec std :: map. qui sont des graphes particuliers.

mais nous avons un gagnant en pratique.14. j ++) t a b [ k++] = i . on se retrouve alors au mieux avec deux tableaux de N/2 éléments. Ayant comparé deux éléments. mais uniquement dans le cas où des contraintes sur les valeurs en entrée sont connues. Algorithmes de tri Chapitre 14 Algorithmes de tri Maintenant que nous savons ranger intelligemment nos données (voir le chapitre précédent). et que la complexité résultante est O(N log N ). QuickSort. 14. f o r ( i n t i = 0 .2 Algorithmes quadratiques Il est extrêment simple d’imaginer un algorithme quadratique de tri. si les nombres en entrée prennent des valeurs parmi un petit nombre de valeurs possibles. Par exemple. cela ne signifie pas qu’il n’est pas possible de faire mieux. il faut souvent les trier. nous allons voir trois algorithmes de tris. L’opération élémentaire est la comparaison de deux éléments. i ++) histo [ i ] = 0. Il y a N ! permutations possibles. i ++) ++ h i s t o [ t a b [ i ] ] . et la conclusion n’est plus “ça dépend ce qu’on veut”. i n t k=0. j < h i s t o [ i ] . on peut chercher la valeur minimale v0 dans le tableau en O(N ) (et on compte son . Par exemple. — 14. En fait. f o r ( i n t i = 0 . on peut très bien faire un histogramme en O(N ). i < 2 5 6 . f o r ( i n t i = 0 . i ++) f o r ( i n t j = 0 . i < N.1 Complexité minimale Un algorithme de tri général et fonctionnant quel que soit la liste de valeurs en entrée doit retrouver la permutation permettant d’énumerer les éléments dans l’ordre. i < 2 5 6 . Nous verrons que c’est ce qui se passe en général avec QuickSort. En supposant que les valeurs possibles sont les entiers entre 0 et 255 : int histo [256]. C’est celui-ci qui est implémenté dans la STL. Ici.

t a b [ j + 1 ] ) . i < N. tels que le tri par insertion ou le tri par sélection. ne sont pas connues à l’avance et doivent donc être trouvées. ce qui peut se faire en N opérations de la façon suivante : — Nous maintienons un compteur i partant du début du tableau et l’incrémentons tant que t[i] est inférieur au pivot. Ceci sépare le tableau en deux parties. on le fait descendre jusqu’au bout du tableau. Notons la ressemblance avec le tri par histogramme proposé ci-dessus. dès qu’on arrive sur l’élément le plus grand. i ++) f o r ( i n t j = 0 . On fait ainsi (N − 1) + (N − 2) + . Nous sélectionnons un élément. — Nous avons un deuxième compteur j qui part de la fin et que nous décrémentons tant que t[j] est supérieur au pivot. Nous pouvons donc remplacer la boucle en j par f o r ( i n t j = 0 .. Puis on cherche la valeur minimale v1 parmi les éléments qui restent et ainsi de suite. Avec cette modification modeste. sont simplement des variantes et restent en O(N 2 ). nous avons divisé par 2 la complexité. Cet algorithme fait clairement N (N − 1) soit O(N 2 ) comparaisons. Il est donc inutile par la suite de comparer tab[N−2] et tab[N−1]. En fait. Algorithmes de tri nombre n0 d’occurences). QuickSort 14.. et nous itèrons sur chacun de ces sous-tableaux. j +1 < N. — Si i < j. On parcourt le tableau et on permute un élément avec son suivant s’ils ne sont pas dans l’ordre. En fait on peut faire mieux en observant que dans la boucle d’indice j.. 2. j ++) . nous permutons t[i] et t[j] et continuons la progression des indices. v1 . — Lorsque i == j. Rappelons brièvement le principe : 1.3. La seule différence est que les valeurs v0 .14. etc. + 1 comparaisons. un algorithme encore plus simple à programmer et bien connu est le tri à bulles. mais cela reste du O(N 2 ). On itère N fois cette boucle et en fin de compte le tableau est trié : f o r ( i n t i = 0 . et ainsi de suite. la gauche et la droite du pivot. Donc après un premier parcours. 200 . On parcourt donc N fois le tableau et la complexité est donc en O(N 2 ). la place du pivot est i. j ++) i f ( tab [ j ] > tab [ j +1]) swap ( t a b [ j ] . soit N (N − 1)/2. j + i +1 < N. L’étape cruciale est de trouver la position du pivot. et cherchons sa place définitive dans le tableau final.. De nombreux autres algorithmes simples. 14. appelé pivot. l’élément le plus grand est en bout et donc à sa position finale.3 QuickSort Nous allons étudier la complexité de QuickSort et constater qu’elle est optimale. Chaque valeur représente le poids d’une bulle et on voit les bulles les plus légères remonter (ou plutôt aller vers le début du tableau).

Choisir comme pivot l’élément du milieu (médian) entre le permier. L’élément pivot est un élément du tableau. Le QuickSort fonctionne sur le principe du “divide and conquer” (diviser pour mieux règner). les éléments du tableau peuvent être d’un type complexe avec une relation d’ordre coûteuse à calculer.3. Malheureusement. 2. souvent le dernier. Algorithmes de tri 14. Choisir comme pivot un élément tiré au hasard dans le tableau plutôt que le premier ou dernier. donc N − 1 comparaisons. Deux techniques courantes sont utilisées pour éviter ce cas défavorable : 1. 201 . Le résultat est donc CN = O(N log N ). Ce sont ces dernières qu’il faut chercher à minimiser. le dernier et l’élément à la moitié du tableau. où i = 0 (première position) ou i = N − 1 (dernière position).14. Le processus s’arrête lorsque l’indice de C à droite de la complexité devient 1. Pour le vérifier rigoureusement. le résultat devient CN = (N − 1) + (N − 2) + · · · + 1 = O(N 2 ). QuickSort Dans la pratique. il vient CN = 3N + 8CN/8 . et alors CN = (log N )N + N C1 . ce cas le pire n’est pas si rare en pratique. En allant un cran plus loin. nous obtenons CN = N + 2(N/2 + 2CN/4 ) = 2N + 4CN/4 . L’important est de diviser équitablement le tableau. la suite CN = N log2 N satisfait la relation de récurrence C2N = 2N + 2CN . La relation de récurrence devient CN = (N − 1) + CN −1 . c’est-à-dire que i = N/2. ce qui n’est pas difficile. Examinons maintenant ce qui se passe pour une violation extrême de cette hypothèse. Nous parlons ici des comparaisons entre éléments du tableau t. nous avons la relation de récurrence : CN = (N − 1) + Ci + CN −i−1 L’indice i dépend des données du tableau. Si nous notons CN la complexité et que nous nous retrouvons avec un pivot en position i. mais nous pouvons supposer qu’en moyenne il s’agit du milieu du tableau. Nous voyons que cette étape du pivot nécessite un parcours du tableau. Il s’agit du cas le pire. qui ne sont pas de même nature : ceux-ci sont des entiers. il faut en fait montrer par récurrence que pour N une puissance de 2. Nous voyons que ce cas n’est pas meilleur qu’un tri à bulles. qui se produit quand le tableau est déjà trié ou bien en ordre inverse. Nous écrivons donc CN = N + 2CN/2 . il faut faire bien attention de ne pas risquer de dépasser du tableau et cela demande du soin. pas d’indices i et j. En utilisant la relation de récurrence pour CN/2 . Si cela se reproduit à chaque étape.

mais faux pour une liste. En fait. comme nous l’avons vu au chapitre précédant. Il est possible de se demander ce qu’il en est entre ces deux extrêmes : pivot au milieu ou pivot à une extrêmité. Algorithmes de tri 10 7 5 8 3 10 10 7 7 5 3 7 9 7 8 9 5 3 7 8 F IGURE 14. il suffit de s’assurer qu’à tout moment nous pouvons accéder facilement à l’élément de plus grande priorité. Une structure d’arbre binaire vérifiant le principe suivant est bien adaptée : tout parent a une priorité au moins aussi grande que ses deux enfants. ce n’est pas pratique : 1. puis nous réparons l’arbre : s’il est plus grand que son parent. même si le cas le pire reste O(N 2 ). Voyons d’abord comment insérer un élément. Une solution simple qui vient à l’esprit est de maintenir à tout moment les éléments de la file triée par ordre de priorité.14. comme pour une liste. il faut alors O(N ) comparaisons pour trouver l’emplacement où l’insérer. HeapSort. il a une profondeur log2 N et donc O(log N ) comparaisons sont nécessaires. on peut trouver l’emplacement d’un élément à ajouter par dichotomie en O(log N ) comparaisons.4. Il est alors facile de voir que la racine est l’élément de plus grande priorité. comparons au nouveau parent et ainsi de suite jusqu’à arriver à la racine ou à un parent qui a plus grande priorité (Fig.1). qui reste optimal dans le pire cas O(N log N ).1 Insertion dans la file de priorité (push) Nous commençons par ajouter le nouvel élément comme feuille de l’arbre à profondeur minimale. ce qui est valable pour un tableau ordinaire ou un vecteur. 14. 202 . En fait. nous permutons les deux. sans qu’il soit nécessaire de tout trier.1 – Fonction push dans la file de priorité Ce choix de pivot se fait en temps constant. L’élément de plus grande priorité sera en tête. File de priorité et HeapSort 9 14. 14. En réalité. L’arbre étant équilibré. 2. mais alors l’insertion se fait en O(N ) copies d’éléments dans le tableau. En fait il est possible de montrer que la complexité moyenne est O(N log N ).4. c’est-à-dire le premier à sortir. Nous allons voir qu’en fait il existe un algorithme de tri. donc a un coût négligeable dans le cas “normal” où il serait inutile. nous allons modifier cette file en considérant que tous les éléments ne sont pas égaux mais que chacun a une priorité. Si on peut insérer l’élément en O(1).4 File de priorité et HeapSort Nous avons vu la file FIFO au chapitre précédant. 14. Cela n’est vrai que lorsqu’on peut accéder à un élément quelconque en O(1). QuickSort est implémenté par la fonction std :: sort de la STL (utiliser #include <algorithm>). Si on peut accéder à un élément arbitraire de la file en O(1).

File de priorité et HeapSort 3 10 6 7 5 6 4 6 7 3 5 6 7 7 4 5 6 6 3 6 4 5 6 3 4 F IGURE 14. nous descendons l’élément au plus jusqu’à la profondeur maximale. Voici donc l’algorithme push : void F i l e P r i o r i t e : : push ( double d ) { int i=size ( ) . 14. soit O(log N ). il est très efficace d’utiliser un simple tableau. while ( i >1 && v [ i ] > v [ i / 2 ] ) { s t d : : swap ( v [ i ] . Son parent en indice i/2 (quotient de la division euclidienne).2 – Fonction pop dans la file de priorité 14.14. back ( ) . v . Nous retirons l’élément la dernière feuille de l’arbre pour la mettre à la racine et réparons l’arbre : si la racine a plus faible priorité que le plus grand des (un ou deux) enfants. v [ i / 2 ] ) .2). nous considérerons que le premier élément du tableau est d’indice 1 et non 0. pop_back ( ) . s i z e ( ) && v [ 2 ∗ i +1] > v [ j ] ) / / D r o i t e j = 2∗ i + 1 . while ( 2 ∗ i < v . v . s i z e ( ) ) { i n t j = i . } } La fonction pop est légèrement plus compliquée : double F i l e P r i o r i t e : : pop ( ) { double d = v [ 1 ] . Les enfants de l’élément d’indice i sont en 2i et 2i + 1. etc. mais plutôt que de jouer sur les indices. int i = 1. La racine est donc en 1. i /= 2 . il est plus facile d’ignorer simplement l’indice 0 du tableau. Le C++ n’autorise pas cela.4.2 Retrait de la file de priorité (pop) L’élément en tête est la racine de l’arbre. Pour une fois. push_back ( d ) . Algorithmes de tri 14. / / On c h e r c h e l e max d e s e n f a n t s i f ( v [ 2 ∗ i ] > v [ j ] ) / / Gauche j = 2∗ i . nous permutons les deux et comparons aux deux nouveaux enfants et ainsi de suite (Fig.4. ses enfants en 2 et 3. Le processus s’arrête lorsque nous atteignons une feuille ou bien lorsque l’élément a plus grande priorité que ses enfants. les enfants de 2 en 4 et 5 et ceux de 3 en 6 et 7. i f ( 2 ∗ i +1 < v .4. L’arbre étant équilibré. 14. 203 . v [ 1 ] = v .3 Stockage de la file de priorité Pour stocker cet arbre binaire équilibré. i f ( i == j ) break .

double HeapSort ( s t d : : v e c t o r <double>& v ) { FilePriorite f .5 Conclusion QuickSort et HeapSort sont tous deux en O(N log N ) dans le cas moyen.4 HeapSort Le principe de HeapSort est juste d’utiliser la file de priorité. s i z e ( ) − 1 . i ++) f . push ( v [ i ] ) . Algorithmes de tri s t d : : swap ( v [ i ] . v [ j ] ) . j >= 0 .14. } return d . j −−) v [ j ] = f . f o r ( i n t j =v . } Mettre les N éléments dans la file nécessite O(N log N ) comparaisons et les retirer également O(N log N ). Cet algorithme est donc de complexité asymptotique optimale dans tous les cas. i < v . Cependant QuickSort est en O(N 2 ) au pire. i = j. } 14. appelée heap en anglais : les éléments sont ajoutés dans la file puis retirés les uns après les autres.5. Conclusion 14. pop ( ) . f o r ( i n t i = 0 . Mais HeapSort nécessite de la mémoire supplémentaire pour la file de priorité et le cas le pire de QuickSort peut être rendu très improbable par sélection du pivot comme expliqué plus haut. 14. s i z e ( ) .4. A noter qu’il s’agit du cas moyen aussi bien que du pire cas. 204 .

1 Bonjour. (a) Visual (donc sous Windows) n’est pas capable d’interpréter le fichier CMakeLists.1.cpp. la décompresser sur le bureau. lancer Kdevelop.txt. Lancer donc ce programme.txt décrit le projet.txt.A.". Programmation : (a) Rajouter cout << "Hello" << endl. 2. Projet : Télécharger l’archive Tp1_Initial. on peut fermer Cmake et ouvrir le fichier "solution" (extension . Travaux Pratiques Annexe A Travaux Pratiques Note : les corrigés seront disponibles sur la page web du cours après chaque TP. Si tout s’est bien passé. Génération : (a) Dans la fenêtre "Solution explorer" de Visual Studio. Connexion : Se connecter sous Windows ou Linux. Les choix proposés par défaut (en particulier le mode "Debug") sont corrects. cpp ) Elle indique que le programme s’appellera Tp1 (Tp1. C’est une bonne idée de séparer les fichiers générés des sources : sélectionner dans la ligne suivante un nouveau répertoire "Build".sln) dans le répertoire Build. 4. 3. La ligne qui nous intéresse est la suivante : a d d _e xe cu ta bl e ( Tp1 Tp1 .zip sur la page du cours. .. sur la ligne avant return 0.. Le fichier CMakeLists.exe sous Windows) et que le code source pour le construire est dans le fichier Tp1. le menu "Open Project" et choisir le CMakeLists. Kdevelop comprend le format CMakeLists et est capable de lancer Cmake lui-même.cpp. Monde ! 1. et aller chercher comme répertoire "source code" Tp1_Initial par le bouton "Browse source. sur les machines de l’École). rechercher et afficher le fichier Tp1. ce qui lance "Visual Studio" (b) Sous Linux. il faut utiliser le programme Cmake au préalable. A.1 L’environnement de programmation A. Cliquer "Generate" et sélectionner le bon génerateur (Visual Studio 2008.

obj qui est la compilation de Tp1. (c) Vérifier qu’on a en fait créé un programme indépendant qu’on peut lancer dans une fenêtre de commande : — Essayer de le lancer depuis le gestionnaire de fichiers standard : le programme se referme tout de suite ! — Dans les menus Windows : "Démarrer/Exécuter" — "Ouvrir: cmd" — Taper "D:". fabriquer une archive comprimée Tp1. Quelle est la nouvelle taille du répertoire ? 8. — Vérifier la présence de Tp1. Constater la présence de Tp1.exe sous Windows) dans Build/Tp1 (Build/Tp1/Debug sous Windows). Il faut d’abord lui préciser quel programme lancer (clic droit sur le projet Tp1 dans l’explorateur de solution. Fichiers : On a déjà suivi la création des fichiers principaux au fur et à mesure. — Taper "Tp1". On peut alors le lancer avec le bouton "Execute". et la fenêtre console reste ouverte jusqu’à l’appui d’une touche du clavier. en cliquant à droite sur le répertoire Tp1.1. Touche utile (Visual) : Ctrl+F5 = = Start without debugging 6.cpp (que le linker a ensuite utilisé pour créer Tp1. Voir aussi la présence de nombreux fichiers de travail. Compression : Sous Windows. Nettoyage : Supprimer les fichiers de travail et les résultats de la génération avec "Build / Clean solution" puis fermer Visual Studio.zip (ou Tp1. Il peut sinon y avoir une erreur ou certains fichiers trop importants peuvent subsister.exe avec la commande "dir". Exécution : (a) Sous Kdevelop. il faut en fait aller sur C :. Sur certaines machines. Travaux Pratiques (b) "Build/Build solution". vérifiez en regardant où est installé votre projet 206 . ou "F7" ou bouton correspondant. il faut commencer par aller dans le menu "Configure launches". (c) Vérifier l’existence d’un fichier Tp1 (Tp1.exe). ajouter avec le bouton "+" et sélectionner l’exécutable. 5. (b) Lancer le programme (sous Visual) avec "Debug/Start Without Debugging" (ou "Ctrl+F5" ou bouton correspondant).A. "Sélectionner comme projet de démarrage") Une fenêtre à fonds noir "console" s’ouvre. Quelle est la taille du répertoire de Build de Tp1 (clic droit + propriétés) ? 7. 1. dans laquelle le programme s’exécute. Attention il faut quitter Visual Studio avant de comprimer.7z suivant la machine). L’environnement de programmation A. 1 puis "cd \Documents and Settings\login\Bureau\Tp1\Tp1\Debug" (apprenez à profiter de la complétion automatique avec la touche TAB).

Envoi : Envoyer le fichier par mail à son responsable de PC en indiquant bien le nom de son binôme et en mettant [ENPCInfo1A] dans le sujet. Vérifier que Visual Studio sauve le fichier automatiquement avant de générer. (a) Tester une nouvelle génération/exécution. le linker indiquera une erreur s’il ne trouve pas une fonction ou des variables parce qu’il manque un fichier objet ou une bibliothèque. Visual Studio demande automatiquement une génération ! Lancer directement une exécution sauve et génère automatiquement. L’environnement de programmation Il faut quitter Visual Studio avant de comprimer. 3. Travaux Pratiques A.2 Premières erreurs 1. A ce propos.1. après std (d) inte au lieu de int (e) cou au lieu de cout (f) Oublier les guillemets " fermant la chaîne "Hello . (b) Modifier à nouveau le programme.A. avant la ligne avec main. 2.. constater et apprendre à reconnaître quelques erreurs de compilation : (a) includ au lieu de include (b) iostrem au lieu de iostream (c) Oublier le . Générer le programme : le linker constate l’absence d’une fonction f() utilisée par la fonction main() qu’il ne trouve nulle part. Attention toutefois de ne pas confirmer l’exécution si la génération s’est mal passée. Erreur de linker Il est un peu tôt pour réussir à mettre le linker en erreur. Il est pourtant indispensable de savoir différencier ses messages de ceux du compilateur. avant le return. En général. C’est pour l’instant une erreur de compilation. A. C’est aussi une erreur s’il trouve deux fois la même fonction.. Modifier le programme : Modifier le programme en changeant par exemple la phrase affichée. Tester directement une exécution. 9. 207 . (a) Rajouter une ligne f(2). " (g) Rajouter une ligne i=3. avant le return et faire Ctrl+F7.1... (b) Corriger l’erreur de compilation en rajoutant une ligne (pour l’instant "magique") void f ( int i ). Erreurs de compilation Provoquer. il est utile de découvrir que : Double-cliquer sur un message d’erreur positionne l’éditeur sur l’erreur. Compiler sans linker : il n’y a plus d’erreur.

L’environnement de programmation A.Ctrl+K.1. accolades.1. clic droit sur le projet dans la fenêtre de gauche. Le menu Edit/Advanced fournit de quoi bien indenter. C’est pourtant essentiel pour une bonne compréhension et repérer d’éventuelle erreur de parenthèses. Une option du compilateur propose même de les considérer comme des erreurs ! A. Touche utile : Ctrl+K. 2. etc. i n t main ( ) { int i . toujours bien indenter. cout << i << endl. 1. provoquer les warnings suivants : 2 (a) int i .A. Exécuter (répondre "abandonner" !) (d) Provoquer le warning inverse : variable déclarée mais non utilisée. k . 5. Appeler une fonction en oubliant les arguments arrive souvent ! Exécuter pour voir.Ctrl+F = tout indenter. (b) int i . Exécuter ! (c) int i . Certains de ces warnings ne se manifestent qu’en niveau d’exigence le plus élevé : pour le mettre en place. est le défaut). 208 . comme première instruction de main.3 Debugger Savoir utiliser le debuggeur est essentiel. menu “Propriétés”. j . Touche utile : Ctrl+A. le programme ne doit plus être correctement "indenté". Il y a maintenant un autre warning. Pourquoi ? (La fonction exit () quitte le programme en urgence !) Il est très formellement déconseillé de laisser passer des warnings ! Il faut les corriger au fur et à mesure. Corriger en mettant exit (0). Indentations : Avec toutes ces modifications. onglet C++. Taper le main suivant. i =2.Ctrl+F = indenter la zone sélectionnée. (e) Ajouter exit. Warnings du compilateur En modifiant le main(). Travaux Pratiques 4. i=4. plus simple et plus puissant que de truffer son programme d’instructions supplémentaires destinées à espionner son déroulement. Pour repérer des erreurs. . Il doit s’agir du premier réflexe en présence d’un programme incorrect.5. C’est un véritable moyen d’investigation. j =i . sélectionner le “warning level” 4 (3. if ( i=3) cout << "salut" << endl. Exécuter pour voir le résultat. j . moins exigent.

Travaux Pratiques A.zip sur la page du cours (http://imagine. Ajouter i=max(j. } 2. Placer un deuxième point d’arrêt et voir que F5 redémarre le programme jusqu’au prochain point d’arrêt rencontré. 8. 209 .A..fr/~monasse/Imagine++. Avancer en "Step over" avec F10 ou le bouton correspondant et suivre les valeurs des variables (dans la fenêtre spéciale ou en plaçant (sans cliquer !) la souris sur la variable). 4. Arrêter l’exécution avant d’atteindre la fin de main sous peine de partir dans la fonction qui a appelé main ! 6.k). apprenez à faire l’équivalent de ce TP sous XCode.. i f ( j ==5) k=3. Il arrive qu’on se retrouve dans la fenêtre "code machine" sans l’avoir demandé quand on debugge un programme pour lequel on n’a plus le fichier source.4 F5 = = Debug Maj+F5 = = Stop F10 = = Step over F11 = = Step inside S’il reste du temps Télécharger le programme supplémentaire Tp1_Final. 7. Constater la différence avec F10. Vous devrez utiliser CMake pour créer un projet XCode. avant le return.fr/~monasse/Info). exécuter jusqu’à un point d’arrêt puis faire Debug/Windows/Disassembly. Utiliser "Step into" ou F11 ou le bouton correspondant quand le curseur est sur cette ligne. return 0.enpc. j =3∗ i . Enfin. Lancer le programme "sous le debuggeur" avec Build/Start ou F5 ou le bouton correspondant. L’environnement de programmation i =2.1.enpc. Placer un "point d’arrêt" en cliquant dans la colonne de gauche à la hauteur de la ligne i=2. jouer avec. 5. On peut aussi dans ce même menu voir les registres du micro-processeur. Touches utiles : A. puis relancer le debuggeur. et le compléter ! A. Que se passe-t’il ? 3. A tout moment. pour voir à quoi ressemble du code machine.5 Installer Imagine++ chez soi Allez voir sur http://imagine.1. Si vous utilisez MacOS.1. on peut interrompre l’exécution avec Stop ou Maj+F5. else k=2. Cet affichage est en fait très utile pour vérifier ce que fait le compilateur et voir s’il optimise bien.

conditions. 1.2. fonctions A. i n t plus ( i n t a . return c .A. Récupérer le programme exemple : Télécharger l’archive Tp2.2 Variables. le dessin. A.zip sur la page du cours. Etudier le projet Hop dont voici les sources : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 # i n c l u d e <iostream > using namespace s t d . } i n t main ( ) { i n t i . } void t r i p l e 1 ( i n t a ) { a=a ∗ 3 . Debugger : Exécuter le programme pas à pas et étudier la façon dont les variables changent. c=a+b . triple2 ( i ). boucles. conditions. Elle permet de gérer très simplement le fenêtrage. k . nous utiliserons la librairie graphique d’Imagine++ (cf annexe du polycopié). h> 210 . i n t b ) { int c . return 0. boucles. triple1 ( i ). fonctions A. i =3. } 2.2 Premier programme graphique avec Imagine++ Dans ce TP et les suivants. la décompresser sur le bureau et ouvrir la solution dans Visual.2. j =2 . } void t r i p l e 2 ( i n t& a ) { a=a ∗ 3 . Variables. et les entrées-sorties clavier et souris. j ) .2.1 Premier programme avec fonctions 1. k=plus ( i . Travaux Pratiques A. Programme de départ : Etudier le programme du projet Tennis dont voici le source : 1 # i n c l u d e <Imagine/Graphics .

7 . 7 . conditions. / / Temporisation milliSleep (20). / / Boucle p r i n c i p a l e while ( t r u e ) { / / A f f i c h a g e de l a b a l l e f i l l R e c t ( xb −3 . 7 . White ) .. Variables. // / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / Fonction p r i n c i p a l e i n t main ( ) { / / Ouverture de l a f e n e t r e openWindow ( 2 5 6 . Tester cette fonctionnalité sur les mots-clés if .yb − 3 . Touche utile : F1 = Accéder à la documentation 3.A. la touche F1 permet d’accéder à la documentation du mot clé situé sous le curseur. vb = 3 . . / / Rebond i f ( xb+ub >253) ub=−ub .2. fonctions using namespace Imagine . yb =20 . Que se passe-t-il ? 2.yb − 3 . Elle se décompose ainsi : (a) Affichage de la balle (b) Temporisation de quelques millisecondes pour que la balle ne se déplace pas trop vite (c) Effacement de la balle 211 . } Ne pas s’intéresser à la fonction Clavier() Générer puis exécuter la solution. / / P o s i t i o n e t v i t e s s e de l a b a l l e i n t xb =128 . while et return.. Aide de Visual Studio A tout moment. ub =2 . boucles. yb+=vb . } endGraphics ( ) . / / E f f a c e m e n t de l a b a l l e f i l l R e c t ( xb −3 . Travaux Pratiques 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 A. 7 . Red ) . Comprendre le fonctionnement du programme : Identifier la boucle principale du programme. 2 5 6 ) . return 0. / / Mise a j o u r d e l a p o s i t i o n d e l a b a l l e xb+=ub .

Aussi. Gestion de tous les rebonds : Compléter le programme afin de gérer tous les rebonds. Le mot clé const indique que ces variables ne peuvent être modifiées après leur initialisation. définir une fonction void BougeBalle(int &x.int &u.yr2 dédiées à la position des deux raquettes. boucles. / / Rayon d e l a b a l l e et reformuler les valeurs numériques du programmes à l’aide de ces variables. Pour cela.2. Ici le dessin de la balle ne nécessite qu’une ligne mais cela pourrait être beaucoup plus si sa forme était plus complexe. Variables globales : Doubler la hauteur de la fenêtre. Afin de simplifier et bien que ça ne soit pas toujours conseillé. / / Hauteur d e l a f e n e t r e c o n s t i n t b a l l _ s i z e = 3 .. et commandées par les touches du clavier. Essayer de rajouter la ligne width=300. Cela nécessite de modifier le code à plusieurs endroits. A. Placer les appels de ces fonctions aux endroits appropriés dans la boucle principale.int &v) pour gérer les rebonds et le déplacement de la balle.yr1. Modifier la taille de la balle. Affichage des raquettes : Ajouter dans la fonction main des variables xr1. 6.xr2. utiliser des variables globales constantes. conditions. et que le code comporte le moins possible de duplications. la première fois en rouge et la deuxième fois en blanc pour l’effacer. insérer tout de suite après les deux lignes d’include le code suivant c o n s t i n t width = 2 5 6 .2. à la place de valeurs numériques “en dur”. Color c o l ) { . regrouper l’affichage de la balle et son effacement dans une fonction DessineBalle définie avant la fonction main : void D e s s i n e B a l l e ( i n t x . fonctions A.3 Jeu de Tennis Nous allons rendre ce programme plus ludique en y ajoutant deux raquettes se déplaçant horizontalement en haut et en bas de l’écran. Puis définir une fonction DessineRaquette en prenant modèle sur DessineBalle. au début de la fonction main et constater que cela provoque une erreur de compilation. } De même. Par exemple. il vaut mieux définir des variables. / / Largeur de l a f e n e t r e const i n t height = 256. Utilisation de fonctions : La balle est dessinée deux fois au cours de la boucle. il faut inverser la vitesse horizontale ub quand la balle va toucher les bords gauche ou droit de la fenêtre. i n t y .A. Aussi. pour que le programme soit mieux structuré et plus lisible. Variables. Travaux Pratiques (d) Gestion des rebonds (e) Mise à jour des coordonnées de la balle Pourquoi la ligne comportant l’instruction while suscite-t-elle un warning ? A quoi sert la condition formulée par l’instruction if ? 4. 5.. 1. 212 .int &y.

Rebonds sur les raquettes : S’inspirer de la gestion des rebonds de la balle..1. retourne dans sens1 et sens2.1 – Mini tennis. 4. Cette fonction. Comptage et affichage du score : Modifier la fonction BougeBalle afin de comptabiliser le score des deux joueurs et l’afficher dans la console. Clavier(int& sens1. int sens) puis appeler cette fonction dans la boucle principale pour chacune des deux raquettes. 1 : vers la droite). 213 . 3. Déplacement des raquettes : Coder le déplacement d’une raquette dans une fonction void BougeRaquette(int &x. 2. Variables.2. Vous devriez avoir obtenu un programme ressemblant à celui de la figure A. Evidemment. les valeurs 0. faire en sorte que les raquettes ne puissent sortir de la fenêtre. -1 ou 1 (0 : pas de déplacement. Travaux Pratiques A.. boucles. fonctions F IGURE A. Pour ceux qui ont fini : Lors d’un rebond sur la raquette. k et l pour la deuxième) sont enfoncées ou non. 5. Cette fonction nous permet de savoir directement si une des touches qui nous intéressent (q et s pour le déplacement de la première raquette. 6. Gestion du clavier : La gestion du clavier est réalisée pour vous par la fonction Clavier dont nous ignorerons le contenu pour l’instant. int& sens2). Ici il faut non seulement vérifier si la balle va atteindre le bas ou le haut de l’écran mais aussi si elle est assez proche en abscisse de la raquette correspondante. conditions.A. -1 : vers la gauche. modifier l’inclinaison de la trajectoire de la balle en fonction de la vitesse de la raquette ou de l’endroit de frappe.

La manière la plus simple de faire consistera à faire afficher les différents chiffres de la combinaison sur une même ligne les uns à la suite des autres. où l’utilisateur doit deviner une combinaison générée aléatoirement par l’ordinateur. nous allons donc utiliser un tableau de 5 entiers. 214 . l’ordinateur doit générer aléatoirement une combinaison à faire deviner à l’utilisateur.2 – Master mind à la console.zip sur la page du cours. Tableaux A.1 Mastermind Texte 1. i n t combin [ 5 ] .. 2. notamment la fonction rand() permettant de générer un nombre au hasard entre 0 et RAND_MAX.. Travaux Pratiques F IGURE A. A chaque essai d’une combinaison. on procédera de la manière suivante : x = rand ()%n . A. Etudier le projet Mastermind. Récupérer la solution de départ : Télécharger l’archive Tp3_Initial. l’ordinateur fournit deux indices : le nombre de pions correctement placés et le nombre de pions de la bonne couleur mais incorrectement positionnés. nous allons programmer un jeu de Mastermind.3.A. Représenter une combinaison : Nous prendrons ici une combinaison de 5 pions de 4 couleurs différentes. Nous allons pour cela utiliser les fonctions déclarées dans le fichier cstdlib . La couleur d’un pion sera représentée par un entier compris entre 0 et 3. Le joueur dispose d’un nombre déterminé d’essais. A. 4. Afficher une combinaison : Programmer une fonction permettant d’afficher une combinaison donnée à l’écran. Afin d’obtenir un nombre entre 0 et n. Générer une combinaison aléatoirement : En début de partie.3 Tableaux Dans ce TP.3. / / t a b l e a u d e 5 e n t i e r s 3. la décompresser dans un répertoire faisant apparaître les noms des deux élèves et ouvrir la solution MasterMind dans Visual Studio. Pour une combinaison de 5 pions.

En fin de compte. il s’agit de modifier cette fonction pour qu’elle contrôle que la chaîne rentrée est bien de bonne taille et que les chiffres sont bien entre 0 et nbcoul−1. Si ce n’est pas le cas. i < 5 . size () qui re215 . vous allez devenir des experts en Mastermind.A. string s . la fonction suivante nous permet donc de générer une combinaison : # include <cstdlib > # i n c l u d e <ctime > using namespace s t d . i ++) combi [ i ]= s [ i ]− ’ 0 ’ . La fonction time() déclarée dans le fichier ctime permet de l’obtenir. / / longueur de l a combinaison / / nombre d e c o u l e u r s d i f f é r e n t e s Reprenez le code que vous avez déjà écrit en utilisant ces constantes. il est nécessaire d’initialiser le générateur avec une graine variable. L’essai devra être redemandé jusqu’à ce que la combinaison soit valide. cela fait gagner beaucoup de temps lorsque l’on veut les modifier. / / i n i t i a l i s a t i o n // du g e n e r a t e u r f o r ( i n t i = 0 . 6. Cela vous est d’ores et déjà très facile si vous avez pensé à définir une constante globale pour chacune de ces deux grandeurs.3. Tableaux Pour que la séquence de nombres générée ne soit pas la même d’une fois sur l’autre. ++ i ) combin [ i ] = rand ( ) % 4 . On utilisera entre autres la fonction s . ou d’augmenter le nombre de couleurs possibles. Changer la complexité du jeu : Rapidement. Il suffira alors d’allonger la longueur de la combinaison. f o r ( i n t i = 0 . Vous allez alors vouloir augmenter la difficulté. / / a p p e l s au g e n e r a t e u r } 5. c i n >> s . Saisie d’une combinaison au clavier : La fonction suivante. Définissez par exemple : c o n s t i n t nbcases = 5 . i <nbcases . Il est très important de stocker les paramètres constants dans des variables. La manière la plus simple de procéder consiste à utiliser l’heure courante. que nous vous demanderons d’admettre. il est grand temps de le faire. saisit une chaîne de caractères (string) au clavier et remplit le tableau combi[] avec les chiffres que les nbcases premiers caractères de la chaîne représentent. } Dans le cadre de notre Mastermind. void genereCombinaison ( i n t combin [ 5 ] ) { srand ( ( unsigned i n t ) time ( 0 ) ) . Travaux Pratiques A. c o n s t i n t nbcoul = 4 . void getCombinaison ( i n t combi [ nbcases ] ) { cout << " Votre e s s a i : " .

programmer une fonction renvoyant uniquement le nombre de pions bien placés.. puis. 0 pion mal placé (xxxxx) 20000 : 0 pion bien placé (xxxxx). A. Nous allons y remédier en y ajoutant une interface graphique. 9. si la combinaison à trouver est 02113 : 00000 : 1 pion bien placé (0xxxx). Version complète : Compléter la fonction de traitement d’une combinaison pour qu’elle renvoie également le nombre de pions mal placés. 0 pion mal placé (xxxxx) . Travaux Pratiques tourne la taille de la chaîne s (la syntaxe de cette fonction sera comprise plus tard dans le cours. Cette fonction devrait renvoyer deux valeurs : le nombre de pions de la bonne valeur bien placés. i n t n ) . Les fonctions graphiques sont déjà définies. on laisse en haut de la fenêtre graphique autant de lignes libres que le joueur a d’essais pour y afficher le déroulement du jeu. Par exemple. permet d’afficher la combinaison combi sur la ligne n. 2 pions mal placés (20xxx) 13133 : 2 pions bien placés (xx1x3). Au début du programme. il n’y a plus qu’à les assembler pour former un jeu de mastermind. 3.2 Mastermind Graphique Le jeu de Mastermind que nous venons de réaliser reste malgré tout très peu convivial.A. Puis reprogrammer la boucle principale du jeu en utilisant l’affichage graphique. dans les pions restant.3.. La fonction : void a f f i c h e C o m b i n a i s o n ( i n t combi [ nbcases ] .3. 1 pion mal placé (1xxxx) 13113 : 3 pions bien placés (xx113). Elles fonctionnent selon un principe de division de la fenêtre graphique en lignes. 8. Etude du projet de départ : Passer dans le projet MastermindGraphique. Pensez par ailleurs à ajouter la détection de la victoire (quand tous les pions sont bien placés). Boucle de jeu : Nous avons maintenant à notre disposition toutes les briques nécessaires 3 . 1. Pour commencer et pouvoir tout de suite tester le jeu. Tableaux A. Penser à le définir comme projet de démarrage pour que ce soit lui qui se lance à l’exécution (son nom doit apparaître en gras dans la liste des projets). Traitement d’une combinaison : Il faudrait maintenant programmer une fonction comparant une combinaison donnée avec la combinaison à trouver. même si la fonction de traitement d’une combinaison est pour l’instant incomplète 216 . 0 pion mal placé (xxxxx) 12113 : 4 pions bien placés (x2113).. On affiche en bas de la fenêtre graphique un mini mode d’emploi qui résume les correspondances entre touches et couleurs.. le nombre de pions de la bonne valeur mais mal placés.) 7. 2. Mastermind graphique : Réinsérer dans ce projet les fonctions de génération aléatoire d’une combinaison et de comparaison de deux comparaisons écrites précédemment. et celle de la défaite (quand un nombre limite d’essais a été dépassé).

et void getCombinaison ( i n t [ ] .3. i n t ) .A. Etudier les fonctions int Clavier ( ) . mais pas la seconde qui la considère comme une erreur de frappe.. Ultime amélioration : On souhaite pouvoir effacer une couleur après l’avoir tapée. Travaux Pratiques A. Modifier cette dernière en conséquence.. 3. 217 . La première prend déjà en compte la touche Retour arrière. au cas où l’on se serait trompé.3 – Master mind graphique. Tableaux F IGURE A.

2 sont décrites quelques-unes des fonctions à utiliser.4. Les sections 11 et 15 ne sont données que pour les élèves les plus à l’aise et ne seront abordées qu’en deuxième semaine. c’est-à-dire ajouter 0. Structures A. A..1 Etapes Mouvement de translation 1. Évolution par accélération : Créer (et utiliser) une fonction qui modifie la vitesse d’une Balle de façon à lui faire subir une attraction constante ax = 0 et ay = 0.3 leur justification physique. de masse 10 et de rayon 4 pixels (la masse de la planète qui bouge étant de 1). L’afficher. Ajouter un champ décrivant la masse à la structure Balle. Ajouter un soleil : On souhaite ne plus avoir une gravité uniforme. Le programme fait évoluer un point (x.0005 ∗ dt à vy . et une autre void EffaceBalle(Balle D) l’effaçant. couleur). par ailleurs. En section A.A. Travaux Pratiques Structures Avertissement : Dans ce TP. Il s’agit d’un long TP qui nous occupera deux semaines.0005. Faire bouger proprement le disque : Pour faire évoluer la position du disque. le décompresser et lancer Visual C++.4 A. Indice : procéder comme précédemment. 6. remplacer les instructions correspondantes déjà présentes dans main par un appel à une fonction qui modifie les coordonnées d’une Balle. afin de l’effacer. et affiche régulièrement un disque centré en ce point. deux instructions commençant par NoRefresh sont placées autour des instructions graphiques afin d’accélérer l’affichage. Utiliser une structure : Modifier le programme de façon à utiliser une structure Balle renfermant toute l’information sur le disque (position. fixe (ie de vitesse nulle) au milieu de la fenêtre. Accélération gravitationnelle : Créer (et utiliser à la place de la gravitation uniforme) une fonction qui prend en 218 . jaune.4. Gravitation 5. 4. et en A. vitesse. en leur ajoutant la vitesse de la Balle multipliée par un certain pas de temps défini en variable globale (dt = 1 pour l’instant). 3. Fonctions d’affichage : Créer (et utiliser) une fonction void AfficheBalle(Balle D) affichant le disque D. Pour ce faire.4. Parcourir le projet. en s’attardant sur les variables globales et la fonction main (inutile de regarder le contenu des fonctions déjà définies mais non utilisées). Pour commencer. vy).. 2. 7. y) selon un mouvement de translation constante (vx. rayon. nous allons faire évoluer des corps soumis à la gravitation. étudier le projet : Télécharger le fichier TP4. on retient la position du disque au dernier affichage (dans ox et oy) .zip sur la page habituelle. puis leur faire subir des chocs élastiques. Créer un soleil (de type Balle).4.

8.1 (modifier la fréquence d’affichage en conséquent). par exemple 0. et disparaissant au bout de 250 pas de temps 219 .4. Essayez diverses initialisations de la planète (par exemple x = largeur/2. Chacun des deux joueurs a une position fixée. et divers soleils sont placés aléatoirement dans l’écran. à deux joueurs. Chaque joueur. Régler la fréquence d’affichage en conséquence (inversement proportionnelle à dt).. dans le calcul du déplacement de l’astéroïde.. de façon à ce qu’il ne rentre plus dedans (fonction ChocSimple). Des soleils par milliers. Pour savoir si deux corps sont sur le point d’entrer en collision. dans cette même solution.cpp dans un fichier Duel.001 si la machine est assez puissante).. Lancer plusieurs fois le programme. utiliser la fonction Collision. et rétablir dt à une valeur plus élevée. Faire tourner et observer. 9. Vous aurez probablement besoin de la fonction Random. qui retourne la racine carrée de x). et sa masse valant le rayon divisé par 20. vy = 0). et qui fait évoluer la position de la planète. vx = 1 . on prendra donc garde à ne pas utiliser ce terme lorsque r devient trop petit. avec ici G = 1 (Vous aurez sans doute besoin de la fonction double sqrt(double x). la balle subissant les effets de gravitation des divers soleils.A. sa position étant dans la fenêtre. Initialisation aléatoire : Créer (et utiliser à la place des conditions initiales données pour le soleil) une fonction initialisant une Balle.cpp et utilise la bibliothèque Graphics d’Imagine++. pour le fixer à 0. Placer 10 soleils aléatoirement (et en tenir compte à l’affichage. 10.). Ouvrir un nouveau projet : Afin de partir dans deux voies différentes et travailler proprement.. y = hauteur/3. peut lancer une Balle avec la vitesse initiale de son choix. à l’aide des fonctions qui y sont déjà présentes.txt en ajoutant deux lignes indiquant que l’exécutable Duel dépend de Duel. en un jeu de tir. modifier le fichier CMakeLists. Faire rebondir l’astéroïde : Faire subir des chocs élastiques à l’astéroïde à chaque fois qu’il s’approche trop d’un soleil. son rayon entre 5 et 15. Recopier (par exemple par copier/coller) intégralement le contenu du fichier Tp4.cpp. à tour de rôle. Structures argument la planète et le soleil. diminuer le pas de temps dt. Jeu de tir (figure A. 13. Ne pas oublier le facteur dt... Notez que l’expression de l’accélération devient très grande lorsque r s’approche de 0 . Travaux Pratiques A. Diminuer le pas de temps de calcul : Afin d’éviter les erreurs dues à la discrétisation du temps.. sa vitesse nulle.4 droite) 12. Une fois cette copie faite. appelé Duel. Rappel − de physique : l’accélération à prendre en compte est −G mS /r3 → r . À vous de jouer ! Transformer le projet Duel.01 (voire à 0. nous allons ajouter un nouveau projet Imagine++.. Chocs élastiques simples 11.

en déduire la vitesse désirée par le joueur en retranchant à ces coordonnées celles du centre de la boule à lancer. Structures A.4. 14. Le gagnant est le premier qui réussit à atteindre l’autre. Travaux Pratiques F IGURE A. Conseils pratiques : positionner symétriquement les joueurs par rapport au centre. 15. Exécuter une fois InitRandom(). et qu’ils soient à une distance minimale de 100 pixels des emplacements des joueurs.2 Aide Fonctions fournies : void InitRandom ( ) ..4.4 – Corps célestes et jeu de tir. tout faire rebondir : On retourne dans le projet Gravitation. Initialisation correcte : Modifier la fonction de placement des soleils de façon à ce que les soleils ne s’intersectent pas initialement.. double Random ( double a . est à exécuter une fois avant le premier appel à Random.00025. utiliser la fonction GetMouse pour connaître la position de la souris . pour les chocs élastiques. double b ) ..A.4 gauche) 16. d’affichage. Tout faire bouger. de préférence à mi-hauteur en laissant une marge d’un huitième de la largeur sur le côté . Faire en sorte que lors de l’initialisation les soleils ne s’intersectent pas. avant la première utilisation de cette fonction. Utiliser. et en multipliant par un facteur 0. y compris les soleils. A. 220 . Chocs élastiques (figure A. la fonction Chocs (qui fait rebondir les deux corps). renvoie un double aléatoirement entre a et b (compris).. Améliorations : Faire en sorte qu’il y ait systématiquement un gros soleil au centre de l’écran (de masse non nécessairement conséquente) afin d’empêcher les tirs directs. Tout faire évoluer.

double x2 . double r 2 ) . Elle peut être ignorée en première lecture. double &vx2 . double m2 ) . X→ − − F i/A = mA → a G(A) i Gravitation universelle Soient deux corps A et B. double y1 . Accélération La somme des forces exercées sur un corps A est égale au produit de sa masse par l’accélération de son centre de gravité. void Choc ( double x . Alors A subit une force d’attraction → − 1 − u B→A . et false sinon. Durant un choc dit élastique. double vx2 . vy1) et de rayon r1 est sur le point d’entrer en collision avec le corps de coordonnées (x2. y1). de vitesse (vx2. double vx2 . double &vx . F B/A = −GmA mB 2 → dA. y). Structures void ChocSimple ( double x . seule la vitesse des particules reste à calculer. fait rebondir les deux particules l’une contre l’autre. double y2 . les positions ne changent pas. renvoie true si le corps de coordonnées (x1. double m. double vx1 . double x2 . vy) et de masse m. y2). bool C o l l i s i o n ( double x1 . double x2 . double y . vy2). double vy2 .3 Théorie physique NB : Cette section n’est donnée que pour expliquer le contenu des fonctions préprogrammées fournies avec l’énoncé. double &vx . Connaissant tous les paramètres avant le choc. double y2 . Travaux Pratiques A. double m. puisque dans l’instant du choc.4. la quantité de mouvement P = m → v +m → v A A B B − − − − 2.4.B Chocs élastiques Soient A et B deux particules rentrant en collision. sur la deuxième. y2) et de vitesse (vx2. de coordonnées (x2. de vitesse (vx1. A. comment déterminer leur valeur après ? En fait.A. vy2) et de rayon r2. double y . 3. double &vy . double &vy . double &vy2 . fait rebondir la première particule. 221 . sans déplacer la deuxième. trois quantités sont conservées : → − − − 1. l’énergie cinétique Ec = 21 mA vA2 + 12 mB vB2 . double r1 . le moment cinétique M = mA → rA×→ v A + mB → rB×→ v B (qui est un réel dans le cas d’un mouvement plan). de coordonnées (x. double vy1 . de vitesse (vx. double y2 . double vy2 ) . Ce qui fait 4 équations pour 4 inconnues.

Décider de l’imminence d’un choc On ne peut pas se contenter. 2Ec = mA (1 + mA )vA2 . le minimum est atteint en tm .4. Travaux Pratiques Résolution du choc On se place dans le référentiel du centre de masse. ni même en prenant en plus en considération cette distance à l’instant t + dt. → − → − → − → − → − → − − − 2..A.. t + dt < tm . On a : − − − − N (u) = (→ r A (t) − → r B (t) + (u − t)(→ v A (t) − → v B (t)))2 Ce qui donne. P = 0 (par définition de ce référentiel). soit : (tm − t) = − − − ∆→ r (t) · ∆→ v (t) → − 2 ∆ v (t) Donc : 1. On a alors. 222 . Ce qui nous donne explicitement et simplement la plus petite distance atteinte entre les deux corps entre t et t + dt. Structures A. Si l’on veut que les vitesses varient effectivement. sinon. 2. avec des notations supplémentaires : − − − − N (u) = ∆→ r (t)2 + 2(u − t)∆→ r (t) · ∆→ v (t) + (u − t)2 ∆→ v (t)2 La norme. lors de l’évolution pas à pas des coordonnées des disques. à tout instant : → − − − 1. M = ( r A − r B ) × mA v A . toujours positive. un disque peut déjà avoir traversé l’autre et en être ressorti en t + dt. − − Soit N (u) = (→ r A (u) − → r B (u))2 le carré de la distance en question. est minimale au point u tel que ∂u N (u) = 0. et la constance du moment cinétique que les vitesses varient parallèlement − à ∆→ r . M = ∆→ r × mA → v A. il ne nous reste plus qu’une − − possibilité : mutliplier par −1 la composante des → v i selon ∆→ r . d’où. car. le minimum est atteint en t + dt. le minimum est atteint en t. si la vitesse est trop élevée. la norme des vitesses est conservée. 3. mB La constance de Ec nous informe que dans ce repère. 3. d’où mA → v A = −mB → v B. de décider qu’un choc aura lieu entre t et t + dt rien qu’en estimant la distance entre les deux disques candidats à la collision à l’instant t. si tm < t. La solution consiste à expliciter le minimum de la distance entre les disques en fonction du temps. Ce qui fournit un algorithme simple de rebond. variant entre t et t + dt. si t < tm < t + dt. en notant ∆ r = r A − r B .

cpp). Attention. Fichiers séparés Fichiers séparés Nous allons poursuivre dans ce TP les simulations de gravitation et de chocs élastiques entamées la semaine dernière.zip sur la page habituelle.h une structure représentant un vecteur du plan. Y placer les fonctions fournies à l’avance au début du TP4 (InitRandom. vous pouvez quitter Visual C++.h. Structure Vector : Créer dans un nouveau fichier Vector.cpp. Inclure la protection contre la double inclusion vue en cours (#pragma once).cpp au projet avec "Fichier / Ajouter un nouvel élément / Fichier C++". et un fichier de déclarations Ajouter un nouveau fichier d’en-tête nommé Tools. le nouveau fichier est créé dans le dossier contenant le fichier "solution" (extension sln) de Visual Studio qui se trouve dans le "build directory" de CMake. Vous devez donc déplacer ce fichier dans le dossier des sources (le même que Gravitation. ChocSimple et Collision). 3. Déclarer (et non définir) les opérateurs et fonction suivants : 223 . De bonnes bases : Télécharger le fichier Tp5.cpp et de Gravitation. Acceptez sa proposition. Ne pas oublier les lignes suivantes.2 Vecteurs 4. Choc. Si vous avez été plus loin et/ou si vous préférez réutiliser votre propre solution. Random. 1.5.. Vous n’avez pas besoin de relancer CMake ou de fermer Visual Studio. il relance CMake en coulisses. Un fichier de définitions.txt et vous proposer de recharger le projet.. A. ainsi que la définition de dt.5 A. que l’on pourra retirer de Gravitation.h". Y placer les déclarations des fonctions mises dans Tools. en séparant dans différents fichiers les différentes fonctions et structures utilisées. en les retirant de Gravitation. . le décompresser et lancer Visual C++. Travaux Pratiques A.cpp un #include "Tools. et relancer Visual C++.cpp : # include <cstdlib > # i n c l u d e <ctime > using namespace s t d . en retirant celle-ci de main. avec deux membres de type double. Ne pas oublier le mécanisme de protection contre la double inclusion. incluse). remplacer le fichier Gravitation.. Ajouter un nouveau fichier source nommé Tools.1 Fonctions outils 2.cpp par celui que vous aurez récupéré dans votre TP4. N’oubliez pas de modifier les arguments de AddImagineExecutable du CMakeLists. Celui-ci va détecter le changement du CMakeLists.cpp. Rajouter au début de Tools.cpp.. Le projet Gravitation contient une solution partielle au TP4 (jusqu’à la question 7. A.5.txt pour y ajouter Tools.5.A.

o p e r a t o r ∗ ( Vector a .cpp. Mettre un #include du fichier d’en-tête correspondant et définir les opérateurs qui y sont déclarés (Rappel : sqrt est défini dans le fichier d’en-tête système <cmath> .cpp.4 Retour à la physique 9. il faut les passer en argument. Ajouter les #include nécessaires dans ce dernier fichier et dans Gravitation. la deuxième version peut utiliser la première dans sa définition. Il ne devrait plus rester dans Gravitation. Diminuer le pas de temps de calcul : Afin d’éviter les erreurs dues à la discrétisation du temps.cpp.A.. ne pas oublier non plus le using namespace std..cpp d’autre fonction que main. diminuer le pas de temps dt.) A.. // // // // // Somme d e deux v e c t e u r s D i f f é r e n c e d e deux v e c t e Norme e u c l i d i e n n e d ’ un v M u l t i p l i c a t i o n p a r un s c M u l t i p l i c a t i o n p a r un s c 5. # i n c l u d e " Vector .3 Balle à part 7. Structure Balle : Déplacer la structure Balle dans un nouveau fichier d’en-tête Balle. Des soleils par milliers. Déclarer dans Balle. qui permet d’utiliser cette fonction). Fichiers séparés Vector Vector double Vector Vector A. il faut aussi ajouter ces lignes : # i n c l u d e <Imagine/Graphics . comme ces constantes ne sont définies que dans Gravitation. Travaux Pratiques o p e r a t o r +( Vector a .cpp. o p e r a t o r ∗ ( double lambda .cp et faire les adaptations nécessaires (par exemple. o p e r a t o r −( Vector a .. Fonctions et opérateurs sur les Vector : Créer un nouveau fichier Vector.5. Vector a ) ..h les fonctions définies dans Balle. Utiliser autant que possible les opérateurs et fonction définis dans Vector. h " 8. Lancer plusieurs fois le programme...cpp. double lambda ) .h. Vector b ) .5.cpp les vitesses et positions par des objets de type Vector (y compris dans la définition de la structure Balle). Astuce : une fois qu’une version de operator∗ est définie.5.. h> using namespace Imagine . 10.01 (voire à 0. dans le calcul du déplacement de l’astéroïde. : Placer 10 soleils aléatoirement (et en tenir compte à l’affichage. pour le fixer à 0. si des fonctions utilisent largeur ou hauteur. A. Vector b ) . 6. Fonctions associées : Déplacer toutes les fonctions annexes prenant des Balle en paramètres dans un nouveau fichier Balle. Régler la fréquence d’affichage en conséquence (inversement proportionnelle à dt).). Vecteur vitesse et vecteur position : Systématiquement remplacer dans Gravitation. norme2 ( Vector a ) . 224 . Puisque Balle utilise les types Vector et Color.001 si la machine est assez puissante).

ajouter un nouveau projet appelé Duel. À vous de jouer ! Transformer le projet Duel. utiliser la fonction Collision. en un jeu de tir. Jeu de tir 12. utiliser la fonction GetMouse pour connaître la position de la souris . Mettre au début de Duel. peut lancer une Balle avec la vitesse initiale de son choix.h. Fichiers séparés Chocs élastiques simples 11. Chacun des deux joueurs a une position fixée. ils ne sont pas recopiés.h. 225 . Ne pas refaire deux fois le travail : Comme nous aurons besoins des mêmes fonctions dans ce projet que dans le projet Gravitation. 16. dans cette même solution.txt.txt du répertoire Duel. en déduire la vitesse désirée par le joueur en retranchant à ces coordonnées celles du centre de la boule à lancer. Conseils pratiques : positionner symétriquement les joueurs par rapport au centre.. Initialisation correcte : Modifier la fonction de placement des soleils de façon à ce que les soleils ne s’intersectent pas initialement. à l’aide des fonctions définies auparavant. Le gagnant est le premier qui réussit à atteindre l’autre.5. et rétablir dt à une valeur plus élevée. Essayer de compiler Duel. Travaux Pratiques A.h" par exemple). et qu’ils soient à une distance minimale de 100 pixels des emplacements des joueurs. 15. Les fichiers sont les mêmes que dans le projet Gravitation.. Chaque joueur.. Faire rebondir l’astéroïde : Faire subir des chocs élastiques à l’astéroïde à chaque fois qu’il s’approche trop d’un soleil.cpp. On ajoute un dossier Duel au même niveau que Gravitation et on modifie le CMakeLists. Tools. inspirez-vous de celui de Gravitation.A. et divers soleils sont placés aléatoirement dans l’écran. de façon à ce qu’il ne rentre plus dedans (fonction ChocSimple). Vector.h. et en multipliant par un facteur 0. Ouvrir un nouveau projet : Afin de partir dans deux voies différentes et travailler proprement. il faut lui indiquer où les trouver. Tools.cpp. Balle. par exemple 0. à tour de rôle. Pour savoir si deux corps sont sur le point d’entrer en collision. de préférence à mi-hauteur en laissant une marge d’un huitième de la largeur sur le côté ./ Gravitation/Tools. et disparaissant au bout de 250 pas de temps d’affichage. la balle subissant les effets de gravitation des divers soleils.1 (modifier la fréquence d’affichage en conséquent). ajouter au projet (sans en créer de nouveaux !) les fichiers Vector. Comme le compilateur n’arrive pas à trouver les fichiers inclus.cpp. (#include " . qui ne sont pas dans le même répertoire. Pour le CMakeLists. 13.cpp. Balle. à deux joueurs.cpp (fichier à placer dans le répertoire Duel) les #include correspondants. 14.00025. Améliorations : Faire en sorte qu’il y ait systématiquement un gros soleil au centre de l’écran (de masse non nécessairement conséquente) afin d’empêcher les tirs directs.

Tout faire bouger. tout faire rebondir : On retourne dans le projet Gravitation. 226 . Fichiers séparés A. y compris les soleils.. la fonction Chocs (qui fait rebondir les deux corps).5.5 – Corps célestes et jeu de tir.A. Travaux Pratiques F IGURE A. Chocs élastiques 17. Tout faire évoluer.. pour les chocs élastiques. Faire en sorte que lors de l’initialisation les soleils ne s’intersectent pas. Utiliser.

A.1 Mélanger un tableau 1.6 – Deux tris en cours d’exécution : tri à bulle et Quicksort. 2. en accédant à une valeur du tableau T0. étudier le projet : Télécharger le fichier Tp6_Initial. nous allons programmer quelques algorithmes de tri d’éléments dans un tableau.h pour découvrir les fonctions de cette interface graphique.. 3.A. Sécuriser les accès tableau Pour s’assûrer qu’on ne sort pas des bornes du tableaux. Pour commencer. ajouter des appels à la fonction assert (vue en cours) dans valeur et echange.h). Entraînez-vous à les utiliser dans la fonction main(). il ne fait qu’initialiser un tableau et l’afficher. qui affiche le tableau et permet de visualiser les opérations qu’on réalise dessus (figure A. Ouvrir tools. qui gère l’interface graphique et quelques fonctions utiles à la comparaison des différents tris. et le couple (tools..6). et que l’on puisse voir comment se passent chacun des tris. Travaux Pratiques A.cpp. Les tris F IGURE A.cpp pour voir comment elles sont utilisées (les lignes commentées sont là pour montrer comment vous utiliserez les fonctions melange_tableau et tri_selection que vous allez programmer). puis main.6 Les tris Dans ce TP.cpp. Le projet est séparé entre le fichier main. le décompresser et lancer Visual C++. dans lequel on programmera les algorithmes de tri. échange de 2 valeurs Pour que les opérations que vous effectuerez sur les tableaux soient affichées automatiquement. et en permutant 2 de ses éléments. une interface graphique a été programmée.6. Pour que cela soit interactif.6. Constater ce qui se passe à l’exécution lorsqu’on appelle par exemple 227 . Accès à un tableau. Pour l’instant. A.zip sur la page habituelle. Exécuter le projet. il faudra que vous utilisiez uniquement les fonctions valeur et echange déclarées dans tools. tools.h (ne pas accéder directement au tableau avec T[i]).

et à chaque fois qu’on est sur un élément.cpp bien sûr) Idée : le mélange le plus rapide (et le plus efficace) consiste à parcourir le tableau une fois.2 Tris quadratiques Les 3 algorithmes de tri qui suivent trient un tableau en temps 0(n2 ). 5. le placer en 2ème position. mettre cet élément au début (par une permutation)... on l’échange avec son voisin de droite si ce dernier est plus petit que lui. pour pouvoir toucher également au tri Quicksort. Programmer la fonction void tri_selection (double T[]. int taille) et regarder comment ça se passe. Travaux Pratiques v a l e u r ( T0 . Mélanger un tableau Une fonction déclarée dans tools.. 6. t a i l l e . Tri insertion En général.h n’existe pas encore dans tools. Il consiste à ajouter un à un les éléments non triés au bon endroit parmi ceux qui sont déjà triés : si nécessaire on échange les 2 premiers éléments du tableau pour les mettre dans l’ordre. 7. A. i n t t a i l l e ) qui.. comme son nom l’indique. par des échanges de proche en proche.cpp pour tirer un entier entre 0 et a-1). Programmer void tri_insertion (double T[]. t a i l l e ) . int taille ). Tri sélection C’est le tri le plus naïf. mélange un tableau. jusqu’à ce qu’il soit à la bonne position par rapport aux 2 premiers. parcourir une seconde fois le tableau (de la 2ème à la dernière case) pour trouver le second plus petit élément.. Si vous avez peu de temps. et ainsi de suite. Il consiste à parcourir le tableau une première fois pour trouver le plus petit élément.. int taille ) et regarder comment ça se passe. Constater qu’on n’est pas obligé de parcourir tout le tableau à chaque fois et améliorer la fonction. puis le 4ème. Programmer tri_bulle(double T[]. et à permuter chacun des éléments avec un autre choisi au hasard (utiliser la fonction int random(int a) définie dans tools. Ajoutez-la (dans tools. Vous pouvez alors décommenter les lignes commentées dans main() et exécuter. Tri à bulle Le tri à bulle consiste à parcourir n fois le tableau. 4.A. c’est à dire que le temps pour trier un tableau est proportionnel au carré de la taille de ce tableau. puis (si nécessaire) on déplace le 3ème élément vers la gauche. vous pouvez ne faire qu’un ou deux des trois.cpp : la fonction void melange_tableau ( double T [ ] . et ainsi de suite.6.6. Les tris A. c’est à peu près l’algorithme qu’utilise un être humain pour trier un paquet de cartes. des fiches. 228 .

3 A. des différences apparemment anodines dans la façon de programmer cette fonction peuvent la faire échouer dans des cas particuliers : demandezvous ce qui se passe si le pivot est la valeur minimale ou la valeur maximale du tableau. le premier à partir du 2ème élément (le 1er étant le pivot) et avançant vers la droite. à gauche de l’intersection tous les éléments sont inférieurs au pivot.6. ainsi que la fonction void quicksort(double T []. sur le premier élément inférieur au pivot qu’il rencontre — on échange alors les 2 éléments. et à droite ils sont supérieurs — on échange le pivot (en 1ère position) avec le dernier des éléments inférieurs pour obtenir ce qu’on désire Attention. et les 2 index continuent d’avancer.6. Quicksort On peut maintenant écrire une fonction void quicksort_recursif (double T[]. puis le pivot. 9. — Ne pas oublier d’exécuter la fonction pour vérifier qu’elle marche bien ! — Idée : — on utilise 2 index qui parcourent le tableau. int debut. Fonctions récursives Le principe même de la statégie diviser pour régner implique que la fonction qui effectue le tri quicksort va s’appeler elle-même pour trier une sous-partie du tableau. on va utiliser 2 arguments debut et fin qui indiquent qu’on ne réalise le tri qu’entre les indices debut et fin. Les tris Quicksort L’algorithme : le tri Quicksort adopte la stratégie “diviser pour régner” qui consiste à réduire le problème du tri d’un tableau de taille n aux tris de 2 tableaux de taille n2 : on choisit un élément dans le tableau (on le prend en général au hasard. échange les éléments du tableau de manière à ce qu’on aie d’abord les éléments inférieurs au pivot. puis les éléments supérieurs au pivot. Changer la fonction pivot en lui ajoutant ces 2 arguments. 229 . On sépare ensuite les autres éléments entre ceux inférieurs au pivot et ceux supérieurs au pivot. 8. mais par commodité on prendra ici le premier élément du tableau) qu’on appelle pivot. Il n’y a plus alors qu’à trier ces deux moitiés. et en ne la faisant effectivement travailler que entre ces 2 indices (par exemple. Pivot — Créer une fonction int pivot(double T[]. int fin ).A. int taille ) qui prend comme pivot le premier élément du tableau. le second à partir du dernier élément et avançant vers la gauche — le premier index s’arrête sur le premier élément supérieur au pivot qu’il rencontre. et qui renvoie la nouvelle position du pivot. et le second index. le pivot est initialement à l’indice debut) 10. et ainsi de suite — quand les 2 index se rencontrent. int taille . Travaux Pratiques A. En pratique. qui contient l’algorithme. int taille ).

il faut utiliser des tableaux de taille variable. il est intéressant de les faire fonctionner et de les comparer sur des tableaux de grande taille. Mode Release On peut changer le mode de compilation en le passant de Debug à Release. mais qui sera celle qu’on utilisera dans main() (car plus simple).4 Gros tableaux Maintenant qu’on a vu graphiquement comment marchent ces algorithmes. D’autre part il faut désactiver l’affichage : init_tools (512. Travaux Pratiques qui ne fait qu’appeler cette dernière. Créer 2 variables globales dans tools. compter et afficher le nombre de lectures et d’écritures. A. Vérifier que l’exécution des algorithmes est effectivement bien plus rapide en mode Release ! 230 . Au fait. Nombre de lectures et d’écriture Pour comparer plus rigoureusement 2 algorithmes.6. 12. false ). Pour cela. 11. en combien d’opérations s’effectue en moyenne Quicksort ? 13. et à la fin des fonctions où se trouvent ces déclarations.6. Tableaux de taille variable Si on veut tester nos algorithmes sur des tableaux de grande taille. par double ∗t = new double[taille]. Remplacer toutes les lignes de type double t[ taille ]. valeur.A. Les tris A. on peut comparer le nombre de lectures du tableau et le nombre d’écritures dans le tableau. on peut enregistrer dans une nouvelle variable globale timer0 le temps qu’il est avant le tri : timer0 = double(clock())/CLOCKS_PER_SEC . 14. ajouter la ligne delete [] t ..cpp et modifier les fonctions init_tri. Temps de calcul Il est intéressant également d’avoir les temps de calcul exacts. echange et fin_tri pour initialiser. et la retrancher au temps qu’il est après le tri (modifier les fonctions init_tri et fin_tri pour faire ce calcul et l’afficher).

Compiler en mode Release pour utiliser la "vraie" gestion du tas (Le mode Debug utilise une gestion spécifique qui aide à trouver les bugs et se comporte différemment.j) est un byte (entier de 0 à 255) allant de 0 pour le noir à 255 pour le blanc. nous allons jouer avec les tableaux bidimensionnels statiques (mais stockés dans des tableaux 1D) puis dynamiques. Faire. Dans ce TP.7. Récupérer le projet : Télécharger le fichier Tp7_Initial.j) en t[i+W*j] : 231 . le décompresser et lancer Visual C++. Niveaux de gris : Une image noir et blanc est représentée par un tableau de pixels de dimensions constantes W=300 et H=200.7). A. Dans un tableau de byte mono-dimensionnel t de taille W*H mémorisant le pixel (i.2 Tableaux statiques 3.. déformation. dans une boucle infinie. i est l’horizontale et j la verticale. contraste et contours).7 A. (Utiliser Ctrl+Shift+Echap pour accéder au gestionnaire de tâches). Saturer la mémoire : Rien à voir avec ce qu’on va faire après mais il faut l’avoir fait une fois. L’origine est en haut à gauche..zip sur la page habituelle. des allocations de 1000000 entiers sans désallouer et regarder la taille du process grandir. nous travaillerons avec des images (figure A.1 Allocation 1. Pour changer de nos passionnantes matrices. Travaux Pratiques A.. 2. Chaque pixel (i.A.. flou.) A. relief.7.7. Images Images F IGURE A.7 – Deux images et différents traitements de la deuxième (négatif.

.jpg".b.7. i n t &W.cpp et un image. i n t &H) .7. Images A.W.g. Fichiers : Créer un image.r. — Idem avec un dégradé du noir au blanc (attention aux conversions entre byte et double). i n t H) . — Idem avec une image blanche. 4. void DetruitImage ( byte ∗ I ) . Dimensions au clavier : Modifier le programme précédent pour que W et H ne soient plus des constantes mais des valeurs entrées au clavier.b.g. void AfficheImage ( byte ∗ I . byte ∗ ChargeImage ( char ∗ name .. une image en couleur stockée dans trois tableaux r. Couleurs : Afficher.h en conséquence. Ne pas oublier de désallouer. Utiliser # d e f i n e _USE_MATH_DEFINES # i n c l u d e <cmath> pour avoir les fonctions et les constantes mathématiques : M_PI vaut π. vert. A.g. — Charger cette image et l’afficher.jpg" qui est dans le répertoire du projet.r. détruire. charge le fichier "ppd.. Ne pas oublier les désallocations.t.b. — Idem avec t(i.3 Tableaux dynamiques 5. Travaux Pratiques — Stocker une image noire et l’afficher avec putGreyImage(0. Utiliser la fonction click() pour attendre que l’utilisateur clique avec la souris entre l’affichage précédent et ce nouvel affichage. i n t W.H). A.5 Fonctions 8. afficher et charger les images : byte ∗ AlloueImage ( i n t W.0. Attention : ne pas oublier de désallouer les tableaux r. les remplit avec les pixels de l’image. 232 .7.H) fait la même chose mais convertit l’image en noir et blanc. i n t H) .. bleu). et affecte aussi W et H en conséquence.W.b avec delete [] après usage.jpg".W.t. 9. j) = 128 + 128 sin(4πi/W ) sin(4πj/H) (cf figure A. Image couleur : La fonction loadColorImage("ppd. A. Image noir et blanc : La fonction loadGreyImage("ppd.A.H). alloue elle-même les tableaux r. Faire des fonctions pour allouer. Afficher l’image en noir et blanc.0. Découper le travail : On ne garde plus que la partie noir et blanc du programme.7). 7.7.g. avec putColorImage(0. g et b (rouge.4 Charger un fichier 6.H).W.

i n t i . 12. i n t j ) .7. Travaux Pratiques A. j) − I(i p − 1. i n t w. Contours ( Image I . j − 1). s’amuser : — Rétrécir une image. puis la norme du gradient |∇I| = d2x + d2y et afficher en blanc les points où cette norme est supérieure à un seuil. i n t j . — Approcher cette dérivée par différence finie : elle est proportionnelle à I(i + 1. on peut par exemple changer le contraste. rajouter : byte Get ( Image I . — Au lieu du négatif. Images Structure 10. Deforme ( Image I ) . Comment ? 233 .7. j))/2 et la dérivée verticale dy . S’il reste du temps. On pourra utiliser un sinus pour aller de 0 à W-1 et de 0 à H-1 de façon non linéaire.A. Traitements : Ajouter dans main. (d) Contours : calculer par différences finies la dérivée horizontale dx = (I(i + 1. byte g ) . }. Indépendance : Pour ne plus avoir à savoir comment les pixels sont stockés. et les utiliser : (a) Negatif : changer le noir en blanc et vise-versa par une transformation affine. Attention aux pixels du bords qui n’ont pas tous leurs voisins (on pourra ne pas moyenner ceux-là et en profiter pour utiliser l’instruction continue !). j) = I(f (i. A. double s e u i l ) . h .7 Suite et fin 13.cpp différentes fonctions de modification des images Image Image Image Image Image N e g a t i f ( Image I ) . — S’arranger pour en faire une image allant de 0 à 255. 11. (c) Relief : la dérivée suivant une diagonale donne une impression d’ombres projetées par une lumière rasante. (e) Deforme : Construire une nouvelle image sur le principe J(i. Principe : Modifier le programme précédent pour utiliser une structure : s t r u c t Image { byte ∗ t . (b) Flou : chaque pixel devient la moyenne de lui-même et de ses 8 voisins. j)) avec f bien choisie.7. AlloueImage() et ChargeImage() pourront retourner des Image. i n t i . void S e t ( Image I . Flou ( Image I ) . j + 1) − I(i − 1.6 A. R e l i e f ( Image I ) .

.cpp et Vector.. Il suffit de rebondir sur la fonction void drawLine ( i n t x1 . Ajouter dans main. Premiers objets et dessins de fractales A.8).A.8. La figure ci-dessous illustre sa construction.1 Le triangle de Sierpinski 1. Etudier la structure Vector définie dans les fichiers Vector. Cette fonc- F IGURE A. i n t y1 . Travaux Pratiques Premiers objets et dessins de fractales F IGURE A. le décompresser et lancer Visual C++. tion prendra en paramètres les trois points du triangle en cours et l’epaisseur du 234 . Récupérer le projet : Télécharger le fichier Tp8_Initial.zip sur la page habituelle. 3. Ecrire une fonction récursive pour dessiner le triangle de Sierpinski. i n t x2 . Triangle de Sierpinski : C’est la figure fractale choisie par l’ENPC pour son logo. i n t y2 .9 – Construction du triangle de Sierpinski. Dans ce TP. c o n s t Color& c .8. Nous allons transformer une structure vecteur en une classe et l’utiliser pour dessiner des courbes fractales (figure A.8 – Fractales.8 A. Le dernier paramètre contrôle l’épaisseur du trait. 2. nous allons nous essayer à la programmation objet. i n t pen_w ) } d’Imagine++. Interfaçage avec Imagine++ : La structure Vector ne comporte pas de fonction d’affichage graphique.cpp des fonctions drawLine et drawTriangle prenant des Vector en paramètres.h. A.

.A.cpp. Premiers objets et dessins de fractales trait. Y incorporer toutes les fonctions et les opérateurs. 5.. Dessin récursif d’un arbre : Nous allons maintenant dessiner un arbre. Quelles sont les modifications à apporter dans main. Les trois sous-triangles seront dessinés avec un trait plus fin. Ne pas oublier la condition d’arrêt de la récursion ! Utiliser cette fonction dans le main en lui fournissant un triangle initial d’épaisseur 6. Passer en public le strict nécessaire. Ecrire une fonction récursive pour dessiner une telle courbe. L’idée est de cacher aux utilisateurs de la classe Vector les détails de son implémentation. La figure cidessous illustre le résultat obtenu pour différentes profondeurs de récursion.8.8. A.y. 235 . Accesseurs pour les membres : Rajouter des accesseurs en lecture et en écriture pour les membres. Placer une constante globale DIM égale à 2 au début de Vector.2 Une classe plutôt qu’une structure 4. et les utiliser systématiquement dans le programme principal.. Vous aurez besoin F IGURE A.cpp ? 8. de la fonction Rotate de la classe Vector. A.8. NB : la fonction Rotate et les accesseurs que nous avons écrits ne se généralisent pas directement aux dimensions supérieures.10 – Construction de l’arbre. Pour cela il faut partir d’un tronc et remplacer la deuxième moitié de chaque branche par deux branches de même longueur formant un angle de 20 degrés avec la branche mère. Travaux Pratiques A. par un tableau double coord[2]. Vecteurs de dimension supérieure : L’avantage de cette dernière implémentation est qu’elle se généralise aisément à des vecteurs de dimension supérieure. Deuxième implémentation : Modifier l’implémentation de la classe Vector en remplaçant les membres double x. Classe vecteur : Transformer la structure Vector en une classe.h et rendre la classe Vector indépendante de la dimension. Les laisser tels quels pour l’instant. Faire les modifications nécessaires dans main.3 Changer d’implémentation 7. 6.

Premiers objets et dessins de fractales A.11 – Construction de la courbe de Koch. F IGURE A.A. Flocon de neige : Il s’obtient en construisant une courbe de Koch à partir de chacun des côtés d’un triangle équilatéral.8. Ecrire une fonction récursive pour dessiner une courbe de Koch. 236 . Courbe de Koch : Cette courbe fractale s’obtient en partant d’un segment et en remplaçant le deuxième tiers de chaque segment par deux segments formant la pointe d’un triangle équilatéral.4 A.8. Travaux Pratiques Le flocon de neige 9. 10.

La solution de départ comporte deux fichiers. Il s’agit ici de concevoir un objet Serpent doté des méthodes adéquates. A.9.h et utils. Dans ce jeu. Tron F IGURE A. ses méthodes. avec la convention que la longueur totale est bornée à nmax éléments). On pourra dans un premier temps ne pas gérer les collisions (avec le bord et avec lui-même). 2. (sur papier) Définir l’interface de la classe Serpent (c’est-à-dire lister toutes les fonctionnalités nécessaires). Travaux Pratiques A. 237 . D’abord programmer un jeu de Serpent à un joueur. 4. Il s’agit de ne pas se rentrer dedans ni de percuter les murs.exe vous donne une idée du résultat recherché.1 Serpent Nous allons procéder en deux temps. et ne les rajouter que dans un second temps. ce qui est public.cpp.h. plus une fonction jeu_1p exploitant les capacités du Serpent pour reproduire le comportement désiré. Implémenter la classe Serpent (c’est-à-dire programmer les méthodes que vous avez déclarées).9 Tron Dans ce TP. Le premier joueur qui percute sa propre trace ou celle de son adversaire a perdu. A. (sur papier) Réfléchir à l’implémentation de la classe Serpent : comment stocker les données ? comment programmer les différentes méthodes ? (lire en préliminaire les remarques du paragraphe suivant). utils. Le programme serpent. ce qui ne l’est pas. dans lequel chaque joueur pilote un mobile qui se déplace à vitesse constante et laisse derrière lui une trace infranchissable. Soumettre le résultat de vos réflexions à votre enseignant pour valider avec lui les choix retenus.A. qui contiennent une structure point (qu’il faudra éventuellement étoffer de méthodes utiles) et une fonction destinée à récupérer les touches clavier pour l’interaction avec les joueurs.9. Il s’agit d’un jeu à 2 joueurs. Dans un fichier serpent. nous allons programmer le jeu TRON. le joueur pilote un Serpent qui s’allonge petit à petit (d’un élément tous les x tours. écrire la déclaration de votre classe Serpent : ses membres. 3. Votre travail se décompose en 6 étapes : 1.12 – Jeu de Tron. 5.

nous allons facilement pouvoir implémenter le jeu Tron. haut. un tableau de 4 points dir de telle manière que. X. créer une fonction jeu_2p implémentant un jeu de serpent à 2 joueurs. qui sont des images à affichage rapide. il suffit d’allonger nos serpents à chaque tour. droite en haut et haut en gauche . cette fonction correspond donc à un quart de tour dans le sens trigonométrique. la fonction p → p + dir[d] renvoie : (a) pour d=gauche le point correspondant au décalage de p de 1 vers la gauche. int ( ’X’). Programmer la fonction jeu_1p utilisant un Serpent. Le principe de ce jeu est que chaque joueur pilote une moto qui laisse derrière elle une trace infranchissable. Nous allons utiliser pour cela les NativeBitmap d’Imagine++. (b) la fonction x → (x − 1)%4 transforme gauche en haut. (d) pour d=bas le point correspondant au décalage de p de 1 vers la bas. Tron A.9.3 Graphismes Petit bonus pour les rapides : nous allons voir comment gérer des graphismes un peu plus sympas que les rectangles uniformes que nous avons utilisés jusqu’ici. Passage à deux joueurs. La fonction Clavier() renverra donc les entiers int ( ’S’ ). Travaux Pratiques 6.A. L’objectif est de remplacer le carré de tête par une image que l’on déplace à chaque tour. A partir de la fonction jeu_1p. Le programme tron. 1. bas. A. (b) pour d=haut le point correspondant au décalage de p de 1 vers la haut. haut en droite. Remarque : Dans le fichier utils.h sont définis : 1. int ( ’D’) et int ( ’F’).exe vous donne une idée du résultat recherché. soit un seul appel à la fonction Clavier() par tour. droite en bas et bas en gauche . On utilisera pour ce joueur les touches S. Remarque : on ne gèrera qu’une touche par tour.2 Tron A partir du jeu de Serpent réalisé précédemment. Le but est de survivre plus longtemps que le joueur adverse.9.9. 2. moyennant la définition d’une fonction permettant de faire la somme de deux points. (c) pour d=droite le point correspondant au décalage de p de 1 vers la droite. Ultimes réglages (a) Gérer la collision entre les deux serpents. 2. Pour implémenter cela. D et F. 4 entiers gauche. A. droite de telle manière que : (a) la fonction x → (x + 1)%4 transforme gauche en bas. Pour charger une image dans une NativeBitmap on procède ainsi : 238 . (b) Le principe de Tron est que la trace des mobiles reste. bas en droite. cette fonction correspond donc à un quart de tour dans le sens des aiguilles d’une montre.

Travaux Pratiques A. h .w. L’affichage d’une NativeBitmap à l’écran se fait alors avec la méthode : void putNativeBitmap ( i n t x . Tron / / E n t i e r s p a s s é s p a r r é f é r e n c e l o r s du c h a r g e m e n t d e l ’ i m a g e p o u r / / qu ’ y s o i e n t s t o c k é e s l a l a r g e u r e t l a h a u t e u r d e l ’ i m a g e i n t w. / / Chargement d e l ’ i m a g e byte ∗ rgb . i n t y . / / D é c l a r a t i o n de l a NativeBitmap NativeBitmap ma_native_bitmap (w. Utiliser l’image explosion.bmp fournies. / / On p l a c e l ’ i m a g e d a n s l a N a t i v e B i t m a p ma_native_bitmap . On pourra utiliser les images moto_blue. 239 . h ) . rgb . 2.A. h ) . Remplacer dans le serpent l’affichage de la tête par l’affichage d’une image. loadColorImage ( " n o m _ f i c h i e r . NativeBitmap nb ) 1. rgb . 0 .w. setColorImage ( 0 .bmp lors de la mort d’un des joueurs.bmp et moto_red.9. h ) . bmp" .

.

MAGENTA. Elles s’appuient pour cela sur les projets Qt (graphisme 2D et 3D) et Eigen (algèbre linéaire). 2 5 5 ) .txt doit comporter ImagineUseModule ( MonProgramme Images ) pour que l’édition de liens (link) réussisse. Imagine++ est un logiciel libre. L’équivalent pour un argument de type string est stringSrcPath : . Tout est englobé dans un namespace Imagine. BLUE. Color rouge = Color ( 2 5 5 . YELLOW. mais si vous le distribuez sous une forme modifiée vous devez offrir selon les mêmes termes les sources de vos modifications. 0 ) . Pour utiliser un module. Color b l a n c = Color ( 2 5 5 . GREEN. 2 5 5 . vous pouvez donc l’utiliser et le distribuer à votre guise (et gratuitement !). Très pratique. Un certain nombre de constantes de ce type sont déjà définies : BLACK.1 Common Le module Common définit entre autres la classe Color codée par un mélange de rouge. un fichier source doit l’inclure # i n c l u d e <Imagine/Images . la quantité de chacun codée par un entier entre 0 et 255 : Color n o i r = Color ( 0 . B. 0 . vert et bleu. RED. détaillant l’insallation et l’utilisation. Ceux-ci proposent une richesse de possibilités bien plus importantes que Imagine++ mais en contrepartie Imagine++ est plus simple à utiliser. donc si vous voulez éviter de préfixer les noms par Imagine:: vous devez utiliser dans votre code using namespace Imagine . h> pour une compilation correcte.B. srcPath fait précéder la chaîne de caractère argument par le chemin complet du répertoire contenant le fichier source. par exemple Images. CYAN. Un type byte (synonyme de unsigned char) est défini pour coder une valeur entière entre 0 et 255. Une documentation complète est disponible sur la page Web d’Imagine++. Imagine++ Annexe B Imagine++ Imagine++ est un ensemble de bibliothèques permettant de faire simplement du graphisme et de l’algèbre linéaire. et votre fichier CMakeLists. WHITE. 0 . 0 ) .

hauteur ) . g . ces fonctions allouent de la mémoire qu’il ne faut pas oublier de libérer après usage. } / / D e s s i n e a v e c c o i n h a u t g a u c h e en ( 0 . / / S é l e c t i o n p o u r l e s p r o c h a i n s t r a c é s drawString ( 1 0 . ce n’est pas le sens mathématique usuel !). g . byte ∗ g .. 4 9 0 . t x t " ) . on encadre le code de tracé par : noRefreshBegin ( ) . / / Ne p a s o u b l i e r ! 242 . hauteur . Le point (0. Les coordonnées 2D sont en pixel. s t r i n g s = " mon_fichier . l a r g e u r . En d’autres termes. 1 0 . j p g " ) << endl . . Pour des tableaux de taille non connue à la compilation (allocation dynamique). j p g " ) . MAGENTA) . d e l e t e [ ] g . utiliser FMatrix et FVector. Graphics B. / / M e t t r e du t e x t e endGraphics ( ) . t x t " .2. i f ( ! loadGreyImage ( s r c P a t h ( " image .. "Du t e x t e " . Pour faire une animation.0) est donc le coin haut-gauche de la fenêtre (pour les fonctions de tracé) ou de l’écran (pour openWindow). utilisant l’allocation statique comme indiqué par le préfixe F (fixed).RED ) .BLUE ) . / / D i a g o n a l e Window w = openWindow ( 1 0 0 . saveColorImage) dans un fichier. openWindow ( 5 0 0 . 1 0 ) . exit (1). / / l a r g e u r 4 8 0 . / / T a i l l e d e f e n ê t r e drawRect ( 1 0 . 4 9 0 . 0 ) : putGreyImage ( 0 . s = stringSrcPath ( s ) . / / A t t e n d un c l i c s o u r i s a v a n t d e f e r m e r l e s f e n ê t r e s Si on a beaucoup de dessins à faire à la suite et qu’on veut n’afficher que le résultat final (c’est plus esthétique). 1 0 0 ) . Imagine++ c o n s t char ∗ f i c h i e r = s r c P a t h ( " m o n _ f i c h i e r .2 Graphics Le module Graphics propose du dessin en 2D et 3D. le fichier sera trouvé quel que soit l’emplacement de l’exécutable. 1 0 . / / A u t r e f e n ê t r e setActiveWindow (w) . / / Temps en m i l l i s e c o n d e s On peut charger une image (loadGreyImage. loadColorImage) ou sauvegarder (saveGreyImage.B. 4 8 0 . h a u t e u r 480 drawLine ( 1 0 . noRefreshEnd ( ) . La classe template FArray s’utilise pour des tableaux de taille petite et connue à la compilation (allocation statique). Pour les matrices et vecteurs. il est utile de faire une petite pause entre les images pour réguler la cadence : m i l l i S l e e p ( 5 0 ) . Les équivalents dynamiques sont dans LinAlg. / / Coin haut −g a u c h e ( 1 0 . l’axe des x est vers la droite et l’axe des y vers le bas (attention. B. 0 . Attention. i n t l a r g e u r . l a r g e u r . 5 0 0 ) . 1 0 . utiliser Array. 4 8 0 . hauteur ) ) { c e r r << " I m p o s s i b l e d ’ o u v r i r l e f i c h i e r " << s r c P a t h ( " image .

/ / M o d i f i e quand même l e p i x e l ( 1 0 . im1 ( 1 0 . / / im2 a é t é a f f e c t é e Pour faire une vraie copie plutôt qu’un lien. / / D e s s i n e d a n s l a f e n ê t r e a c t i v e im ( 0 . png ’ ’ ) << endl .1)=1.3.0 f . exit (1). / / Matrice nulle I (0 .3 Images Le module Images gère le chargement.4 LinAlg Le module LinAlg propose l’algèbre linéaire avec des classes matrice et vecteur.0)= I (1 . png " ) . Imagine++ B. on utilise : im2 = im1 . / / N ’ a f f e c t e p a s im2 Ainsi. défini dans Common. / / Met l e p i x e l en g r i s save ( im .0 f ) . 1 0 ) Une erreur courante est de chercher à lire ou écrire à des coordonnées au-delà des bornes du tableau. Image<byte > im . Ce qui fait que la modification de l’une affecte l’autre : Image<Color > im1 ( 1 0 0 . Images A noter srcPath. sans réelle copie. } d i s p l a y ( im ) . " f i c h i e r _ i m a g e 2 . Matrix < f l o a t > I ( 2 . } f ( im1 ) . 1 0 ) = CYAN. 1 0 0 ) .B. / / T a i l l e 2 x2 I . tout se passe comme si on avait passé par référence : void f ( Image<Color > im ) { / / P a s s a g e p a r v a l e u r im ( 1 0 . typiquement une erreur dans un indice de boucle. il existe une classe dédiée à cela : B. qui indique de chercher dans le dossier contenant les fichiers source. / / S a u v e g a r d e l ’ i m a g e d a n s un f i c h i e r Attention : la recopie et l’affectation (opérateur =) sont des opérations peu coûteuses qui en fait ne font qu’un lien entre les images. puisque c’est le constructeur par copie qui est appelé. 2 ) . png " ) ) ) { c e r r << " I m p o s s i b l e d ’ o u v r i r l e f i c h i e r " << s r c P a t h ( f i c h i e r _ i m a g e . f i l l (0. si on passe une image comme paramètre d’une fonction. 1 0 ) = CYAN. / / Matrice i d e n t i t é 243 . B. pour éviter de gèrer soi-même la mémoire des images. Image<Color > im2 = im1 . / / C o n s t r u c t e u r p a r r e c o p i e im1 ( 1 0 . En fait. 1 0 ) == CYAN) . 1 0 ) = CYAN. c l o n e ( ) . s r c P a t h ( " f i c h i e r _ i m a g e . / / Image en n i v e a u x d e g r i s i f ( ! load ( im . 0 ) = 1 2 8 . la manipulation et la sauvegarde des images. a s s e r t ( im2 ( 1 0 .

244 .4. vecteur-vecteur) et multiplication (matrice*matrice. attention de ne pas sortir des bornes en accédant aux éléments des matrices et vecteurs ! Une fonction très utile est linSolve pour résoudre un système linéaire. / / D é t e r m i n a n t Les opérateurs d’addition (matrice+matrice. Imagine++ cout << " det ( I )= " << det ( I ) << endl . matrice*vecteur) sont bien sûr définis.B. Comme pour les images. soustraction (matricematrice. LinAlg B. vecteur+vecteur).

C. Fiche de référence finale .

2f. else j=2.. } — mx=(x>y)?x:y.o=n.. default: .. — Affectation : i=2. .. — Initialisation : int n=5... void f() { n=10.j. .) { .. interdit! int j=2.3. // i=j. j=i. k=l=3. i=i+1.l. case 2: case 3: . } — for (int i=. — for (int i=. — Constantes : const int s=12.. if (t[i]==s){ // quitte boucle break.. unsigned char d=25. first=false. int k. void avance(Dir d). } . — switch (i) { case 1: . double x=12..) for (int j=...j=10. — if (i==0) { j=1. // OK! } //i=k. . — int i=1.3).. — Conversion : int i=int(x). Boucles — Variables statiques : int f() { — do { static bool first=true. while(i<=100) { } ... — if (i==0) j=1. // OK int i=m. signed char d=-128. sud.C...ouest}.. if (first) { } while(!ok). break. float x=float(i)/j. — Portée : int i. break. k=2. float y=1. // OK .. } — bool t=(i==0).... i=j.m. — Variables globales : int n... } ..i<=10.. bool t=true. interdit! — Types : int i=3..) { //saute cas i==j if (i==j) continue. 246 ...j>i.i++) — Comparaison : == != < > <= >= — Négation : ! — Combinaisons : && || — if (i==0) j=1.. unsigned int j=4.est. Fiche de référence finale Annexe C Fiche de référence finale Fiche de référence (1/5) Variables — Définition : int i. — for(int i=1......j=j-3) . . i=i+2. complex<double> z(2. .. } } Tests — for(int i=1. j=k. — Pile/Tas — Type énuméré : enum Dir{nord... // OK! if (j>1) { int k=3. const int m=12. string s="hop". if (t) j=1. char c=’A’.

3... int hasard(int n). — En paramètre (fin) : .t[3]. t[i]=.. — struct Point { — Surcharge : int i[n]..i<4..2).i<3. — Référence en retour : — Appel : A[i+2*j]=. int plus(int a. — double x[5]...6}}.int n){ if (x<0) f(12).{4.n.2). — En paramètre (suite) : int signe(double x) { void g() { — void f(int* t. double y=sqr(z-3). a.. return. void affiche(int a) { } } cout << a << endl.i<5. vect operator+( for (int i=0.C.y)... — En paramètre : — Une structure est un objet en} .y[5]. t=new int[n]. .. } t[i]=.int b). f(10. vect A. // Var. .// f(10.2.. // i=3! void swap(int& a. — 2D : int y) { — Inline (appel rapide) : int A[2][3]. vect C=A+B. int t[4]={1. — Références constantes (pour int c=a+b.int b) { return 0. int n) { int& b){ . }. f()=3.y. a.. } return. // OK — Définition : a=b. Point b={1. DrawPoint(x. .i++) void f(const obj& x){ } t[i]=0.5... double x.y. — 2D dans 1D : } int A[2*3]. delete[] t. // ..x=2. .i<n.2..4. string s[2]={"ab". . .. Fiche de référence finale Fiche de référence (2/5) Fonctions — Pile des appels t[i]=0.. } — Définition : — Itératif/Récursif int plus(int a. — Valeurs par défaut : — Retour : void f(int a. } return i.. double hasard().vect B) { t[i]=s[i]. Structures swap(x. int hasard(int a.j=g(). Tableaux int tmp=a. — Déclaration : } . — void alloue(int*& t){ void f(int a.y=3. — Affectation : Point a. if (x>=w || y>=h) {{1. — void init(int t[4]) { for(int i=0.4}..i++) a. int *t.. — Opérateurs : int s[3]={1. s+=t[i]..i++) .5. } if (x>0) } return 1.. int n) { un passage rapide) : return c.c=Red.3}.... void f(int A[2][2]). int b). void g(const obj& x){ — Taille variable : } f(x). int A[2][3]= return x*x.0). — Initialisation : Color c. } int i=f(2). int x=3.. // f(12... } } } void afficher(int x. — Références : void f(const int* t..3. } y[i]=2*x[i].i++) 247 tièrement public (→ cf objets !) ...*s. globale int f(int a) { . // NON! for(int i=0."c"}..b=tmp. return -1.int b=0). — const int n=5. } int& f() { — Taille variable (suite) : int g() { . // OK int* t=new int[n]... for(int i=0.RED).2..y=2.2.int b) { — void init(int t[].j[2*n]. int i.3}..Blue}.. inline double sqr( if (x<0 || y<0) double x) { A[i][j]=.

.end(). ..cpp Constructeur vide : — Fonctions : déclarations dans obj::obj() { le . (int i... public: int z..x.h obj a. //OK A.find(3)!=l.f(). // OK — } .end()) // mieux que obj b..B. p. Fiche de référence finale Fiche de référence (3/5) Objets — struct obj { int x.. x. — }.max. .. int obj::f() { int i=g(3). .valeur de retour it. void a_moi().. A.. a..3). } ...obj b(a). // OK a_moi().first=2.y. . définitions dans le ... Constructeur de copie : — #include<list> obj::obj(const obj& o){ using namespace std.. Utilisé par : l. // C=A. — if (l.... //OK A.second="hop".h".. // OK — } void une_autre(obj A) { x=A..pour_tous(). — complex<double> z. return point(x+b.. . assert(i>=0 .end(). Accesseurs : map. vector.a_moi(). . class mat { 248 .begin(). — Ne déclarer dans le . //NON A.. // OK .int j){ Constructeur : assert(i>=0 .. — Types : définitions dans le ... .. } public: double operator() point(int X. — } void g(const obj& x){ double *x.obj b=a. // OK } void obj::pour_tous() { x=. heap. int i=a. void pour_tous().h que les Objets temporaires : fonctions utiles. void obj::a_moi() { x=. — #include "vect. obj& obj::operator=( it!=l. — class obj { — int x.). int main() { obj a..x=.a_moi(). // mon g int j=x+i. int main() { obj A..cpp } . set. +f(point(2.2) — min.B.C.. it!=l. . } p. // OK public: } double& operator() (int i.it++) Objets avec allocation dynaif ( *it==2) mique automatique : cf secit=4. class point { return x[i+M*j]. Destructeur : obj::~obj() { — pair<int.=y..int j)const{ }.. y point a(2. — list<int>::iterator it } for (it=l.string> p. .x=3. void un_autre(obj A). compris dans vect... } . // mon x return j. — }. .y. y=Y.paramètres des fonctions — list<int>::const_iterator .x. point::point(int X. // OK A.begin(). * tion 10. return x[i+M*j]. }..une_autre(B)..C. //NON A..)..z=.. // OK z=. } Compilation séparée . int main() { obj A. } list<int> l. STL c=point(1.y). //OK — class obj { obj operator+(obj B).11 — stack.h.operator+(B) — — Méthodes constantes : void obj::f() const{ . int Y){ } x=X.int Y).. — Ne pas trop découper.. C=A+B..b=a. ..3)).. queue.. // méthode — int g(int y).. return *this.. int x..push_front(1)... // champ int f()..f().it++) const obj&o){ s+= *it.. . y+b. point point::operator+( point b) { — #pragma once au début du fichier. Affectation : for (it=l.

public: — Ne pas oublier delete. }. Entiers : template <int N> class hop { ... g.write((const char*)&y.. using namespace std. class hop { — Indent : Ctrl+K.C. stringstream f. //double — Faire des structures. if (!g. tâches : Ctrl+Maj+Ech .is_open()) { // ou dans un . f << i. .//string — Faire des fichiers séparés....2). g.2). — Objets : teur (qui ne doit pas regarder ofstream f("hop. — do { — Tableaux : pas pour transcrire // tout seul! .2. — Ne pas toujours faire des ob— string s. — #include <cassert> g.. f. Template f << 1 << ’ ’ << 2. template <typename T. hop<3> A. f. template <typename T> } — Indenter. } g >> i >> x.. ofstream f("hop.read((const char*)&y.cpp) ios::binary). class paire { f. assert(x!=0).bin". — Fonctions : f.. T x[2]. — Ne pas abuser du récursif. — Utiliser le debuggeur.eof()). — ofstream f..bin". paire(T a. maxi("a".read((char*)x. — Build : F7 . paire() {} — Compiler régulièrement. Fiche de référence finale Fiche de référence (4/5) Entrées/Sorties — #include <iostream> using namespace std...txt"). — Ne pas laisser de warning. } jets ! ifstream f(s. }..T b) { f.open("hop. // Le type doit être — Penser interface / implémen— #include <sstream> // précisé! tation / utilisation.c_str()). maxi(. . . ostream& f.. typename S> — Step inside : F11 f >> s. cout <<"I="<<i<<endl. ifstream g("hop.y.. cin >> i >> j... .x[1]=b. — Debug/Release : nettoyer les x[0]=a.. // fichier qui l’utilise — Nettoyer en quittant. //int } while (!(g.somme().y. — Debug : F5 // Entier vers chaîne — Multiples : — Step over : F10 f."c").h return 1. T add()const.Ctrl+F . Clavier int s=a. . const point&p) { 249 ... f >> i. return f. . . — ostream& operator<<( — Step out : Maj+F11 }. sizeof(double)). Conseils // A mettre dans LE ifstream g("hop. // Le type est trouvé — Faire des fonctions. — #include <fstream> using namespace std.close().string> A. 10*sizeof(double)). template <typename T> y=1/x. f << s. 10*sizeof(double)).txt"). } ios::binary).3. deux..... g.txt"). return x[0]+x[1].point& p){ f>>p. T maxi(T a. f<<p. ..close().y..x>>p. — Le . } hop<int.. ..clear().x<<’ ’<< p. // Chaîne vers entier paire<double> b.write((const char*)x. paire<int> a(1.close().close().h doit suffire à l’utilisa— double x[10]. template <typename T> le . T paire<T>::add()const{ — Faire des objets. — Erreurs et warnings : cliquer. } — istream& operator>>( istream& f. une formule mathématique ! maxi(1. double x. — Gest. return f. sizeof(double)).T b) { int i.3).

i++) — obj* t=new obj[n].2}. — for (i . #include <ctime> . — int q=r=4. — int f()[4] { // NON! if (s1!=s2) . if (i<0 | i>n) // NON int t[4].2). // Non.) const char *t=s. break. srand((unsigned int) // NON! delete t. — int* t....3]..i<100. — for (int i=0.cpp"//NON /CLOCKS_PER_SEC. // NON Erreurs fréquentes #include <cstdlib> — Pas de définition de fonction ...0 et non 0: max — int* t=new int[2]. point a={2. } y[i]=2*x[i].int b). l=s.. .y[10]. int i.//NON #include <ctime> if (i==2) then // NON! point p(1..// s est int // et non int* t=new int[n].2}. t={1.1) : reset(i. // NON! txt=a+" "+b.s. j+=3. j=max(i.. size_t i=s.’:’). if (.1) : — i|=(1<<n) i&=~(1<<n) if (i&(1<<n)) i^=(1<<n) — — j=i%n.size().. //Déja fait — Voir documentation. // On perd s! right shift : a>>n Imagine++ delete[] t. return i. s1="un deux trois". i-=2. — if (i>0 & i<n) // NON } k=s.) — int t[2]. int l=s. ..) { — Point a. s=new int[n]. — double x=1/3. // NON! — void f(int a.. double sqrt(double x).. // NON! j=s. ..//NON.int b=0)...i++) string s="hop"... exemples : set(i. // NON if (s1==s1) .) — #include "vec. Fiche de référence finale Fiche de référence (5/5) Divers — i++. // OUI ... if i==2 // NON! — point p=point(1. f()=3.2). double cos(double x). // boucles seulement — struct Point { getline(cin.. dans une fonction ! i=rand()%n..s. // NON! y=max(x.//NON // juste la boucle j #define _USE_MATH_DEFINES int t[2. // NON! double pi=M_PI.j]=.i<=10..1) : test(i. t=f().y.3}.find(’h’)..3}. — int s[3]={1.//NON! and : a&b // 0. i--. //NON! — Ne pas tout mettre inline ! #include <string> — int f() { — double x[10].. a="comment"...//OK Opérateurs binaires t[1]=. x=i/j. public: .0).} — //NON! #include <cmath> int i=f. // NON! s2=string(s1.j. double x.3).0).C. }. t[i.. // NON! x=rand()/ — if (i=2) // NON! double(RAND_MAX).. break. f()=3. // NON! s=double(clock()) if (...4). // NON! void f(int a=2..// NON! class point { int x. // manque [] time(0)).find(’h’. void f(int a). using namespace std.find("hop".. // HORREUR! — int n=5..t[3].// NON! double sin(double x). int t[4].. — int& f() { int t[n]. a={1.. complement : ~a 250 . } double x. xor : a^b s=t.. //NON char c=s[0]. if (s1<s2) .find("hop"). getline(cin. t=s.3. quitte — void f(int t[][])..2.. } .. for (int i=1. // NON! #include <cmath> — int i. — if (. int i. // Modulo — — — — — — — int *t.. x=double(i/j).1) : flip(i.s). // NON! return t. — int f() {.. or : a|b // est un template STL int* s=new int[2]. // NON! double acos(double x). left shift : a<<n delete[] s. } // NON! for (j .3).y.c_str().) { b="ça va?".