Vous êtes sur la page 1sur 182

Cours de Langage C

Eric Berthomier
Daniel Schang
2
Table des matières

1 Avant de commencer... 1

2 Premiers pas 3

2.1 Prologue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

2.2 Utiliser un éditeur sous Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

2.3 Exemple de programme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4

2.4 Normalisation du programme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5

2.5 Petit mot sur ce qu’est une bibliothèque . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

2.6 Un exemple de fichier bibliothèque . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

2.7 Compléments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

2.8 Squelette de programme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

2.9 Les blocs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

2.10 Les commentaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

2.11 Exercice d’application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

2.12 Astuce . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

2.13 Corrigé de l’exercice du chapitre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

2.14 A retenir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

3 Les variables (1ere partie) 11

3.1 La fonction printf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

3.2 Variable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

i
ii TABLE DES MATIÈRES

3.3 Déclaration d’une variable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

3.4 Application : exemples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

3.5 Utilisation multiple du % . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

3.6 Exercices d’application directe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

3.7 Réutilisation d’une variable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

3.8 Caractères spéciaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

3.9 Exercice à réaliser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

3.10 Corrigés des exercices du chapitre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

3.11 Un exercice pour finir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

3.12 A retenir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

4 Les variables (2eme partie) 19

4.1 Exercice de mise en bouche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

4.2 Déclaration des variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

4.3 Saisie des variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

4.4 Les types flottants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

4.5 D’autres fonctions utiles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

4.6 Corrigés des exercices du chapitre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

4.6.1 Exercice de mise en bouche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

4.6.2 Addition de 2 nombres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

4.6.3 Soustraction de 2 nombres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

4.6.4 Multiplication de 2 nombres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24

4.6.5 Transformez les 3 précédents programmes . . . . . . . . . . . . . . . . . . . . . . 25

4.6.6 Solutions de "exercices à réaliser" . . . . . . . . . . . . . . . . . . . . . . . . . . 26

4.6.7 Solutions de "autres fonctions utiles" . . . . . . . . . . . . . . . . . . . . . . . . 27

4.7 Un exercice pour finir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

4.8 A retenir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
TABLE DES MATIÈRES iii

5 Les conditions 29

5.1 Exercice de mise en bouche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

5.2 Les conditions : Si Alors Sinon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

5.3 Opérateurs logiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

5.4 Opérateurs logiques purs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31

5.5 Vrai ou faux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31

5.6 Combinaison . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

5.7 Astuce . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

5.8 Les accolades . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

5.9 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34

5.10 Corrections des exercices du chapitre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35

5.10.1 Écrire un programme qui met en application le théorème de Pythagore . . . . . . . 35

5.10.2 Tester le signe d’une valeur saisie au clavier . . . . . . . . . . . . . . . . . . . . . 35

5.10.3 Tester si un caractère saisi au clavier est une consonne ou une voyelle . . . . . . . 36

5.11 Un exercice pour finir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37

5.12 A retenir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37

6 Mise au point 39

6.1 Prologue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

6.2 Exercice 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

6.3 Retour sur getchar() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

6.4 Boucle Faire ... Tant que (vrai) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

6.5 Exercice 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

6.6 Exercice 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

6.7 Corrigés des exercices du chapitre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44

6.8 Un exercice pour finir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45


iv TABLE DES MATIÈRES

7 Et les shadocks pompaient 47

7.1 While . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47

7.2 Et les Shadoks apprenaient que reprendre équivaut à apprendre . . . . . . . . . . . . . . . 47

7.3 Fonction toupper () . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48

7.4 O tant que en emporte le Shadok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48

7.5 Et les Shadoks continuaient à pomper pour obtenir le résultat . . . . . . . . . . . . . . . . 49

7.6 Au clan des Shadoks, on trie, voyelles, chiffres premiers . . . . . . . . . . . . . . . . . . 49

7.7 Incrémentations, pré-incrémentations... . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

7.8 Corrigés des exercices du chapitre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51

7.9 Un exercice pour finir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55

7.10 A retenir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55

8 Les boucles 57

8.1 Et les shadoks pédalèrent pendant 15 tours . . . . . . . . . . . . . . . . . . . . . . . . . . 57

8.2 Syntaxe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58

8.3 Notion de double boucle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58

8.4 Exercice 2 : Et les Shadoks fêtèrent Noël... . . . . . . . . . . . . . . . . . . . . . . . . . . 59

8.4.1 « Cône » du sapin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59

8.4.2 Affichage du tronc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60

8.5 Exercice 3 : Table Ascii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60

8.6 Corrigés des exercices du chapitre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62

8.7 Un exercice pour finir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64

8.8 A retenir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64

9 Pointeurs et Fonctions 65

9.1 Binaire, octets... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65

9.1.1 Binaire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65

9.1.2 Octet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
TABLE DES MATIÈRES v

9.1.3 Compter en base 10 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66

9.1.4 Compter en base 16 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66

9.2 Variables : pointeurs et valeurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67

9.2.1 Les variables et la mémoire. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67

9.2.2 Pointeurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68

9.2.3 Exercices d’application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70

9.3 Les fonctions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72

9.3.1 Définition générale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72

9.3.2 void . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74

9.3.3 Variables globales et locales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74

9.3.4 Utilisation et modification de données dans les fonctions . . . . . . . . . . . . . . 75

9.3.5 Exercice 3 : . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77

9.3.6 Piège ! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78

9.3.7 Exercice 4 : . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78

9.3.8 Les prototypes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78

9.3.9 Un rappel avant le dernier exercice... . . . . . . . . . . . . . . . . . . . . . . . . . 80

9.3.10 Exercice 5 : . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80

9.4 Corrigés des exercices du chapitre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81

9.5 Un exercice pour finir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84

9.6 A retenir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84

10 Tableaux & Chaînes de caractères 87

10.1 Tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87

10.1.1 Définition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87

10.1.2 Déclaration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87

10.1.3 Utilisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88

10.2 Chaînes de caractères . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88


vi TABLE DES MATIÈRES

10.2.1 Déclaration d’une chaîne de caractères . . . . . . . . . . . . . . . . . . . . . . . . 88

10.2.2 Affichage d’une chaîne de caractères . . . . . . . . . . . . . . . . . . . . . . . . . 89

10.2.3 Longueur d’une chaîne . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89

10.2.4 Initialisation d’une chaîne de caractères . . . . . . . . . . . . . . . . . . . . . . . 89

10.2.5 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90

10.2.6 gets : saisie d’une chaîne de caractères . . . . . . . . . . . . . . . . . . . . . . . . 91

10.2.7 Passage d’une chaîne de caractères en paramètres . . . . . . . . . . . . . . . . . . 92

10.3 Quelques fonctions utiles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93

10.3.1 La fonction strcat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93

10.3.2 La fonction strncpy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93

10.3.3 La fonction strncat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93

10.3.4 La fonction strcmp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93

10.3.5 Les fonctions sprintf et sscanf . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94

10.4 Tableaux à 2 dimensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94

10.5 Correction des exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97

10.5.1 Boucle for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97

10.6 Un exercice pour finir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97

10.7 A retenir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97

11 Structures et Fichiers 99

11.1 Les types synonymes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99

11.2 Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99

11.2.1 Déclaration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99

11.3 Bases sur les fichiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101

11.3.1 Fichiers et structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104

11.4 Correction de l’exercice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107

11.5 Un exercice pour finir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108


TABLE DES MATIÈRES vii

12 Debugger un programme 109

12.1 La chasse aux bugs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109

12.1.1 Deux types d’erreurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109

12.1.2 Un phénomène surprenant... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110

12.1.3 La chasse aux bugs... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111

12.1.4 Bonne chasse... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112

12.1.5 Les erreurs de segmentation... . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113

12.1.6 Le debugger ddd . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114

12.1.7 Une dernière chasse... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115

12.1.8 Une dernière sournoiserie... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115

12.1.9 Solutions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117

12.1.10 A retenir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118

13 Complements 119

13.1 Conversions de type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119

13.2 Un usage très utile des conversions de type . . . . . . . . . . . . . . . . . . . . . . . . . . 119

13.3 La fonction putchar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121

13.4 Allocation dynamique de mémoire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121

13.4.1 Les fonctions malloc et sizeof . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121

13.4.2 La fonction free . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123

13.5 Avez-vous bien compris ceci ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124

13.6 Un exemple pour se rafraîchir la mémoire sur l’utilité des pointeurs . . . . . . . . . . . . 125

13.7 Un second exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126

13.8 Un mot sur les warnings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127

14 Code Ascii 129


viii TABLE DES MATIÈRES

15 Annexes utiles 131

15.1 Les types numériques les plus utiles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131

15.2 Les formats les plus utiles pour printf . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131

15.3 Les formats les plus utiles pour scanf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131

15.4 Les 12 erreurs les plus classiques en Langage C . . . . . . . . . . . . . . . . . . . . . . . 132

16 Quelques exemples de programmes 133

16.1 Convertisseur francs/euros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133

16.2 Proportion de nombres pairs et impairs . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134

16.3 Inverser les éléments d’un tableau . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135

16.4 Affichage d’une table de multiplication . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137

16.5 Quelques manipulations sur les tableaux à l’aide de fonctions . . . . . . . . . . . . . . . . 138

16.6 Effectuer un tri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139

16.7 Le jeu de la vie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142

16.7.1 Historique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142

16.7.2 Règles du jeu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142

16.7.3 Exemples obtenus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142

16.7.4 Exemple de corrigé . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143

17 En 2ème lecture... 149

17.1 Quelques fonctions mathématiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149

17.2 Pointeurs et structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149

17.3 En finir avec les warnings du gets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150

17.4 Stdout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151

17.5 Utilité des prototypes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151

17.6 Opérateur ternaire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152

17.7 Choix multiple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153

17.8 Tableaux de chaînes de caractères . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153


TABLE DES MATIÈRES ix

17.9 Pointeurs et tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156

17.10Tableaux de pointeurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159

17.11switch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161

17.12Edition de liens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162

17.13La compilation conditionnelle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163

17.14La compilation séparée . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163

18 Partie optionnelle : exercices supplémentaires... 165

18.1 Balle rebondissante . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165

18.2 Le morpion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168

18.2.1 Le morpion de base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168

18.2.2 Améliorations possibles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169

18.3 Le pendu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170

18.3.1 Le pendu de base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170

18.3.2 Améliorations possibles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170


x TABLE DES MATIÈRES
Chapitre 1

Avant de commencer...

Ce document va vous permettre de découvrir le Langage C. Allez à votre rythme, le but n’est pas de tout
faire le plus vite possible mais au contraire de bien assimiler les choses.

Pour ce faire, vous aurez à votre disposition environ 20 heures. En sachant que ce polycopié fait aux
alentours de 180 pages, cela fait environ 9-10 pages par heure (en rythme de croisière...). Dans la pratique,
vous irez sans doute plus vite au départ... Par endroits, on vous demandera de retaper des petits bouts de
programme, ainsi plutôt que de vous les donner ou faire du copier-coller, cela vous permettra de ne pas
aller trop vite et de mieux assimiler ces programmes lorsque vous les saisirez.

Pour mémoire, quand on parlera de la touche entrée, c’est la même chose que la touche return (voir
figure).

Touche entrée/return :

entrée

barre espace

1
2 CHAPITRE 1. AVANT DE COMMENCER...
Chapitre 2

Premiers pas

2.1 Prologue

Ce cours est destiné à vous faire connaître le C sous Linux. Linux, ou GNU/Linux, est un système d’ex-
ploitation (façon Windows). Linux est un logiciel libre créé en 1991 par Linus Torvalds sur un ordinateur
compatible PC.

2.2 Utiliser un éditeur sous Linux

Voici une manipulation pour lancer un éditeur : un éditeur est un programme (façon Word) qui nous servira
à taper nos programmes :

- sous Ubuntu, vous faites Applications −→ Accessoires −→ Terminal. Dans un autre environnement
recherchez, dans les menus, quelque chose qui pourrait s’appeler terminal ou console.

Dans cette nouvelle fenêtre qui ressemble à la figure ci-dessous tapez gedit. Vous verrez par la suite que
taper gedit & serait encore mieux, car cela vous permettra de gagner du temps en évitant d’ouvrir une
seconde fenêtre du type "Terminal".

3
4 CHAPITRE 2. PREMIERS PAS

2.3 Exemple de programme

Voici pour exemple un premier programme qui fonctionne malgré le fait qu’il ne soit pas normalisé 1 .
Celui-ci affiche le mot Bonjour à l’écran. À l’aide de votre éditeur de texte (dans la fenêtre gedit donc),
tapez le texte qui se trouve à l’intérieur du rectangle suivant :

main () {
puts ("Bonjour");
getchar ();
}

puis sauvegardez ce fichier avec le nom suivant : programme1.c

Une fois le texte du programme tapé, il faut le compiler, c’est à dire en analyser la syntaxe (on verra que la
compilation fait d’autres choses aussi...). Nous allons donc ouvrir une seconde fenêtre dans laquelle nous
allons compiler notre programme : comme tout à l’heure lancez une fenêtre du type terminal.

La compilation se fait avec le compilateur gcc :

tapez dans cette nouvelle fenêtre : gcc −o programme1 programme1.c

Si vous n’avez pas fait d’erreur, rien ne va s’afficher (pas de nouvelles bonnes nouvelles...) En fait, cette
commande vient de créer un fichier nommé programme1. Vous pouvez le vérifier en tapant ls -l
(attention, tapez bien ℓs − ℓ) qui devrait vous renvoyer quelque chose du type :

-rw-r--r-- 1 dschang dschang 44 2008-10-14 11:10 programme1.c


-rwxr-xr-x 1 dschang dschang 6525 2008-10-14 11:11 programme1

Si on y regarde de plus près on peut voir le fichier intitulé programme1.c qui a été sauvegardé à 11h10
1. On verra par la suite qu’un programme écrit en Langage C doit respecter certaines normes...
2.4. NORMALISATION DU PROGRAMME 5

et qui contient 44 octets. En dessous se trouve le fichier programme1 qui vient d’être créé et qui contient
6525 octets. Ce dernier fichier contient le code machine (code qui est compréhensible par la machine).

Le fichier programme1 est nommé le fichier exécutable. Le fichier programme1.c se nomme le fichier
source (source de tous les bonheurs de Linux ...). En fait, un fichier source désigne un fichier qu’un être
humain peut comprendre par opposition à un exécutable que seule la machine arrive à comprendre. Il ne
reste plus qu’à exécuter le programme :
./programme1 .

Et comme par magie, s’affichera alors Bonjour et attendra que vous appuyez sur la touche Entrée.

Remarque : nous reviendrons par la suite sur le fait qu’il faille taper ./programme1 et pas programme1...

Remarque : vous avez pu constater que la fin des lignes se terminait par un ; sauf pour la ligne du main...
nous reviendrons là dessus... disons pour l’instant que c’est un peu comme en français, chaque ligne se
termine par un "." alors qu’en Langage C, chaque ligne se termine généralement par un ;

2.4 Normalisation du programme

Bon, jusqu’à présent, nous avons fait un peu "dans le sale". Pourquoi me direz–vous ? Eh bien rendons
notre compilateur bavard en lançant la commande gcc −o programme1 programme1.c −Wall
Observez les insultes.

programme1.c:5: warning: return-type defaults to ‘int’


programme1.c: In function ‘main’:
programme1.c:6: warning: implicit declaration of function ‘puts’
programme1.c:9: warning: control reaches end of non-void function

Peu compréhensible et c’est normal.

En fait, l’option de compilation −Wall permet de « déclencher la production de messages soulignant toute
technique autorisée mais discutable », en deux mots à éviter.

Nous allons donc normaliser ce programme.

À sa base, le langage C n’est qu’un ensemble de bibliothèques à partir desquelles le compilateur trouve les
fonctions et les applications qui lui permettent de créer un programme exécutable. Exactement ce que vous
faîtes si vous recherchez dans une encyclopédie pour réaliser un exposé.

Certaines bibliothèques (les plus courantes) sont incluses dans des compilateurs ce qui permet à notre
programme de se compiler. Normalement puts a besoin de la bibliothèque stdio.h. Pour ajouter une
bibliothèque, il suffit d’ajouter #include <nom de la bibliothèque> en début de programme.

Un autre point à corriger est l’ajout de la ligne return 0. En fait, le programme renvoie une valeur de retour
(0), tout à la fin. Cette valeur de retour permet à un programme ou à l’utilisateur de savoir si le programme
que l’on exécute s’est correctement terminé. En général 0 signifie une terminaison sans erreur. Enfin, il
faut transformer la ligne main () en int main(). Ce point sera détaillé par la suite lorsque nous parlerons de
fonctions...
6 CHAPITRE 2. PREMIERS PAS

En rajoutant ces quelques correctifs on obtient donc :

#include <stdio.h>

int main () {
puts ("Bonjour");
getchar (); /* Permet d’attendre la frappe d’une touche */
return 0;
}

2.5 Petit mot sur ce qu’est une bibliothèque

À l’instar de l’étudiant qui recherche dans des livres, on peut dire que le « .h » représente l’index du livre
et le « .c » le contenu du chapitre concerné.

Exemple : Lorsque le compilateur C rencontre le mot puts, il regarde dans chacun des « .h » déclaré
par l’instruction #include si ce mot y est défini. Il trouve celui-ci dans la bibliothèque stdio.h. À
l’inverse, s’il ne le trouve pas, celui-ci émettra une erreur de syntaxe.

2.6 Un exemple de fichier bibliothèque

Vous trouverez ci-dessous, un extrait de la bibliothèque stdio.h. On y retrouve notamment la déclaration


de puts que l’on voit dans ce cours et la déclaration de printf que l’on verra dans le second cours. C’est
assez compliqué... on y jette juste un oeil, pas plus ;-)
2.7. COMPLÉMENTS 7

/* Write formatted output to STREAM. */


extern int fprintf __P ((FILE *__restrict __stream,
__const char *__restrict __format, ...));
/* Write formatted output to stdout. */
extern int printf __P ((__const char *__restrict __format, ...));
/* Write formatted output to S. */
extern int sprintf __P ((char *__restrict __s,
__const char *__restrict __format, ...));

/* Write formatted output to S from argument list ARG. */


extern int vfprintf __P ((FILE *__restrict __s,
__const char *__restrict __format,
_G_va_list __arg));
/* Write formatted output to stdout from argument list ARG. */
extern int vprintf __P ((__const char *__restrict __format,
_G_va_list __arg));
/* Write formatted output to S from argument list ARG. */
extern int vsprintf __P ((char *__restrict __s,
__const char *__restrict __format,
_G_va_list __arg));

/* Write a string, followed by a newline, to stdout. */


extern int puts __P ((__const char *__s));

2.7 Compléments

#include <stdio.h>

int main () {
puts ("Bonjour");
getchar (); /* Permet d’attendre la frappe d’une touche */
return 0;
}

– puts : permet d’afficher du texte suivi d’un retour à la ligne.


– getchar : permet d’attendre la frappe d’une touche suivie d’un retour chariot 2 ou un retour chariot
seul.
– /* Commentaire*/ : permet de mettre un commentaire. On trouvera aussi // qui permet de mettre
le reste de la ligne en commentaire.

Notre programme affiche bonjour et attend que l’on appuie sur la touche entrée ou sur une autre touche
puis la touche entrée.

2. Un retour chariot désigne la même chose que la touche entrée ou return. C’est une touche qui sert en quelque sorte à en-
trer/valider des données.
8 CHAPITRE 2. PREMIERS PAS

2.8 Squelette de programme

On peut définir le squelette d’un programme C de la façon suivante :

/* Déclaration des bibliothèques */

int main () {
/* Déclaration des variables */ // cf. chapitres suivants...

/* Corps du programme */
getchar(); /* Facultatif mais permet d’attendre l’appui d’une touche */
return 0; /* Aucune erreur renvoyée */
}

2.9 Les blocs

La partie de programme située entre deux accolades est appelée un bloc. On conseille de prendre l’habitude
de faire une tabulation 3 après l’accolade. Puis retirer cette tabulation après l’accolade fermante du bloc.
Ainsi, on obtient :

int main () {
Tabulation
Tout le code est frappé à cette hauteur
}

Retrait de la tabulation
Tout le texte est maintenant frappé à cette hauteur.

Cette méthode permet de contrôler la fermeture des accolades et de leurs correspondances.

2.10 Les commentaires

Commenter signifie qu’une personne ne connaissant pas votre programme doit pouvoir lire le programme
et le comprendre. Les commentaires sont indispensables dans tout bon programme. Les commentaires
peuvent être placés à n’importe quel endroit dans le programme. Ils commencent par /* et se terminent
par */.
/* Commentaire */
Vous trouverez aussi :

// Le reste de la ligne est un commentaire


3. La touche du clavier à gauche de la touche "A", cette touche sert à décaler.
2.11. EXERCICE D’APPLICATION 9

2.11 Exercice d’application

Écrire un programme qui :

– Écrit « Salut toi, appuie sur une touche s’il te plaît »


– Attend l’appui d’une touche
– Écrit « Merci d’avoir appuyé sur une touche »

Une fois que vous aurez proposé/tapé/testé votre solution, vous pouvez comparer avec la solution qui
apparaît au paragraphe suivant...

2.12 Astuce

Sous Linux, afin d’éviter de retaper à chaque fois la commande suivante : gcc -o programme programme.c,
ce qui peut être laborieux à force, il suffit d’appuyer une ou deux fois sur la flèche vers le haut, ce qui fera
réapparaître (comme par magie), la commande gcc -o programme programme.c...
10 CHAPITRE 2. PREMIERS PAS

2.13 Corrigé de l’exercice du chapitre

#include <stdio.h>

int main () {
puts ("Salut toi, appuie sur une touche s’il te plaît");
/* Affiche le message Salut toi, ... s’il te plaît */

getchar (); /* Attend la frappe d’une touche */

puts ("Merci d’avoir appuyé sur une touche");


/* Affiche le message Merci d’avoir appuyé sur une touche */

return 0;
}

2.14 A retenir
– l’éditeur que l’on utilise s’appelle gedit
– connaître les fonctions puts et getchar qui apparaissent dans le programme suivant :

#include <stdio.h>

int main () {
puts ("Bonjour");
getchar (); /* Permet d’attendre la frappe d’une touche */
return 0;
}

– pour compiler un programme :


gcc -o programme1 programme1.c
– pour le lancer :
./programme1

Remarque : lorsque vous quitterez votre session (à la fin du TP), il ne faut pas arrêter brutalement la
machine en appuyant sur le bouton on/off, au contraire, il faut chercher dans les différents menus, quelque
chose qui ressemble à : clore la session oubien quitter oubien logout...
Chapitre 3

Les variables (1ere partie)

Rappelez-vous, allez à votre rythme, pas trop vite...

3.1 La fonction printf

#include <stdio.h>
int main () {
printf ("Coucou c’est moi\n");
/* Affiche Coucou c’est moi à l’écran puis saute une ligne */
getchar();
/* Attendre l’appui d’une touche */
return 0;
}

On pourrait dire que la fonction printf est la même que l’instruction puts vue précédemment mais il
n’en est rien... Celle-ci est beaucoup, beaucoup, beaucoup plus puissante.

Syntaxe : La syntaxe de printf est très complexe et pourrait à elle seule faire l’objet d’un cours, nous
en verrons donc des applications au fur et à mesure des besoins.

3.2 Variable

Comme son nom l’indique une variable est quelque chose qui varie. C’est vrai mais ce n’est pas suffisant.
Une variable peut être considérée comme une boîte dans laquelle on met des données que l’on peut lire ou
écrire.

La manière la plus facile de lire le contenu d’une variable est la fonction printf que l’on a aperçue
précédemment. La manière la plus simple de donner une valeur à une variable est l’opérateur mathématique
=.

11
12 CHAPITRE 3. LES VARIABLES (1ERE PARTIE)

Écrire dans une variable ayant déjà une valeur revient à la modifier.

Une variable ne peut contenir qu’une seule chose à la fois. Si vous mettez une seconde donnée dans la
variable, la précédente est effacée.

3.3 Déclaration d’une variable

La déclaration d’une variable se fait simplement en écrivant :


<son type> <son nom>;

Remarque importante : comme le montre le bout de programme suivant qui déclare deux variables :

#include <stdio.h>
int main () {
int i; /* déclare un entier de nom i */
char c; /* déclare un caractère de nom c */
}

Il ne faut pas mettre les < et > comme cela apparaissait dans
<son type> <son nom>; !!!
on met ceci pour rendre les programmes plus lisibles... Vous ne mettrez jamais ces < et > exceptés
pour des cas du type #include <stdio.h>.

3.4 Application : exemples

Inutile de retaper cet exemple, lisez le programme puis les explications ci-dessous :

#include <stdio.h> int main () {


int i; /* i : variable de type entier */
int j; /* j : variable de type entier */

i=22; /* i vaut 22 */
j=i; /* on recopie la valeur de i dans j */
/* donc j vaut aussi 22 à présent */

printf ("i vaut %d\n", i); /* Affiche la valeur de i */


printf ("i+j vaut %d\n", i+j); /* Affiche la valeur de i+j */
getchar();
return 0;
}

Explications :
3.5. UTILISATION MULTIPLE DU % 13

1. printf ("i vaut %d\n", i); : %d signifie que l’on attend une valeur entière, le programme
va donc remplacer le %d par la valeur de i. Ce début de programme provoquera donc l’affichage
suivant : i vaut 22
2. printf ("i+j vaut %d\n", i+j); : ici, on voit que des calculs sont possibles. On aura
donc l’affichage suivant : i+j vaut 44

L’exécution complète de ce programme donnerait donc :


i vaut 22
i+j vaut 44

Voici un second exemple avec des variables du type caractère (lisez le programme puis les explications
ci-dessous) :

#include <stdio.h>
int main () {
char car; /* car: variable de type caractère */
car = ’E’;

char e;
e=’e’;
printf("car=%c e=%c",car,e);
getchar();
return 0;
}

Explications :
1. car = ’E’ : on met dans la variable car la valeur du caractère E.
Note : En informatique, tout n’est que nombre, je dis donc la valeur de E et non E car c’est le code
ASCII 1 de E qui est sauvegardé dans cette variable. Nous reviendrons là dessus un peu plus tard.
2. e=’e’ : on met dans la variable e la valeur du caractère e. On voit bien ici que si l’on avait écrit
e=e, cela n’aurait pas fait grand chose ! On aurait juste recopié la valeur de la variable e dans la
variable e. Cela aurait juste consommé un peu de temps...

L’exécution complète de ce programme donnerait donc :

car=E e=e

3.5 Utilisation multiple du %

Le code « %x » signifie que le compilateur C doit remplacer ce code par la valeur correspondante (qui lui
est fournie dans la suite de l’instruction) en la transformant dans le type x. Cette transformation est appelée
un cast.

Exemple :

1. ASCII : sera expliqué juste après...


14 CHAPITRE 3. LES VARIABLES (1ERE PARTIE)

int i;
i =65;
printf ("Le caractère %d est %c",i,i);

nous donnera l’affichage suivant :


Le caractère 65 est A

– En effet, le %d est remplacé par la valeur numérique de i c’est à dire 65.


– Le %c est remplacé par la valeur alphanumérique (ASCII) de i c’est à dire A (cf. Table ASCII en Annexe
page 129). Cette table est très utile car l’ordinateur ne "comprend" que des nombres. Cette table assure
donc une bijection entre les lettres que nous autres êtres humains comprenons et leur codage par la
machine. En résumé, chaque fois que vous manipulez la lettre A, pour l’ordinateur, il s’agira de la valeur
numérique 65...

3.6 Exercices d’application directe


1. En utilisant ce qui a été fait précédemment, faîtes afficher les valeurs 70, 82, 185 et 30.
2. En utilisant ce qui a été fait précédemment, faîtes afficher, les caractères c, o, u, C, O, U.

Une fois que vous avez votre propre solution, vous pouvez comparer avec celle qui se trouve à la fin du
chapitre... Eventuellement, vous pouvez même taper le premier exercice, le compiler puis le tester...

3.7 Réutilisation d’une variable

On peut réutiliser une variable autant de fois que l’on veut, la précédente valeur étant effacée :
i = 3;
i = 5;
i = 7; donnera au final pour valeur de i la valeur 7.
car = ’E’;
car = ’G’;
car = ’h’; donnera au final pour valeur de car la valeur de h.

3.8 Caractères spéciaux

Certains caractères utilisés par la fonction printf (« % » par exemple) ou même tout simplement pour
déclarer une variable (’ pour les caractères par exemple) obligent à utiliser le caractère de suffixe \ pour
pouvoir être affichés.

Exemple : Pour déclarer un caractère avec la valeur ’ (prononcée quote en informatique et non pas
apostrophe (français)), on écrira :
3.9. EXERCICE À RÉALISER 15

char car;
car = ’\’’;

En effet, le compilateur serait perdu avec une expression du type :

char car;
car = ’’’;

Pour afficher un % avec printf j’écrirai :


printf("La réduction était de 20 %%");

3.9 Exercice à réaliser

Réaliser un programme qui réalise l’affichage suivant :


C

e
s
t
Ok i vaut: 1

Rappel : Pour mettre une variable c égale à ’ on écrit c=’\”

Rappel : Pour pouvoir afficher un caractère réservé à la syntaxe du C comme par exemple ", on utilise
le caractère \ comme préfixe à ce caractère. Pour obtenir un ", on utilise donc \".
16 CHAPITRE 3. LES VARIABLES (1ERE PARTIE)

3.10 Corrigés des exercices du chapitre

Faites afficher, en utilisant ce qui a été fait précédemment, les valeurs 70, 82, 185 et 30.

#include <stdio.h>

int main () {
int i,a,b,c;
i=70;
a=82;
b=185;
c=30;

printf ("i vaut %d.\n",i);


printf ("a vaut %d.\n",a);
printf ("b vaut %d.\n",b);
printf ("c vaut %d.\n",c);
getchar();

return 0;
}

Faites afficher, en utilisant ce qui a été fait précédemment, les caractères c, o, u, C, O, U.

#include <stdio.h>

int main () {
char a,b,c,d,e,f;
a=’c’;
b=’o’;
c=’u’;
d=’C’;
e=’O’;
f=’U’;

printf ("a vaut %c.\n",a);


printf ("b vaut %c.\n",b);
printf ("c vaut %c.\n",c);
printf ("d vaut %c.\n",d);
printf ("e vaut %c.\n",e);
printf ("f vaut %c.\n",f);
printf ("a, b, c, d, e, f valent : %c, %c, %c, %c, %c, %c.\n",a,b,c,d,e,f);

getchar();
return 0;
}
3.11. UN EXERCICE POUR FINIR 17

Faire un programme qui réalise l’affichage :


C

e
s
t
Ok i vaut: 1

#include <stdio.h>

int main () {
char car;
car = ’C’;
int i;
i = 1;

car = ’C’;
printf ("\n%c",car);
car = ’\’’;
printf ("\n%c",car);
car = ’e’;
printf ("\n%c",car);
car = ’s’;
printf ("\n%c",car);
car = ’t’;
printf ("%c",car);

printf ("\nOk i vaut: %d",i);


return 0;
}

3.11 Un exercice pour finir

Voir sur le campus numérique l’exercice final de ce chapitre (exercice dont la solution n’est pas fournie).

3.12 A retenir

– Exemples de type de variables :


char : caractère
int : entier
– Exemple de programme :
18 CHAPITRE 3. LES VARIABLES (1ERE PARTIE)

#include <stdio.h>

int main () {
char caractere;
caractere = ’c’;
int variable;
variable = 1;

printf ("caractere vaut: %c\n",caractere);


printf ("variable vaut: %d",variable);

return 0;
}
Chapitre 4

Les variables (2eme partie)

Durant tout ce chapitre, nous aurons pour but simple, mais complet dans sa programmation, de faire une
malheureuse calculatrice.

Attention : Nous allons voir qu’afin de pouvoir utiliser la bibliothèque mathématique du C (#include
<math.h>), il est nécessaire d’ajouter au moment de la compilation ceci :-lm (lisez bien ℓm) ; ce qui nous
donne :

gcc -o monprog monprog.c -lm

4.1 Exercice de mise en bouche

Ecrire un programme qui :


– écrit « Calculatrice : » et saute 2 lignes
– écrit « Valeur de a : » et saute 1 ligne
– attend l’appui d’une touche
– écrit « Valeur de b : » et saute 1 ligne
– attend l’appui d’une touche
– écrit « Valeur de a + b : »
Normalement, vous n’avez pas de soucis pour l’écrire... comparez ensuite avec la solution en fin de cha-
pitre...

Pas à pas, nous allons maintenant réaliser notre petit programme de calculatrice.

4.2 Déclaration des variables

Complétez le programme en :
– déclarant 2 variables a et b de type int (entier) ;
– assignant à ces deux variables les valeurs 36 et 54 ;

19
20 CHAPITRE 4. LES VARIABLES (2EME PARTIE)

– faisant afficher le résultat de la somme de a + b (et non pas en faisant afficher 90 !).

Pour faire afficher le résultat il est possible d’utiliser la fonction printf en utilisant une troisième variable.
Pour plus de facilité, nous allons utiliser un affichage direct. Celui-ci s’effectue de la façon suivante :
printf ("Valeur de a + b : %d",a+b);
Le %d sera remplacé par la valeur de a+b.

4.3 Saisie des variables

Si une calculatrice se limitait à exécuter la somme de deux nombres fixes, le boulier serait encore de mise.

Pour saisir une variable 1 , il est nécessaire d’utiliser la fonction scanf. La fonction scanf s’utilise de la
façon suivante :
Saisie de la valeur a : scanf ("%d", &a);
Comme pour printf, on reconnaît le %d pour la saisie d’un nombre entier. Le & devant le a signifie que
l’on va écrire dans la variable a.

Aïe... En fait &a signifie « l’adresse mémoire de a ». La fonction scanf va donc écrire dans l’empla-
cement mémoire (la petite boîte contenant la variable) de a. Si l’on oublie le &, on écrira chez quelqu’un
d’autre, à une autre adresse. Et là ça peut faire mal, très mal... Mais ceci est une autre histoire sur laquelle
nous reviendrons longuement par la suite... Pour l’instant, n’oubliez pas le &.

Nous allons maintenant saisir les variables a et b. Pour exemple, voici le code pour la saisie de a, la saisie
de b reste à faire par vos soins à titre d’exercice.

/* Saisie de la valeur de a */
printf ("Valeur de a :\n");
scanf ("%d",&a);

Il est conseillé d’initialiser les variables avant de les utiliser. Au début du programme, après leur déclara-
tion, assignez à a et à b la valeur 0.
Exemple : a = 0;
Si tout s’est bien passé, vous avez maintenant entre les mains une super calculatrice qui réalise des addi-
tions !

N.B. Appuyez sur la touche Enter (Entrée) après avoir tapé la valeur de a puis à nouveau sur la
touche Enter après avoir tapé la valeur de b.

Pour aller plus rapidement, il est possible d’initialiser une variable dans le même temps que sa déclaration.
Pour cela, rien de plus simple, ajoutez à la fin de la déclaration = suivi de la valeur d’initialisation :
Exemple : int i = 10;

Donc à peu de chose près, votre programme devrait ressembler à ceci (lisez le programme puis la remarque
importante qui se trouve en dessous) :

1. Quand on dit "saisir", on veut dire que l’ordinateur va attendre que l’utilisateur entre une valeur au clavier puis qu’il appuie sur
la touche entrée.
4.3. SAISIE DES VARIABLES 21

#include <stdio.h>
#include <math.h>

int main () {
int a,b; /* Déclaration des variables */

a=0; /* Initialisation des variables */


b=0;

printf("Calculatrice :\n\n");
printf("Valeur de a : ");
scanf("%d",&a);
printf("\n");
printf("Valeur de b : ");
scanf("%d",&b);

printf("Valeur de a+b : %d\n",a+b); /* Affichage de la somme */

getchar ();
return 0;
}

Remarque importante : vous pouvez tester ce programme et vous vous apercevrez que curieusement, le
programme se finit alors qu’on n’a même pas eu le temps d’appuyer sur la touche entrée ; c’est comme
si le getchar() était pûrement et simplement oublié ? ! En fait, il s’agit d’une petite "sournoiserie" du
Langage C ; en effet le scanf("%d",&b) qui se situe au dessus attend que vous entriez une valeur
au clavier. Pour fixer les idées, supposons que vous entriez la valeur 1234, la chose à bien comprendre
est que pour entrer cette valeur, vous appuyez également sur la touche entrée. La subtilité tient au
fait que le scanf("%d",&b) "veut" juste une valeur entière. Il laissera donc cet \n disponible pour la
prochaine instruction qui ira lire quelque chose au clavier (on dit que le retour chariot reste disponible dans
le tampon clavier) ; c’est précisément le getchar() qui va le récupérer et qui permettra donc la sortie du
programme.

Evolution du logiciel : exercice Aïe aïe aïe, dur dur d’être informaticien. J’ai envie de faire une super
calculatrice et je me dis qu’en fait la simplicité qui a eu lieu tout à l’heure n’est pas forcément adaptée à
l’évolution désirée.

Déclarer une troisième valeur de type int (penser à l’initialiser à 0) que l’on nommera bêtement s comme
somme. Une fois les valeurs de a et b saisies, initialisez s avec la valeur de a + b. Comme en mathé-
matiques élémentaires. Affichez la valeur de s. On devrait avoir les mêmes résultats qu’auparavant, bien
sûr.

Exercices d’application : Réalisez 2 petits programmes qui font :


– la soustraction de 2 nombres ;
– la multiplication de 2 nombres.
Une fois que vous avez votre solution, comparez avec ce qui se trouve à la page 24.
22 CHAPITRE 4. LES VARIABLES (2EME PARTIE)

4.4 Les types flottants

Un nouveau type : les nombres à virgule ou flottants (float). Le type float permet de déclarer un
nombre à virgule. Transformez les 3 précédents programmes en utilisant le type float au lieu du type
int. Enfin, si pour les int, on utilisait le format %d pour les printf et les scanf, à présent, nous allons
utiliser le format %f pour les flottants.

Petite aide : Ce petit morceau de programme saisit la valeur de a et l’affiche :

float a;
printf ("Saisie de a :");
scanf ("%f",&a);
printf ("\n a vaut : %f",a);

Pour un affichage plus agréable il est possible de fixer le nombre de chiffres après la virgule de la façon
suivante : %.[nombre de chiffres après la virgule]f
Exemple : printf ("%.2f",a);

Ouah, les 4 opérations ! ! ! Créer un quatrième programme qui réalise la division de deux nombres.
Vous pourrez vous amuser à tester avec une division par 0 ! ! ! La solution à ce petit problème sera vu dans
le cours sur les conditions (if).

Exercices à réaliser Compléter les 4 programmes afin de réaliser les opérations suivantes :
s = a + b + c
s = a - b - c
s = a / b / c
où a,b et c sont des nombres à virgules (float).

Jetez un oeil à la page 27 pour comparer avec votre solution.

4.5 D’autres fonctions utiles

La fonction abs permet d’obtenir la valeur absolue d’un nombre entier (il faut inclure stdlib.h dans ce
cas). La fonction fabs permet d’obtenir la valeur absolue d’un nombre flottant.

Utiliser cette dernière fonction pour calculer la valeur absolue de (a-b).


Exemple d’utilisation de abs : s = abs (a-b);

Remarque : la fonction fabs doit être utilisée si on manipule des nombres flottants. Pour des nombres
entiers (du type int par exemple), il faudrait utiliser la fonction abs et non fabs !

La fonction ceil permet d’obtenir l’arrondi entier supérieur d’un nombre réel. Utiliser cette fonction pour
calculer l’arrondi supérieur de (a / b).
Exemple d’utilisation de ceil : s = ceil (a/b);

! ! ! On notera que pour utiliser ces 2 fonctions mathématiques, il faut ajouter #include <math.h> et
bien sûr compiler en ajoutant l’option -lm lors de la compilation.
4.6. CORRIGÉS DES EXERCICES DU CHAPITRE 23

4.6 Corrigés des exercices du chapitre

4.6.1 Exercice de mise en bouche

#include <stdio.h>
#include <math.h>

int main () {
printf("Calculatrice :\n\n");

printf("Valeur de a : \n");
getchar();

printf("Valeur de b : ");
getchar();

printf("Valeur de a+b :\n");


return 0;
}

4.6.2 Addition de 2 nombres

#include <stdio.h>
#include <math.h>

int main () {
int a,b; /* Déclaration des variables */

a=36; /* Initialisation des variables */


b=54;

printf("Valeur de a+b : %d\n",a+b); /* Affichage de la somme */

getchar ();
return 0;
}

4.6.3 Soustraction de 2 nombres


24 CHAPITRE 4. LES VARIABLES (2EME PARTIE)

#include <stdio.h>
#include <math.h>

int main () {
int a,b; /* Déclaration des variables */
int d; /* Différence entre les 2 nombres */

a=0; /* Initialisation des variables */


b=0;

printf("Calculatrice :\n\n");
printf("Valeur de a : ");
scanf("%d",&a);
printf("\n");
printf("Valeur de b : ");
scanf("%d",&b);

d=a-b;
printf("Valeur de a-b : %d\n",d); /* Affichage de la différence */

getchar ();
return 0;
}

4.6.4 Multiplication de 2 nombres


4.6. CORRIGÉS DES EXERCICES DU CHAPITRE 25

#include <stdio.h>
#include <math.h>

int main () {
int a,b; /* Déclaration des variables */
int m; /* Résultat de la multiplication */
a=0; /* Initialisation des variables */
b=0;

printf("Calculatrice :\n\n");
printf("Valeur de a : ");
scanf("%d",&a);
printf("\n");
printf("Valeur de b : ");
scanf("%d",&b);

m = a*b;
printf("Valeur de a*b : %d\n", m);
/* Affichage de la multiplication */

getchar ();
return 0;
}

4.6.5 Transformez les 3 précédents programmes

Aucune difficulté dans cet exercice, vous ne trouverez que la correction que de la multiplication, les autres
opérations se réalisent sur le même schéma.
26 CHAPITRE 4. LES VARIABLES (2EME PARTIE)

#include <stdio.h>
#include <math.h>

int main () {
float a,b; /* Déclaration des variables */
float m; /* Résultat de la multiplication */

a=0; /* Initialisation des variables */


b=0;

printf("Calculatrice :\n\n");
printf("Valeur de a : ");
scanf("%f",&a);
printf("\n");
printf("Valeur de b : ");
scanf("%f",&b);

m = a*b;
printf("Valeur de a*b : %f\n", m);
/* Affichage de la multiplication */

getchar ();
return 0;
}

Pour l’addition :

m = a + b;
printf ("Valeur de a+b : %f", m);

Pour la soustraction :

m = a - b;
printf ("Valeur de a-b : %f", m);

4.6.6 Solutions de "exercices à réaliser"

Calculer la somme a + b + c
4.6. CORRIGÉS DES EXERCICES DU CHAPITRE 27

#include <stdio.h>
#include <math.h>

int main () {
float a,b,c,s; /* Déclaration des variables */

a=0; /* Initialisation des variables */


b=0;
c=0;
s=0;

printf("Saisie de a : ");
scanf("%f",&a);
printf("Saisie de b : "); /* Saisie variables flottantes */
scanf("%f",&b);
printf("Saisie de c : ");
scanf("%f",&c);

s = a+b+c; /* Calcul de la somme */


printf("Valeur de s : %.2f\n",s); /* Affichage de la somme */

getchar ();
return 0;
}

Calculer la différence a - b - c

d = a-b-c; /* Calcul de la différence */


printf("Valeur de d : %.2f\n",d); /* Affichage de la différence */

Calculer la division a / b / c

d = a/b/c; /* Calcul de la division */


printf("Valeur de d : %.2f\n",d); /* Affichage de la division */

4.6.7 Solutions de "autres fonctions utiles"

Calculer la valeur absolue de a - b

r=fabs(a-b); /* Calcul de la valeur absolue */


printf("Valeur de r : %f\n",r); /* Affiche la valeur absolue */

Remarque : la fonction fabs doit être utilisée si on manipule des nombres flottants. Pour des nombres
entiers (du type int par exemple), il faudrait utiliser la fonction abs et non fabs !
28 CHAPITRE 4. LES VARIABLES (2EME PARTIE)

Calculer l’arrondi de a + b

c=ceil(a/b); /* Calcul de l’arrondi */


printf("Valeur de c : %f\n",c); /* Affichage de l’arrondi */

Il aurait été possible d’utiliser %d du fait que l’arrondi est un nombre entier !

4.7 Un exercice pour finir

Voir sur le campus numérique l’exercice final de ce chapitre (exercice dont la solution n’est pas fournie).

4.8 A retenir
– Exemple de programme :

#include <stdio.h>

#include <math.h> // ne pas oublier si on veut utiliser des


// fonctions mathématiques

int main () {
float pi; // déclaration d’un nombre à virgule
pi=3.14159;
printf ("pi vaut: %f",pi); // ne pas se tromper, bien
// mettre %f et non pas %d
// par exemple !

int i=10; // déclaration + initialisation


printf("\n i vaut:%d\n",i);

int j;
printf("\n entrez une valeur:");
scanf("%d",&j); // !!!!!!!!! ne pas oublier le &
printf("\n vous venez d’entrer %d",j);

return 0;
}

– on compilera ce programme comme ceci :


gcc -o programme1 programme1.c -lm
– on le lancera comme ceci :
./programme1
Chapitre 5

Les conditions

5.1 Exercice de mise en bouche

Ecrire un programme qui met en application le théorème de Pythagore pour calculer l’hypothénuse d’un
triangle rectangle.
Rappel : Dans un triangle rectangle, l’hypothénuse (le plus grand côté) peut se calculer en appliquant la
formule suivante : √
hypothénuse = a2 + b2
où a et b sont les longueurs des côtés adjacents à l’angle droit.
Notes : – La racine carrée s’obtient par l’utilisation de la fonction sqrt(valeur) contenue dans la
bibliothèque math.h. (#include <math.h>)
2
– a peut s’obtenir par a*a.
Méthodologie : 1. Rechercher les variables nécessaires et les déclarer dans le programme.
2. Faire saisir a au clavier.
3. Faire saisir b au clavier.
4. Effectuer l’opération de la racine carrée et afficher le résultat.

Attention : Afin de pouvoir utiliser la bibliothèque mathématique du C (#include <math.h>), il


est nécessaire d’ajouter au moment de la compilation -lm ce qui nous donne :

gcc -o monprog monprog.c -lm

Une fois que vous avez proposé/tapé/compilé/testé votre solution, vous pourrez comparer avec la solution
qui se trouve à la fin de ce chapitre.

5.2 Les conditions : Si Alors Sinon

En langage C, nous écrirons quelque chose de ce type :

29
30 CHAPITRE 5. LES CONDITIONS

if (condition vraie) {
instructions 1
}
else {
instructions 2
}

en français, on traduirait celà comme ceci :

si (condition vraie) {
alors faire instructions 1
}
sinon {
faire instructions 2
}

Les conditions s’expriment avec des opérateurs logiques dont nous allons expliquer tout de suite la signi-
fication et l’utilisation.

5.3 Opérateurs logiques

Ils servent à comparer deux nombres entre eux.

Libellé Opérateur
Inférieur <
Supérieur >
Egal ==
Différent !=
Inférieur ou égal <=
Supérieur ou égal >=

Voici un exemple de programme qui demande à l’utilisateur de saisir 2 valeurs puis il affiche la valeur qui
est la plus grande :
5.4. OPÉRATEURS LOGIQUES PURS 31

#include <stdio.h>

int main () {
int valeur1; /* valeur 1 */
int valeur2; /* valeur 2 */

/* Saisie de valeur1 */
printf ("entrez une 1ere valeur : ");
scanf ("%d",&valeur1);

/* Saisie de valeur2 */
printf ("entrez 2eme valeur : ");
scanf ("%d",&valeur2);

if (valeur1<valeur2)
printf("la plus grande valeur vaut:%d",valeur2);
else
printf("la plus grande valeur vaut:%d",valeur1);

return 0;
}

5.4 Opérateurs logiques purs

Ce sont des opérateurs logiques permettant de combiner des expressions logiques.

Libellé Opérateur
Et (and) &&
Ou (or) ||
Non (not) !

" | " se nomme en informatique un pipe (prononcer païpe). Des exemples suivront bientôt...

5.5 Vrai ou faux

La valeur Vrai peut être assimilée à la valeur numérique 1 ou à toute valeur non nulle.
La valeur Faux peut être assimilée à la valeur numérique 0.
L’opérateur Ou (||) correspond alors à une addition.

Ou Vrai Faux + 1 0
Vrai Vrai Vrai 1 2 1
Faux Vrai Faux 0 1 0

L’opérateur Et (&&) correspond alors à une multiplication


32 CHAPITRE 5. LES CONDITIONS

Et Vrai Faux * 1 0
Vrai Vrai Faux 1 1 0
Faux Faux Faux 0 0 0

On notera que !(Vrai) = Faux et que !(Faux) = Vrai.

Voici quelques exemples :

int i1;
int i2;
int res;

i1=1;
i2=0;

printf("i1 || i2 = %d",i1||i2);
/* renverra 1 car : vrai||faux=vrai et vrai vaut 1 */

printf("i1 && i2 = %d",i1&&i2);


/* renverra 0 car : vrai&&faux=faux et faux vaut 0 */

printf("contraire(1)=%d",!(1));
/* renverra 0 car : !(vrai)=faux et faux vaut 0 */

5.6 Combinaison

Toutes les opérations logiques peuvent se combiner entre elles. La seule condition d’utilisation d’un si (if)
avec de telles combinaisons est de l’entourer de ( ).

Exemple : if ((car == ’a’) || (car == ’A’))

5.7 Astuce

Vous verrez souvent ce type de code écrit :

if (er) {
/* Alors faire quelque chose */
}

En appliquant ce qui a été vu précédemment, on en déduit que ce code signifie que


5.8. LES ACCOLADES 33

si (er != 0) /* si er différent de 0 */
{
/* Alors faire quelque chose */
}

ce qui donne en Langage C :

if (er != 0) { /* si er différent de 0 */
/* Alors faire quelque chose */
}

Dans l’immédiat, préférez if (er!=0) à if (er) !!!

5.8 Les accolades

Les accolades entourant les blocs d’instructions d’une condition peuvent être omises si le bloc n’est consti-
tué que d’une seule instruction.
Exemple :

/* VERSION LOURDE:...*/
if (car == ’b’) {
printf ("car vaut b.");
}
else {
printf ("car est différent de b.");
}

voici une version plus légère :

/* VERSION LEGERE:...*/
if (car == ’b’)
printf ("car vaut b.");
else
printf ("car est différent de b.");

Donc dans l’exemple ci-dessous, il faut mettre des accolades !

if (car == ’b’) { // il y a 2 instructions donc on met des { }


printf ("car vaut "); // 1ère instruction
printf(" %c",car); // 2ème instruction
} else
printf ("car est différent de b.");
34 CHAPITRE 5. LES CONDITIONS

5.9 Exercices
1. Faites saisir une variable de type entier et indiquez à l’utilisateur si celle-ci est strictement positive
ou strictement négative ou nulle.
Aide :
if (a>0)
printf ("Valeur positive");
else
printf ("Valeur négative");
2. Faites saisir une variable de type caractère et indiquez à l’utilisateur si celle-ci est une voyelle ou une
consonne. On considèrera que le caractère saisi est en minuscule.
5.10. CORRECTIONS DES EXERCICES DU CHAPITRE 35

5.10 Corrections des exercices du chapitre

5.10.1 Écrire un programme qui met en application le théorème de Pythagore

#include <stdio.h>
#include <math.h>

int main () {
float a; /* base du triangle */
float b; /* côté du triangle rectangle */
float h; /* valeur de l’hypoténuse */

/* Initialisation des variables pour pallier les erreurs */


a = 0;
b = 0;

/* Saisie de a */
printf ("Valeur de la base : ");
scanf ("%f",&a);

/* Saisie de b */
printf ("Valeur du côté : ");
scanf ("%f",&b);

/* Calcul par Pythagore */


h = sqrt (a*a + b*b);

/* Affichage du résultat */
printf ("L’hypoténuse mesure : %.2f\n",h);

/* Attendre avant de sortir */


getchar ();

return 0;
}

5.10.2 Tester le signe d’une valeur saisie au clavier


36 CHAPITRE 5. LES CONDITIONS

#include <stdio.h>

int main () {
/* Valeur que l’on va saisir */
int a = 0;

/* Saisie de a */
printf("Saisie de a : ");
scanf("%d",&a);

/* Test condition a<0 */


if (a<0)
printf("la variable a est négative.\n");

else {
/* Test condition a>0 */
if (a>0)
printf("la variable a est positive\n");
/* Sinon a est nulle */
else
printf("la variable a est nulle\n");
}

getchar ();
return 0;
}

5.10.3 Tester si un caractère saisi au clavier est une consonne ou une voyelle

#include <stdio.h>

int main () {
/* Valeur que l’on va saisir */
char car;

/* Saisie du caractère a */
printf("Saisie du caractère : ");
scanf("%c",&car);

/* Test condition car voyelle minuscule */


if ((car == ’a’) || (car == ’e’) || (car == ’i’) || (car == ’o’) ||
(car == ’u’) || (car == ’y’))
printf("la variable car est une voyelle.\n");
else
printf("la variable car est une consonne.\n");

getchar ();
return 0;
}
5.11. UN EXERCICE POUR FINIR 37

5.11 Un exercice pour finir

Voir sur le campus numérique l’exercice final de ce chapitre (exercice dont la solution n’est pas fournie).

5.12 A retenir
– La valeur Vrai peut être assimilée à la valeur numérique 1 ou à toute valeur non nulle.
– La valeur Faux peut être assimilée à la valeur numérique 0.

– ne pas oublier les parenthèses lorsqu’il y a un if :


if a>0 // ne compilera pas !!!
printf ("Valeur positive");
else
printf ("Valeur négative");
– Au contraire, il faudrait écrire :
if (a>0)
printf ("Valeur positive");
else
printf ("Valeur négative");
38 CHAPITRE 5. LES CONDITIONS
Chapitre 6

Mise au point

6.1 Prologue

L’objet de ce cours est de réaliser un petit break dans l’apprentissage du C et de s’attacher à voir ce que
l’on est capable de réaliser avec le peu de moyens que l’on a.

Ce cours sera donc constitué de 3 exercices de difficulté croissante avec apprentissage d’une nouvelle
fonction et d’un exercice complet de programmation.

6.2 Exercice 1

Réaliser un programme qui saisit un nombre et indique à l’utilisateur si celui-ci est plus grand ou plus petit
qu’un autre nombre fixé dans le programme.

Exemple :

si (nbre_saisi<10)
alors "plus petit"

Reprendre l’exercice du chapitre 4 qui disait si un nombre est strictement positif, strictement négatif ou
nul.

6.3 Retour sur getchar()

La fonction getchar() permet d’attendre la frappe d’un caractère au clavier, de le lire et de le renvoyer.
Deux utilisations peuvent être faites de getchar(), la première est celle permettant d’attendre la frappe
d’une touche sans se soucier de sa valeur, la seconde est celle permettant de lire un caractère au clavier.

39
40 CHAPITRE 6. MISE AU POINT

Exemples :
1. Attente
getchar();
2. Saisie d’un caractère
char car;
car = getchar();
A chaque fois, getchar() effectue le même traitement :
– Attendre la frappe d’une touche au clavier suivi d’un retour chariot (Entrée).
– Renvoyer le caractère frappé.
Dans le 1ercas où le getchar() est tout seul, ce caractère n’est simplement pas récupéré.

6.4 Boucle Faire ... Tant que (vrai)

do ... while, traduisez par faire ... tant que permet de réaliser une suite d’instructions tant
qu’une condition ou un ensemble de conditions est rempli.

Exemple :
1˚ lisez le programme suivant
2˚ tapez, essayez ce programme en tapant par exemple A puis la touche entrée, puis en appuyant uni-
quement sur la touche entrée plusieurs fois...
3˚ lisez les explications qui figurent en dessous du programme
4˚ comprenez ce programme... programme

#include <stdio.h>

int main () {
char car;
int sortie;

do {
printf ("Tapez S pour sortir !\n");

/* On saisit un caractère */
car = getchar ();

/* On le compare pour savoir si l’on peut sortir: */


/* sortie vaudra 1 si car vaut ’s’ ou ’S’ */
/* autrement sortie vaudra 0: */
sortie = ((car == ’s’) || (car == ’S’));
}
while (sortie==0);

return 0;
}

Remarque 1 : Cette utilisation n’est pas très belle, le retour chariot utilisé pour la saisie du caractère
étant renvoyé et interprété nous donne un affichage double. Malgré celà, au niveau de ce cours, nous nous
en contenterons.
6.5. EXERCICE 2 41

Rappels :
– Un nombre entier vaut la valeur logique vraie si celui-ci est différent de 0.
– Un nombre entier vaut la valeur logique faux si celui-ci est égal à 0.
– || signifie un ou logique (or), donc au niveau de la ligne sortie=((car==’s’)||(car==’S’));
lorsque car vaut ’s’ ou ’S’, dans ce cas sortie vaudra 1. Dans tous les autres cas, sortie vaudra
0.
Dès lors, les deux portions de programme suivantes sont rigoureusement équivalentes :

sortie = ((car == ’s’) || (car == ’S’));

if ((car == ’s’) || (car == ’S’))


sortie=1;
else
sortie=0;

Remarque 2 : pour stopper un programme qui boucle, il suffit de presser simultanément sur la touche
Ctrl et la touche C.

Remarque 3 : finalement, on comprend que getchar est assez subtile. Elle ne renvoie qu’un seul
caractère mais elle ne pourra renvoyer ce caractère que s’il a été validé par la touche entrée. Donc 2 cas de
figure :
– vous appuyez seulement sur la touche entrée. Dans ce cas getchar renverra à chaque fois la touche
entrée, autrement dit : \n.
– vous appuyez par exemple sur A puis entrée. A l’écran apparaîtra d’abord le A ce qui est le fonctionne-
ment normal de getchar ; ensuite, comme nous sommes dans une boucle, au 2ème coup, getchar
vous renverra \n.

6.5 Exercice 2

Transformez l’exemple précédent afin que l’on sorte de la boucle uniquement lorsque l’utilisateur a tapé le
nombre 10.

Attention : La saisie d’un nombre ne se fait pas par getchar mais par scanf.

6.6 Exercice 3

Pour obtenir le reste de la division entière de deux nombres, on peut utiliser le %, on appelle cela le "mo-
dulo". Ainsi 10 modulo 2 vaut 0 car la division de 10 par 2 vaut 5, en revanche, le reste de cette division
vaut 0 :
42 CHAPITRE 6. MISE AU POINT

int z;
z=10%2;
printf("10 modulo 2=%d",z);
z=10%3;
printf("10 modulo 3=%d",z);

va nous afficher :

10 modulo 2=0
10 modulo 3=1

Voici un petit exemple de programme qui permet d’obtenir des nombres aléatoires entre 0 et 99.

#include <stdio.h>
#include <stdlib.h> // sert pour les fonctions
//srand et rand
#include <time.h>

int main() {
int nb_alea; /* Nombre aléatoire */

srand (time (NULL)); /* Initialisation des nombres aléatoires */

/* Le nombre aléatoire est stocké dans une variable puis affiché */


nb_alea = rand () % 100;
printf ("%d",nb_alea);

getchar ();
return 0;
}

srand (time (NULL)) permet d’initialiser le système aléatoire. On reviendra dessus par la suite...
rand () permet d’obtenir un nombre entre 0 et RAND_MAX (RAND_MAX est assez astronomique ! ! !
de l’ordre de quelques milliards...).
rand () % 100 va donc nous renvoyer le reste de la division d’un nombre aléatoire (éventuellement
très grand) par 100, ce qui va nous faire un nombre compris entre 0 et 99...

En vous aidant de ce petit programme et de ce qui a été fait précédemment, réalisez un petit jeu qui :

1. Initialise un nombre entre 0 et 99 ;

2. Tente de faire deviner ce nombre à l’utilisateur en lui indiquant s’il est plus petit ou plus grand.
Exemple de dialogue avec l’ordinateur :
6.6. EXERCICE 3 43

Entrez votre nombre: 50


C’est plus !

Entrez votre nombre: 25


C’est moins !

... ...

Gagné !!!
44 CHAPITRE 6. MISE AU POINT

6.7 Corrigés des exercices du chapitre

#include <stdio.h>

int main () {
int nb_choisi = 33;
int nb_saisi = 0;

printf ("Votre nombre : ");


scanf ("%d",&nb_saisi);

if (nb_choisi < nb_saisi)


printf ("Mon nombre est plus petit.\n");
else {
if (nb_choisi == nb_saisi)
printf ("Mon nombre est égal.\n");
else
printf ("Mon nombre est plus grand.\n");
}

/* Attente */
getchar ();

return 0;
}

#include <stdio.h>
#include <stdlib.h>

int main () {
int valeur;

do {
printf ("Votre nombre : ");
scanf ("%d",&valeur);
}
while (valeur != 10);

return 0;
}
6.8. UN EXERCICE POUR FINIR 45

#include <stdio.h>
#include <stdlib.h> /* pour les valeurs aléatoires */
#include <time.h>

int main () {
int nb_hasard = 0;
int votre_nb = 0;

srand (time (NULL));


nb_hasard = rand () % 100 ;/* Nombre entre 0 et 100 */

do {
printf("Votre nombre : ");
scanf("%d",&votre_nb);

if (nb_hasard < votre_nb)


printf ("\nMon nombre est plus petit\n");
else
{
if (nb_hasard > votre_nb)
printf ("\nVotre nombre est plus grand\n");
}
}
while (votre_nb != nb_hasard);

printf ("Trouvé\n");
getchar ();

return 0;
}

6.8 Un exercice pour finir

Voir sur le campus numérique l’exercice final de ce chapitre (exercice dont la solution n’est pas fournie).
46 CHAPITRE 6. MISE AU POINT
Chapitre 7

Et les shadocks pompaient

Note : "Les Shadoks" est une série télévisée d’animation française en 208 épisodes de 2 à 3 minutes, créée
par Jacques Rouxel, produite par la société aaa (Animation Art-graphique Audiovisuel) et diffusée entre le
29 avril 1968 et 1973 (trois premières saisons) et à partir de janvier 2000 (quatrième saison) sur Canal+
et rediffusée sur Cartoon Network...

7.1 While

De la même façon que do {...} while(condition); , il est possible d’utiliser :


while (condition == Vrai) {...}

Exemple :

char car = ’ ’;
while ((car != ’s’) && (car != ’S’)) {
car = getchar ();
}

Attention : while (condition); signifie que tant que condition est vraie, on revient à la même
ligne. Si condition est toujours vraie, on tombe alors dans un puits. Si condition est fausse, on passe alors
immédiatement à la ligne/commande suivante.

7.2 Et les Shadoks apprenaient que reprendre équivaut à apprendre


Exercice 1 Traduisez en langage C, complétez avec les variables nécessaires, compilez, exécutez, com-
prenez :

47
48 CHAPITRE 7. ET LES SHADOCKS POMPAIENT

Faire
Saisir une touche
Tant Que (touche != S) et (touche != s)

Exercice 2 Traduisez en langage C, complétez avec les variables nécessaires, compilez, exécutez, com-
prenez :

Faire
Saisir un nombre
Tant Que (nombre != 10)

Attention : La saisie d’un nombre ne se fait pas par la fonction getchar() mais par la fonction scanf
(reportez vous au chapitre correspondant pour plus de précisions).

7.3 Fonction toupper ()

Le problème de la comparaison de la minuscule et de la majuscule de l’exercice 1 peut être contourné par


l’utilisation de la fonction toupper qui transforme un caractère minuscule en majuscule. Il faut inclure
la bibliothèque <ctype.h> par #include <ctype.h>.

La fonction toupper s’utilise de la façon suivante :

int main () {
char car;
char car_min;

car_min = ’a’
car = toupper (car_min);
printf ("%c",car);

return 0;
}

Ce programme affichera donc A.

7.4 O tant que en emporte le Shadok


Exercice 3 Ecrire le programme :
Tant que je ne tape pas un nombre impair compris entre 1 et 10 je recommence
la saisie d’un nombre

Exercice 4 Ecrire le programme :


Tant que je ne tape pas une voyelle je recommence la saisie d’une touche
7.5. ET LES SHADOKS CONTINUAIENT À POMPER POUR OBTENIR LE RÉSULTAT 49

7.5 Et les Shadoks continuaient à pomper pour obtenir le résultat

Dans les exercices qui suivent, la notion de compteur intervient. Un compteur est une variable numérique
que l’on décrémente (-1) ou incrémente (+1) suivant nos besoins.

Exemple :

int i;
i=1;
i=i+1;

printf("i vaut: %d",i); // affichera 2

En effet, quand on fait i=1, l’ordinateur prend la case mémoire où est stocké i et il lui affecte la valeur 1.
Quand on fait i=i+1, on commence par prendre connaissance de la valeur de i en mémoire (ici 1) ; on
lui ajoute 1 et on stocke le tout dans la case mémoire associée à i. Donc finalement, i vaudra 2.

Pour gagner du temps, le Langage C vous permet de remplacer une expression i=i+1 par l’expression
suivante : i++ qui fera exactement la même chose :

Exemples :

int i;

i++; /* Incrémente le compteur i */


i--; /* Décrémente le compteur i */
i++; /* revient à la même chose que i=i+1; */
i--; /* revient à la même chose que i = i-1; */

Dans les exemples précédents, le nombre de caractères entrés peut donc être comptabilisé en ajoutant 1 à
une variable à chaque fois qu’une touche est frappée.

Exercice 5 Ecrire le programme :


Tant que je n’ai pas saisi 10 nombres, je recommence la saisie d’un nombre

Exercice 6

Ecrire le programme :
Tant que je n’ai pas saisi 10 caractères, je recommence la saisie d’une
touche.

De petites difficultés peuvent surgir... vous aurez donc le droit de vous pencher plus rapidement sur la
solution.

7.6 Au clan des Shadoks, on trie, voyelles, chiffres premiers


Exercice 7 Ecrire le programme :
50 CHAPITRE 7. ET LES SHADOCKS POMPAIENT

Tant que je n’ai pas saisi 10 voyelles, je recommence la saisie d’une touche
On prendra soin d’indiquer à l’utilisateur combien de voyelles il lui reste à entrer.

Exercice 8 Ecrire le programme :


Tant que je n’ai pas saisi 10 chiffres premiers (1,3,5,7), je recommence
la saisie d’un chiffre
On prendra soin d’indiquer à l’utilisateur combien de chiffres premiers il lui reste à entrer.

7.7 Incrémentations, pré-incrémentations...

Nous avons vu qu’incrémenter désignait la même chose qu’augmenter une variable de 1 :


i++; <=> i=i+1;

Il y a cependant une nuance subtile

x=i++ passe d’abord la valeur de i à x et incrémente après


x=i−− passe d’abord la valeur de i à x et décrémente après
x=++i incrémente d’abord et passe la valeur décrémentée à x
x=−−i décrémente d’abord et passe la valeur décrémentée à x

Pour bien comprendre, détaillons le programme suivant :

int n;
n=5;

int x;
x=n++;

Détails de la ligne x=n++ :


1˚) on commence par affecter n à x, donc x va contenir la valeur 5
2˚) on augmente n de 1, donc n vaudra 6
On parlera dans ce cas d’incrémentation post-fixée...

Pour ce programme :

int n;
n=5;

int x;
x=++n;

1˚) on augmente n de 1, donc n vaudra 6


2˚) on affecte n à x, donc x va contenir la valeur 6
On parlera dans ce cas d’incrémentation pré-fixée...
7.8. CORRIGÉS DES EXERCICES DU CHAPITRE 51

7.8 Corrigés des exercices du chapitre


Exercice 1

#include <stdio.h>

int main () {
char car = 0;
printf ("Tapez ’s’ ou ’S’ pour arrêter ...\n");
do {
car = getchar ();
printf("j’ai lu car=(%c)\n",car);
}
while ((car != ’s’) && (car != ’S’));
return 0;
}

Exercice 2

#include <stdio.h>

int main () {
int nbre = 0;
printf ("Tapez 10 pour arrêter ...\n");
do {
scanf ("%d", &nbre);
}
while (nbre != 10);
return 0;
}

Exercice 3

#include <stdio.h>

int main () {
int nbre = 0;

printf ("Tapez un chiffre impair pour arrêter ...\n");


while ((nbre != 1) && (nbre != 3) && (nbre != 5) && (nbre != 7) && (nbre != 9))
scanf ("%d", &nbre);

return 0;
}

ou bien :
52 CHAPITRE 7. ET LES SHADOCKS POMPAIENT

#include <stdio.h>

int main () {
int nbre = 0;

printf ("Tapez un chiffre impair pour arrêter ...\n");


while ((nbre < 0) || (nbre > 10) || (nbre%2==0))
scanf ("%d", &nbre);

return 0;
}

Exercice 4

#include <stdio.h>
#include <ctype.h>

int main () {
char car = ’ ’;

printf ("Tapez une voyelle pour arrêter ...\n");

while ((car != ’A’) && (car != ’E’) && (car != ’I’)


&& (car != ’O’) && (car != ’U’) && (car != ’Y’)){

car = getchar ();


car = toupper (car);
}
return 0;
}

Exercice 5
7.8. CORRIGÉS DES EXERCICES DU CHAPITRE 53

#include <stdio.h>

int main () {
int nbre = 0;
int nb_nbre = 0;

printf ("Tapez 10 nombres pour arrêter ...");

do {
scanf ("%d",&nbre);
nb_nbre ++;
}
while (nb_nbre != 10);

return 0;
}

Exercice 6

Lisez la solution et ce qu’il y a en dessous :

#include <stdio.h>

int main () {
char car = ’ ’;
int nbre = 0;

printf ("Tapez 10 caractères pour arrêter ...");

do {
car = getchar ();
printf("j’ai lu car=(%c)\n",car);

nbre ++;
printf("nbre=%d\n",nbre);
}
while (nbre != 10);
return 0;
}

Voici un exemple d’exécution :

Tapez 10 caractères pour arrêter ...123456789 j’ai lu (1) nbre=1 j’ai lu (2) nbre=2 j’ai lu (3) nbre=3 j’ai lu
(4) nbre=4 j’ai lu (5) nbre=5 j’ai lu (6) nbre=6 j’ai lu (7) nbre=7 j’ai lu (8) nbre=8 j’ai lu (9) nbre=9 j’ai lu
(
) nbre=10

l’utilisateur a donc tapé 123456789 suivi de la touche entrée, il a donc bien tapé 10 caractères. Ce que
montre l’affichage de la dernière ligne avec la parenthèse ouvrante sur une ligne et la parenthèse fermante
sur la ligne suivante.
54 CHAPITRE 7. ET LES SHADOCKS POMPAIENT

Vous pouvez faire d’autres essais...

Exercice 7

#include <stdio.h>
#include <ctype.h>

int main () {
char car;
int nb_nbre = 10;

printf ("Tapez encore %d voyelles pour arrêter...\n",nb_nbre);


do {
car=getchar();

car=toupper(car);
if (car==’A’ || car==’E’ || car==’I’ || car==’O’ || car==’U’)
{
nb_nbre--;
printf ("Tapez encore %d voyelles pour arrêter...\n",nb_nbre);
}
}
while (nb_nbre != 0);

return 0;
}

Exercice 8

#include <stdio.h>
#include <ctype.h>

int main () {
int nb_nbre = 10;
int nbre;

printf ("Tapez encore %d chiffres premiers...\n",nb_nbre);


do {
scanf("%d",&nbre);
if (nbre==1 || nbre==2 || nbre==3 || nbre==5 || nbre==7) {
nb_nbre--;
printf ("Tapez encore %d chiffres premiers arrêter...\n",nb_nbre);
}
}
while (nb_nbre != 0);

return 0;
}
7.9. UN EXERCICE POUR FINIR 55

7.9 Un exercice pour finir

Voir sur le campus numérique l’exercice final de ce chapitre (exercice dont la solution n’est pas fournie).

7.10 A retenir

Voici un exemple de programme qui résume ce qui a été vu dans ce chapitre. Ce programme doit afficher à
l’écran tous les nombres pairs (inférieurs à 100) :

#include <stdio.h>

int main () {
int i = 0;
while (i<=100) {
if ((i%2)==0) /* reste de la division de i par 2 */
printf("%d",i);
/* pas de else ici, c’est inutile...*/

i++;
}
return 0;
}

Voici une autre version :

#include <stdio.h>

int main () {
int i = 0;
while (i<=100) {
printf("%d",i);
i=i+2;
}
return 0;
}

Enfin, à retenir :

x=i++ passe d’abord la valeur de i à x et incrémente après


x=i−− passe d’abord la valeur de i à x et décrémente après
x=++i incrémente d’abord et passe la valeur décrémentée à x
x=−−i décrémente d’abord et passe la valeur décrémentée à x
56 CHAPITRE 7. ET LES SHADOCKS POMPAIENT
Chapitre 8

Les boucles

8.1 Et les shadoks pédalèrent pendant 15 tours

Pour faire effectuer un certain nombre de fois une tâche, on utilise l’instruction for de la façon suivante
(avec i, une variable de type entier (int par exemple)).

for (i=point de départ; i<point d’arrivée; i=i+pas) {


instruction(s) répétée(s);
}

Ceci est rigoureusement équivalent à cela :

i=point de départ;

while (i<point d’arrivée) {


instruction(s) répétée(s);

i=i+pas;
}

Par souci de simplicité, nous dirons simplement que la formule suivante :

for (i=0; i<15; i++) {


instr;
}

signifie que l’on va exécuter instr pour i variant de 0 à 14 (<15) c’est à dire 15 fois.

57
58 CHAPITRE 8. LES BOUCLES

Exemple :

#include <stdio.h>
int main () {
int i;
for (i=0; i<15; i++) {
printf ("Je me répète pour i valant %d\n",i);
}
printf ("Je me suis répétée... 15 fois\n");

return 0;
}

8.2 Syntaxe

De la même façon que le if, le for ne nécessite pas d’accolades si le nombre d’instructions à répéter est
de 1.

Exemple : On peut utiliser cette fonctionnalité dans le programme précédent en remplaçant :

for (i=0; i<15; i++) {


printf ("Je me répète pour i valant %d\n",i);
}

par

for (i=0; i<15; i++)


printf ("Je me répète pour i valant %d\n",i);

8.3 Notion de double boucle

Il est possible de remplacer les instructions par une boucle afin de réaliser une double boucle. On obtient
donc :
8.4. EXERCICE 2 : ET LES SHADOKS FÊTÈRENT NOËL... 59

Pour i allant de ... à ... {


...
Pour j allant de ... à ... {
...
}
}

Exemple :

#include <stdio.h>
int main () {
int i;
int j;
for (i=0; i<5; i++) {
printf ("\nJe suis dans la boucle i, i vaut %d\n",i);

for (j=3; j>0; j--)


printf ("Je suis dans la boucle j, j vaut %d\n",j);
}

return 0;
}

Exercice 1 En utilisant la double boucle, écrire un programme qui écrit une étoile, passe à la ligne, écrit
deux étoiles, passe à la ligne, écrit trois étoiles... jusqu’à cinq étoiles afin d’obtenir ceci :

*
**
***
****
*****

8.4 Exercice 2 : Et les Shadoks fêtèrent Noël...

8.4.1 « Cône » du sapin

Exercice 2.a À l’aide d’une double boucle, réalisez un cône pour dessiner le haut du sapin sur 10 lignes.
Vous devriez obtenir ceci :
60 CHAPITRE 8. LES BOUCLES

*
***
*****
*******
*********
***********
*************
***************
*****************
*******************

Aide :
– Sur la ligne no 1, on affiche 9 espaces puis 1 étoile ;
– Sur la ligne no 2, on affiche 8 espaces puis 3 étoiles ;
– Sur la ligne no 3, on affiche 7 espaces puis 5 étoiles ;
– Sur la ligne no i, on affiche 10-i espaces puis 2*i-1 étoiles.
On obtient donc par exemple pour l’affichage des étoiles sur la ligne i :

for (j=0; j<((2*i)-1); j++) {


printf ("*");
}

8.4.2 Affichage du tronc

Exercice 2.b Pour poursuivre le sapin, il nous faut maintenant dessiner le tronc. Ecrire la suite du
programme en dessinant le tronc à l’aide du caractère @. Vous devriez obtenir ceci (juste pour le tronc) :

@@@
@@@
@@@

8.5 Exercice 3 : Table Ascii


Exercice 3.a

Les codes ASCII (c’est-à-dire les nombres qui représentent les caractères en informatique) vont de 0 à 255.
Ecrire un programme qui fait afficher sur des lignes successives les codes ASCII avec les caractères qui
leur correspondent (On pourra commencer l’affichage à partir du code 32 et s’arrêter au caractère 127).

Pour faire afficher le caractère associé à un code ASCII, on écrit (le %3d signifie que le nombre va être
affiché en utilisant un emplacement qui fait 3 caractères de long) :
printf ("%3d : %c", code_ascii, code_ascii);

Exemple :
8.5. EXERCICE 3 : TABLE ASCII 61

int i = 65;
printf ("%3d : %c", i, i); // affichera 65 : A

Exercice 3.b

Même chose, mais de façon propre, par exemple sous la forme d’un tableau sur 8 colonnes...
62 CHAPITRE 8. LES BOUCLES

8.6 Corrigés des exercices du chapitre

Exercice 1

#include <stdio.h>
#include <stdlib.h>

int main () {
int i;
int j;
for (i=1; i<=5; i++) {
for (j=1; j<=i; j++) {
printf ("*");
}
printf ("\n");
}

return 0;
}

On aurait aussi pu écrire :

#include <stdio.h>
#include <stdlib.h>

int main () {
int i;
int j;
for (i=1; i<=5; i++) {
for (j=1; j<=i; j++) // pas d’accolades nécessaires...
printf ("*");

printf ("\n");
}

return 0;
}

Exercice 2.a
8.6. CORRIGÉS DES EXERCICES DU CHAPITRE 63

#include <stdio.h>
#include <stdlib.h>

int main () {
int i;
int j;

for (i=1; i<=10; i++) {


for (j=0;j<10-i;j++) // les espaces...
printf(" ");

for (j=0; j<(i*2-1); j++)


printf("*");

printf ("\n");
}

return 0;
}

Exercice 2.b

Vous ajoutez ceci :

...
for (i=1; i<=3; i++)
printf (" @@@\n");
}

Exercice 3

Exercice 3.a

#include <stdio.h>
#include <stdlib.h>

int main () {
int i;
int j;

for (i=32; i<128; i++) {


printf ("%3d %c\n",i,i);
}

return 0;
}
64 CHAPITRE 8. LES BOUCLES

Exercice 3.b

#include <stdio.h>
#include <stdlib.h>

int main () {
int i;
int j;

for (i=4; i<32; i++) {


for(j=0; j<8; j++) {
printf ("%3d %c ",i*8+j,i*8+j);
}
printf("\n");
}

return 0;
}

8.7 Un exercice pour finir

Voir sur le campus numérique l’exercice final de ce chapitre (exercice dont la solution n’est pas fournie).

8.8 A retenir

Retenez l’équivalence entre les deux programmes suivants :

for (i=1; i<100; i=i+2) {


printf("i vaut:%d",i);
}

i=1;

while (i<100) {
printf("i vaut:%d",i);

i=i+2;
}
Chapitre 9

Pointeurs et Fonctions

Peu d’exercices dans ce chapitre dans lequel l’important est de comprendre les nombreux exemples cités.

9.1 Binaire, octets...

9.1.1 Binaire

Vous le savez peut-être, à la base, l’ordinateur n’arrive à comprendre que des 0 et des 1. En gros 0 : pas de
courant, 1 : du courant.

Le problème, c’est qu’on est assez vite limité, rien que pour compter jusqu’à 10, on a du mal ! ...

La solution va être de regrouper plusieurs 0 et 1, ce qui nous permettra d’aller plus loin... :

Pour l’ordinateur Pour l’humain


0 0
1 1
10 2
11 3
100 4
101 5
110 6
111 7
1000 8
1001 9
1010 10
... ...
11111111 255

Pour la machine, le nombre 7 des êtres humains serait donc représenté par trois "1" qui se suivent. Chacun
de ces "1" s’appelle un bit. Autre exemple : le nombre 1010001 est composé de 7 bits.

65
66 CHAPITRE 9. POINTEURS ET FONCTIONS

Le fait de compter comme ça, pour la machine, s’appelle du binaire.

9.1.2 Octet

Si on considère 8 bits à la suite (chacun pouvant donc prendre la valeur 0 ou 1), on appelle cela un octet.
Voici un exemple d’octet : 11110000, en voici un autre : 10101010.

9.1.3 Compter en base 10

Depuis tout petit, vous comptez en base dix, pour ce faire vous utilisez dix chiffres de 0 à 9. Pour compter
en binaire (base 2), on utilise deux chiffres (0 et 1). Si on voulait compter en base 3, on utiliserait 3
chiffres : 0, 1 et 2.

9.1.4 Compter en base 16

Il s’agit d’une base très utilisée en informatique. Problème : comment compter en base 16 ? Il nous faudrait
16 chiffres, or nous n’en avons que dix ? !

Solution :

Base 16 Base 10
0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
A 10
B 11
C 12
D 13
E 14
F 15

Tout ceci va nous servir dans la suite...


9.2. VARIABLES : POINTEURS ET VALEURS 67

9.2 Variables : pointeurs et valeurs

9.2.1 Les variables et la mémoire.

Une variable (par exemple la variable car déclarée par char car;) est en fait une petite zone mémoire
ici de 1 octet que l’on s’alloue et où l’on va ranger les informations. Chaque type de variable utilise au
moins 1 octet. Le type int varie selon les compilateurs, pour nous il utilise 4 octets c’est à dire 4 cases
mémoire. Nous avons vu précédemment qu’il était possible de voir le contenu d’une variable avec la fonc-
tion printf et qu’il était possible de mettre une valeur dans une variable avec l’opérateur = ou la fonction
scanf.

Exemple :

char car = ’C’;


printf ("%c",car);

Représentons-nous la mémoire comme une Rue remplie de maisons. Chaque case mémoire est représentée
par une maison. Chaque maison porte un numéro, son adresse postale. Pour une case mémoire, on parlera
d’adresse mémoire. L’adresse est unique pour chaque maison. Cependant, rien ne vous empêche de vous
tromper intentionnellement ou non de maison. De la même façon, une adresse mémoire d’une case mémoire
est unique mais rien ne vous empêche de vous tromper intentionnellement ou non de case mémoire.

Exemple de la vie courante :

Mr Leneuf adresse : 99, grand rue

Exemple en C :

char car;

/* avec car stocké à l’adresse :


0xF (soit 15 en décimal).

en effet: 0x signifie que l’adresse est en


hexadécimal c’est-à-dire (en base 16). */

En langage C, l’adresse mémoire est accessible en faisant précéder la variable de l’opérateur &.

Exemple :
68 CHAPITRE 9. POINTEURS ET FONCTIONS

char car = ’C’;


int nbre = 12;

printf ("Adresse de car : %p",&car);


printf ("Adresse de nbre : %p",&nbre);

Le "%p" permet d’indiquer que nous avons affaire à un pointeur et qu’il faut afficher l’adresse mémoire où
il est stocké. Cet affichage se fera en hexadécimal.

Retapez/ essayez ce programme de 4 lignes que nous venons de voir...

9.2.2 Pointeurs

Pour utiliser les adresses mémoire des variables à la place des variables elles-mêmes, on utilise les poin-
teurs. Les pointeurs sont définis par un type et se déclarent de la façon suivante :

type* variable;
ou
type *variable;
ou
type * variable;

Le signe * indique l’utilisation d’un pointeur c’est-à-dire l’utilisation d’une adresse mémoire. Le type per-
met simplement de savoir comment le compilateur C doit interpréter l’adresse mémoire lors de sa lecture.
Nous en verrons des exemples lors de l’utilisation des chaînes de caractères.
Pour enregistrer une valeur dans une adresse mémoire on écrit :

*variable = valeur

*variable signifie contenu de l’adresse mémoire.

Lire le bout de programme suivant ainsi que les explications en dessous du programme. C’est un point
délicat de ce cours, au besoin relisez 2 à 3 fois les explications :
9.2. VARIABLES : POINTEURS ET VALEURS 69

#include <stdio.h>

int main () {
char car=’C’;
char * ptr_car; /* ptr_car est une variable */
/* cette variable est particulière */
/* dans le sens où sa valeur vaudra */
/* l’adresse d’une autre variable */

printf ("Avant, le caractère est : %c\n",car);


ptr_car = &car; /* ptr_car = adresse de car */
*ptr_car = ’E’; /* on modifie le contenu de l’adresse mémoire */
printf ("\nAprès le caractère est : %c\n",car); /* on a modifié car */

return 0;
}

Explicitons cet exemple à l’aide de notre rue bordée de maisons.

ptr_car = &car; Signifie que l’on m’a donné l’adresse postale de Monsieur car.
*ptr_car = ’E’; Signifie que je rentre chez Monsieur car et que j’y dépose le caractère E.
printf ("%c",car); Signifie que l’on va lire le contenu de car (en mémoire) pour l’afficher à l’écran.

Voici un dessin qui explicite tout ceci. On supposera que la variable car est stockée à l’adresse 100 ;
la variable ptr_car sera stockée à l’adresse 110. Après l’exécution des deux premières lignes du pro-
gramme :

#include <stdio.h>

int main () {
char car=’C’;
char* ptr_car;
}

la mémoire ressemble à ceci :

nom de la variable: car ptr_car


------------------------------
contenu mémoire: | C | | ? |
------------------------------
adresse mémoire: 100 110

Nous mettons ? pour ptr_car car cette variable n’a pas encore été initialisée...

Le programme se poursuit par :


70 CHAPITRE 9. POINTEURS ET FONCTIONS

printf ("Avant, le caractère est : %c\n",car);


ptr_car = &car; /* ptr_car = adresse de car */

Il y aura donc affichage à l’écran de Avant, le caractère est : C


puis la mémoire sera modifiée comme ceci :

nom de la variable: car ptr_car


------------------------------
contenu mémoire: | C | |100 |
------------------------------
adresse mémoire: 100 110

On voit bien que ptr_car n’est qu’une "vulgaire" variable, elle contient la valeur 100 (l’adresse de la
variable car).

Finalement, la ligne :

*ptr_car = ’E’; /* on modifie le contenu de l’adresse mémoire */

conduit à modifier la mémoire comme suit :

nom de la variable: car ptr_car


------------------------------
contenu mémoire: | E | |100|
------------------------------
adresse mémoire: 100 110

Peut-être qu’il serait rentable de relire, avant les exercices, juste tout ce paragraphe ;-)

9.2.3 Exercices d’application

Exercice 1 Réaliser un programme équivalent qui change la valeur en K.

Exercice 2 Réaliser un programme équivalent qui change une valeur numérique (int) de 10 à 35.

Dans la pratique, lorsque l’on déclare un pointeur, il est plus prudent de l’initialiser :
9.2. VARIABLES : POINTEURS ET VALEURS 71

#include <stdio.h>

int main () {
char car=’C’;
char * ptr_car; /* ptr_car est une variable */
/* cette variable est particulière */
/* dans le sens où sa valeur vaudra */
/* l’adresse d’une autre variable */

ptr_car = NULL; /* on aurait aussi pu écrire ptr_car=0 */

printf ("Avant, le caractère est : %c\n",car);


ptr_car = &car; /* ptr_car = adresse de car */
*ptr_car = ’E’; /* on modifie le contenu de l’adresse mémoire */
printf ("\nAprès le caractère est : %c\n",car); /* on a modifié car */

return 0;
}

NULL vaut 0 en fait. Du coup, c’est l’adresse 0. Dans la pratique l’adresse 0 désigne donc la toute première
case de la mémoire. Cette case est utilisée par Linux ou Windows (suivant le système sous lequel vous
travaillez). En particulier, vu que cette case est utilisée par le système d’exploitation, vous n’aurez donc
pas le droit de modifier cette case. Dans la pratique, écrire prt_car=NULL sert à initialiser le pointeur et
dire que l’adresse mémoire est pour l’instant invalide. Dans la pratique aussi, c’est plus parlant/lisible que
prt_car=0.

On peut se demander à quoi servent les pointeurs ? ! En effet, ça a l’air bien compliqué alors que l’on
pourrait faire bien plus simple ; ainsi tout le travail effectué par le programme précédent se résume au code
suivant :

#include <stdio.h>

int main () {
char car=’C’;

printf ("Avant, le caractère est : %c\n",car);


car=’E’;
printf ("\nAprès le caractère est : %c\n",car); /* on a modifié car */

return 0;
}

En fait, on verra par la suite que dans certains cas, on ne peut tout bonnement pas se passer des pointeurs,
notamment pour certaines manipulations au sein des fonctions...
72 CHAPITRE 9. POINTEURS ET FONCTIONS

9.3 Les fonctions

9.3.1 Définition générale

Une fonction est un petit bloc de programme qui à l’image d’une industrie va créer, faire ou modifier
quelque chose.
Un bloc de programme est mis sous la forme d’une fonction si celui-ci est utilisé plusieurs fois dans un
code (un programme) ou simplement pour une question de clarté (Imaginez un livre sans paragraphes !).
A l’identique de notre fonction principale main(), une fonction s’écrit de la façon suivante :

<type de sortie> <nom de la fonction> (<paramètres d’appels>) {


Déclaration des variables internes à la fonction

Corps de la fonction

Retour
}

Souvenez vous de cette remarque que nous avions déjà faite : il ne faut pas mettre les < et > ! ! !, on met
ceci pour rendre les programmes plus lisibles... Vous ne mettrez ces < et > que pour des cas du type
#include <stdio.h>.

Voici un exemple de fonction qui renvoie le maximum de 2 nombres :

int maximum (int valeur1, int valeur2) {


int max;
if (valeur1<valeur2)
max=valeur2;
else
max=valeur1;
return max; // on pourrait aussi écrire: return (max);
}

Le programme complet donnerait ceci :


9.3. LES FONCTIONS 73

/* ligne 01 */ #include <stdio.h>


/* ligne 02 */
/* ligne 03 */ int maximum (int valeur1, int valeur2) {
/* ligne 04 */ int max;
/* ligne 05 */ if (valeur1<valeur2)
/* ligne 06 */ max=valeur2;
/* ligne 07 */ else
/* ligne 08 */ max=valeur1;
/* ligne 09 */
/* ligne 10 */ return max;
/* ligne 11 */ }
/* ligne 12 */
/* ligne 13 */ int main () {
/* ligne 14 */ int i1,i2;
/* ligne 15 */ printf("entrez 1 valeur:");
/* ligne 16 */ scanf("%d",&i1);
/* ligne 17 */ printf("entrez 1 valeur:");
/* ligne 18 */ scanf("%d",&i2);
/* ligne 19 */ printf("max des 2 valeurs :%d\n",maximum(i1,i2));
/* ligne 20 */
/* ligne 21 */ return 0;
/* ligne 22 */ }

Dans la pratique, quand vous tapez ./programme pour lancer votre programme, l’ordinateur va lancer la
fonction main qui se trouve à la ligne /* ligne 13 */. L’ordinateur demande donc ensuite de saisir
2 valeurs pour i1 et i2. Supposons que l’utilisateur ait tapé les valeurs 111 et 222 au clavier.

Ensuite, à la /* ligne 19 */, l’ordinateur commence à afficher "max des 2 valeurs :" puis
il tombe ensuite sur le "%d" qu’il va chercher à remplacer par quelque chose... Ce quelque chose sera en
fait l’appel maximum(i1,i2). L’ordinateur saute alors à la /* ligne 03 */. A ce niveau, on peut
comprendre intuitivement que la variable valeur1 prendra la valeur de i1 (c’est-à-dire 111) et la valeur
valeur2 prendra la valeur de i2 (c’est-à-dire 222), justement à cause de l’appel de maximum(i1,i2).

Ensuite, la fonction maximum va se dérouler normalement ; après avoir passé les lignes 4 à 9, la valeur de
max sera de 222.

A la /* ligne 10 */ on va donc sortir de la fonction maximum pour revenir à l’appel fait par le
printf de la /* ligne 19 */. Dès lors, le fameux "%d" du printf sera remplacé par la valeur
issue du return max, c’est à dire 222...

Enfin, le programme se termine.

Remarque de vocabulaire : on dit que les variables i1 et i2 sont passées en paramètre de la fonction
maximum lors de l’appel : maximum(i1,i2).
74 CHAPITRE 9. POINTEURS ET FONCTIONS

9.3.2 void

Le type void signifie vide, il est utilisé :


– comme type de sortie pour les fonctions qui ne retournent aucun résultat.

Exemple :

/* Fonction bête affichant un caractère */


void affiche_car (char car) {
printf ("%c",car);
}

Exemple de programme complet :

#include <stdio.h>

/* Fonction bête affichant un caractère */


void affiche_car (char car) {
printf ("%c",car);
}

int main () {
char c;
int i;

printf("Veuillez entrer un caractère:");


scanf("%c",&c);

for (i=0;i<100;i++)
affiche_car(c);
return 0;
}

9.3.3 Variables globales et locales

Les variables déclarées dans les fonctions sont dites locales. Il existe aussi les variables dites globales qui
sont déclarées en dehors de toute fonction y compris le main ().
Les variables globales sont modifiables et accessibles par toutes les fonctions sans avoir besoin de les passer
en tant que paramètres. Il est de ce fait extrêmement dangereux d’utiliser des variables globales.
Les variables locales ne sont modifiables et accessibles que dans la fonction où elles sont déclarées. Pour
les modifier ou les utiliser par une autre fonction, il est nécessaire de les passer en tant que paramètres.

Exemple de variable locale :


9.3. LES FONCTIONS 75

#include <stdio.h>

int carre (int val) {


int val_retour = 0; /* Déclaration variable locale */

val_retour = val * val;


return val_retour;
}

int main () {
int val_retour = 0; /* Déclaration variable locale */

val_retour = carre (2);


printf ("Le carré de 2 est : %d\n", val_retour);

return 0;
}

val_retour est dans les deux cas une variable locale. Bien qu’elles aient le même nom dans les fonctions
main et carre, les deux variables n’ont rien à voir entre elles.

Exemple de variables globales :

#include <stdio.h>

int val = 0;
int val_retour = 0;

void carre () {
val_retour = val * val;
}

int main () {
val = 2;

carre ();
printf ("Le carré de 2 est %d\n", val_retour);

return 0;
}

val et val_retour sont des variables globales.


On constate que le programme devient rapidement illisible...

9.3.4 Utilisation et modification de données dans les fonctions

L’appel d’une fonction peut s’effectuer à l’aide de paramètres.


Ces paramètres peuvent être utilisés en les déclarant dans les parenthèses.
76 CHAPITRE 9. POINTEURS ET FONCTIONS

Exemple :

#include <stdio.h>

void affiche_Nombre (int no) {


printf ("Le nombre est : %d\n",no);
}

int main (){


affiche_Nombre (12);

return 0;
}

Ces paramètres peuvent être modifiés à condition qu’ils soient passés par adresse c’est à dire que la fonc-
tion reçoive un pointeur. Voir le programme ci-dessous et les explications/déroulements du programme
ci-après :

Exemple :

#include <stdio.h>

void avance_Position (int* pointeur_int) {


*pointeur_int = *pointeur_int + 2;
}

int main () {
int x=0;

printf ("Position de départ : %d\n",x);

avance_Position (&x);

printf ("Nouvelle position : %d\n",x);

return 0;
}

Imaginons que pointeur_int se trouve en mémoire logé à l’adresse 20 et x à l’adresse 10 :

Nom de la variable: x pointeur_int


-------------------------------
Contenu : | | | |
-------------------------------
Position en mémoire: 10 20

A présent, déroulons le programme principal :


9.3. LES FONCTIONS 77

1. int x=0; : déclaration et initialisation de x à 0

2. avance_Position (&x); : appel de la fonction avance_Position (&x);, avec la valeur 10


(l’adresse de x)

x pointeur_int
------------------------------
| 0 | |10|
------------------------------
10 20

3. void avance_Position (int* pointeur_int) : On lance cette fonction et pointeur_int


vaut donc 10

4. * pointeur_int = (* pointeur_int) + 2; : (* pointeur_int) pointe sur x en fait. L’an-


cienne valeur de x va être écrasée par la nouvelle valeur : 0+2

5. printf("Nouvelle position :%d",x); : on va bien se retrouver avec 2 comme valeur.

En fait, nous sommes obligés d’utiliser des pointeurs pour pouvoir modifier certaines variables. Voir
l’exemple du programme ci-dessous où l’on aurait bien aimé qu’après l’appel de la fonction calcule_double
la variable i soit doublée :

Exemple :

void calcule_double (int x){


x=x+x ;
}

main () {
int i =2;
calcule_double(i);
printf("i vaut à présent :%d",i); /* il vaut toujours 2 !!! */
}

Généralement, on dit en résumé qu’une fonction n’est pas capable de modifier ses arguments. L’usage
des pointeurs devient dans ce cas nécessaire...

9.3.5 Exercice 3 :

Modifier le programme précédent afin d’avoir à dispoisition une fonction qui prend en paramètre un entier
et le modifie pour le doubler...

Une fois que vous aurez votre solution, comparez avec celle qui se situe à la fin...
78 CHAPITRE 9. POINTEURS ET FONCTIONS

9.3.6 Piège !

Attention, ce type d’opération sur les pointeurs est dangereuse.

int* x;
*x++;

augmentera l’adresse de x (on va chez le voisin) et non la valeur, pour cela il faut écrire :

(*x)++;

Ceci est une faute courante, prenez garde ...

9.3.7 Exercice 4 :

Reprendre le programme du sapin de Noël du chapitre précédent et réaliser 2 fonctions : cone (int n)
et tronc (int n) qui dessinent respectivement le cône du sapin sur n lignes et le tronc en position n
(n blancs avant le tronc).
Ecrire un programme qui, utilisant ces deux fonctions, dessine un sapin sur n lignes.

9.3.8 Les prototypes

Dans la pratique quand on veut écrire un programme qui contient des fonctions, on fait les choses propre-
ment. Ainsi, plutôt que cette version :
9.3. LES FONCTIONS 79

#include <stdio.h>

int max (int x, int y) {


if (x<y)
return y;
else
return x;
}

int main () {
int i1,i2;

i1=123;
i2=1267;

printf("%d\n",max(i1,i2));

return 0;
}

on lui préfère la suivante où l’on ajoute la ligne "int max (int x, int y);" que l’on appelle un
prototype.

#include <stdio.h>

/* PROTOTYPE : */
int max (int x, int y);

int max (int x, int y) {


if (x<y)
return y;
else
return x;
}

int main () {
int i1,i2;

i1=123;
i2=1267;

printf("%d\n",max(i1,i2));

return 0;
}

Pour l’instant, prenez cela pour argent comptant ;-) on verra dans la suite quelques intérêts à procéder
ainsi...
80 CHAPITRE 9. POINTEURS ET FONCTIONS

9.3.9 Un rappel avant le dernier exercice...

Le programme suivant ne présente pas de difficultés, son but est d’échanger 2 variables i1 et i2 :

#include <stdio.h>

int main () {
int i1,i2;

i1=123;
i2=1267;

int variable_auxiliaire;
printf("i1=%d i2=%d Avant l’échange\n",i1,i2);

variable_auxiliaire=i1;
i1=i2;
i2=variable_auxiliaire;

printf("i1=%d i2=%d Après l’échange\n",i1,i2);

return 0;
}

9.3.10 Exercice 5 :

Reprendre le programme précédent et réaliser l’échange au moyen d’une fonction.


9.4. CORRIGÉS DES EXERCICES DU CHAPITRE 81

9.4 Corrigés des exercices du chapitre

Exercice 1

#include <stdio.h>

int main () {
char car = ’C’;
char * ptr_car;

printf ("Avant le caractère est : %c\n",car);


ptr_car = &car;
*ptr_car = ’K’;
printf ("Après le caractère est : %c\n",car);

return 0;
}

Exercice 2

#include <stdio.h>

int main () {
int val = 10;

int *ptr_val;
printf ("Avant le nombre est : %d\n",val);
ptr_val = &val;
*ptr_val = 35;
printf ("Après le nombre est : %d\n",val);

return 0;
}

Exercice 3
82 CHAPITRE 9. POINTEURS ET FONCTIONS

#include <stdio.h>

void calcule_double (int* pointeur_int) {


*pointeur_int = *pointeur_int + *pointeur_int;
}

int main () {
int i=10;

printf ("i=%d\n",i);
calcule_double (&i);
printf ("i=%d\n",i);

return 0;
}
9.4. CORRIGÉS DES EXERCICES DU CHAPITRE 83

Exercice 4

void tronc (int n) {


int i=0, j=0;
#include <stdio.h>
#include <stdlib.h> for (j=1; j<=3; j++) {
for (i=1; i<=n; i++)
// Dessin du cône du sapin printf (" ");
void cone (int n) {
int i=0, j=0; printf ("@@@\n");
}
for (i=1; i<=n; i++) { }
for (j=0;j<n-i;j++)
printf(" "); int main () {
int nb_lig = 15;
for (j=1; j<= (i*2-1); j++)
printf("*"); cone (nb_lig);
tronc (nb_lig - 2);
printf ("\n");
} getchar ();
} return 0;
}

Exercice 5
84 CHAPITRE 9. POINTEURS ET FONCTIONS

#include <stdio.h>

/* PROTOTYPE : */
int echange (int *x, int *y);

int echange (int *x, int *y) {


int variable_auxiliaire;

variable_auxiliaire=*x;
(*x)=(*y);
(*y)=variable_auxiliaire;
}

int main () {
int i1,i2;

i1=123;
i2=1267;

printf("i1=%d i2=%d Avant l’échange\n",i1,i2);

echange(&i1,&i2); // la fonction a besoin de l’adresse de i1 et i2


// du coup, x dans la fonction echange, va pointer sur i1
// y, va pointer sur i2

printf("i1=%d i2=%d Après l’échange\n",i1,i2);

return 0;
}

9.5 Un exercice pour finir

Voir sur le campus numérique l’exercice final de ce chapitre (exercice dont la solution n’est pas fournie).

9.6 A retenir
– compter en base 2 (on dit aussi en binaire) revient à n’utiliser que des 0 et des 1
– un octet est formé de 8 bits ; exemple 11110101
– base dix : la base classique
– base 16 : les lettres utilisées : 0, 1, ..., 9, A, B, C, D, E, F.

– un pointeur n’est rien d’autre qu’une variable. Cette variable est juste un peu particulière dans le sens où
elle peut contenir l’adresse d’une autre variable.

Pour finir 2 programmes qui reprennent l’essentiel de ce qui a été vu :


9.6. A RETENIR 85

#include <stdio.h>

int main () {
int i;
i=100;

int * pointeur_sur_i;
pointeur_sur_i=&i; // ce pointeur pointe à présent sur i

*pointeur_sur_i=200;
printf ("i vaut à présent %d\n",i); // i vaudra 200...

return 0;
}

#include <stdio.h>

void avance_Position (int* x);

void avance_Position (int* x) {


*x = *x + 2;
}

int main () {
int i=0;
int x=0;

printf ("Position de départ : %d\n",x);

for (i = 0; i<5; i++) {


avance_Position (&x);
printf ("Nouvelle position : %d\n",x);
}

return 0;
}

Nous venons de terminer sans doute le chapitre le plus difficile de ce cours, le plus dûr est derrière nous...
86 CHAPITRE 9. POINTEURS ET FONCTIONS
Chapitre 10

Tableaux & Chaînes de caractères

10.1 Tableaux

10.1.1 Définition

Un tableau est un ensemble d’éléments consécutifs. Celui-ci peut être constitué de plusieurs lignes et
colonnes. Nous n’utiliserons dans un premier temps que les tableaux à une seule ligne.

Exemple de tableau de caractères :

Numéro de case 0 1 2 3 4 5 6 7
Contenu A B C D E F G H

! ! ! Les cases d’un tableau sont numérotées à partir de 0.

10.1.2 Déclaration

Un tableau se déclare de la manière suivante :


<type> <nom du tableau> [<taille du tableau>];

Exemples :

/* Déclaration d’un tableau de 10 caractères */


char tab_char [10];

/* Déclaration d’un tableau de 10 nombres */


int tab_int [10];

87
88 CHAPITRE 10. TABLEAUX & CHAÎNES DE CARACTÈRES

10.1.3 Utilisation

On accède à une case du tableau en mettant le nom du tableau, suivi d’un crochet "[" puis un numéro de
case et un crochet fermant : "]" :

Exemple :

/* déclarations: */
int tab_int[10]; /* tableau de 10 cases (0 à 9) d’entiers */
char tab_char[10]; /* tableau de 10 cases (0 à 9) de caractères */

/* utilisations: */
tab_char [3] = ’C’; /* Accès à la case 3 (la quatrième) de tab_char */
tab_int [6] = 10; /* Accès à la case 6 (la septième) de tab_int */

Attention : Le compilateur ne vérifie pas que vous utilisez le tableau dans ses limites. Il vous est donc
possible d’écrire à l’extérieur de votre tableau donc chez le voisin. C’est l’un des bugs le plus courant de
l’informatique.

10.2 Chaînes de caractères

Les chaînes de caractères sont des tableaux de caractères suivis du 0 (zéro ; ne pas confondre avec le
caractère O (de Ollalah par exemple...)) qui est considéré lui aussi comme un caractère. Une chaîne s’écrit
donc : chaîne + 0.

Exemple : « Eric » s’écrit dans un tableau de 5 caractères de la façon suivante :

Caractère ’E’ ’r’ ’i’ ’c’ ’\0’ (***)


Code A SCII 69 114 105 99 0
Case 0 1 2 3 4

(***) cette histoire d’ \0 va être expliquée par la suite...

10.2.1 Déclaration d’une chaîne de caractères

Une chaîne de caractères se déclare sous la forme d’un tableau de caractères de longueur fixe. Attention,
comme signalé auparavant, si vous dépassez la longueur de tableau, vous écrivez chez le copain.

Exemple :
char m_chaine [20];
permettra d’enregistrer des chaînes de 19 caractères maximum (20-1 pour le 0 de fin de chaîne).

D’autre part, il est possible de déclarer une chaîne de caractères sans en spécifier la longueur de départ de
la façon suivante :
char chaine [] = "Eric";
10.2. CHAÎNES DE CARACTÈRES 89

10.2.2 Affichage d’une chaîne de caractères

Une chaîne de caractères s’affiche grâce à la commande printf et le format %s.

Exemple :
printf ("%s",chaine);

10.2.3 Longueur d’une chaîne

La longueur d’une chaîne de caractères s’obtient par la fonction strlen. Le 0 de fin de chaîne n’est pas
compté dans cette longueur.

Exemple :

#include <stdio.h>
#include <string.h> //nécessaire pour disposer
// de la fonction strlen

int main () {
char ch [] = "toto" ;
printf ("La longueur de %s est : %d", ch, (int) (strlen (ch))) ;

return 0;
}

Affichera : « La longueur de toto est 4 » à l’écran.

10.2.4 Initialisation d’une chaîne de caractères

Le programme suivant :
char tab[10];
printf("adresse où commence tab=%p",&tab[0]);

affichera la même chose que le programme suivant :


char tab[10];
printf("adresse où commence tab=%p",tab);

On voit donc que tab est la même chose que &tab[0]. Dès lors, le programme suivant est incorrect :
char tab[10];
tab="coucou";

En effet, tab désigne l’adresse où débute le début du stockage de ce tableau en mémoire. Ainsi, on voit
bien que dans l’affectation tab="coucou"; le membre de gauche désigne une adresse alors que le
membre de droite désigne une chaîne de caractères ; les deux n’étant pas du même type, le Langage C se
plaindrait...
90 CHAPITRE 10. TABLEAUX & CHAÎNES DE CARACTÈRES

Pour initialiser une chaîne de caractères, il faut en fait utiliser la fonction strcpy. Cette fonction nous
impose d’inclure la bibliothèque string.h :

#include <stdio.h>
#include <string.h>

int main() {
char line[80];
strcpy(line,"un exemple de chaine initialisee...");
return 0;
}

Il y a donc recopie (et ce, caractère par caractère) de la chaine "un exemple de chaine initialisée..."
en démarrant à l’adresse où se trouve line stockée en mémoire.

Enfin, si l’on souhaite lire une chaîne directement au clavier, on peut utiliser la fonction scanf :

#include <stdio.h>

int main() {
char line[81];
printf("veuillez entrer votre chaine:");
scanf("%s",line);

/* scanf("%s",&line[0]) ferait la même chose en plus


compliqué... */

printf("la chaine saisie vaut:%s",line);


return 0;
}

10.2.5 Exercices

Lisez tout ce paragraphe avant de démarrer l’exercice...

En utilisant une boucle (for) :


– remplissez un tableau de 10 caractères avec les lettres de l’alphabet en commençant par A (code A SCII
65) ;
Le tableau devra donc contenir ceci :

Case 0 1 2 3 4 5 6 7 8 9 10
Contenu ’A’ ’B’ ’C’ ’D’ ’E’ ’F’ ’G’ ’H’ ’I’ ’J’ 0
– faîtes afficher la chaîne de caractères ainsi obtenue (n’oubliez pas de rajouter le 0) ;
– faîtes afficher chaque caractère du tableau sous la forme « Caractère no 0 : A ».

Rappel : Je peux écrire Tab_car [i] = code_ascii; où code_ascii est un entier représen-
tant le code A SCII du caractère désigné.
10.2. CHAÎNES DE CARACTÈRES 91

Aide : Pour faire afficher un seul caractère, on utilisera la syntaxe suivante :

int pos /* Position dans le tableau */


printf ("Caractère n˚ %d : %c",pos, Tab_car [pos]);

La valeur 0 peut être assignée à un élément de tableau de la façon suivante :


Tab_car [la bonne position] = 0;

ou encore (étant donné que le caractère ’\0’ désigne la même chose que 0) par :

Tab_car [la bonne position] = ’\0’;

On préfèrera généralement cette dernière solution qui est plus explicite et montre bien que l’on travaille
avec des chaînes de caractères.

10.2.6 gets : saisie d’une chaîne de caractères

Description La fonction gets permet de saisir une chaîne de caractère validée par un retour chariot.
Attention, bien que cette chaîne nécessite d’être validée par un retour chariot, celui-ci n’est pas enregistré
dans le tableau de caractères.

Remarque : lorsque vous compilez votre programme par gcc -o essai essai.c, vous verrez qu’un
message d’erreur du type "warning : gets may be dangerous", ne vous inquiétez pas. Le com-
pilateur craint simplement qu’à l’exécution, l’utilisateur saisisse une chaine de caractère qui fasse plus de
80 caractères longs... auquel cas, des problèmes risquent de surgir si la chaine qui vient d’être entrée au
clavier dépasse les 80 caractères maximum.

Exemple d’utilisation

#include <stdio.h>
int main() {
char line[81];
/* 81 : taille arbitraire supposée suffisante */
/* Une ligne écran = 80 caractères + 1 case pour le ’\0’ de fin de chaîne */

printf( "Saisissez une chaîne de caractère :\n" );


gets( line );
/* La frappe de l’utilisateur sera enregistrée dans line,
on suppose qu’il ne frappera pas plus de 80 caractères,
sinon aïe aïe aïe */
printf( "\nLa chaîne de caractères saisie est : \n%s\n", line );
return 0;
}

Exemple d’exécution :
92 CHAPITRE 10. TABLEAUX & CHAÎNES DE CARACTÈRES

Saisissez une chaîne de caractère :


Bonjour !

La chaîne de caractère saisie était :


Bonjour !

Notons qu’il n’y a qu’un retour chariot (celui affiché par la fonction printf).

10.2.7 Passage d’une chaîne de caractères en paramètres

Si on veut passer un tableau en paramètres à une fonction, en fait on ne passe que la 1ère case du tableau
et pas tout le tableau. Les 2 fonctions suivantes sont donc rigoureusement équivalentes :

int ma_saisie (char chaine[]) {


...Faire ce qu’il faut...
return 0;
}

int main () {
char ma_chaine [30];
ma_saisie (ma_chaine);
return 0;
}

int ma_saisie (char* chaine) {


...Faire ce qu’il faut...
return 0;
}

int main () {
char ma_chaine [30];
ma_saisie (ma_chaine);
return 0;
}

En fait, dans la pratique, lorsque vous écrivez :

int ma_saisie (char chaine[]) {

l’ordinateur retranscrira, à la compilation, votre programme sous la forme :

int ma_saisie (char * chaine) {

Nous reviendrons longuement, par la suite sur cette équivalence pointeurs/tableaux... donc pas d’inquiétude
si ça n’est pas lumineux pour l’instant ;-)
10.3. QUELQUES FONCTIONS UTILES 93

10.3 Quelques fonctions utiles

10.3.1 La fonction strcat

strcat (<s>, <t>) : ajoute <t> à la fin de <s>.

Cette fonction ajoute une chaîne à la fin d’une autre (en algorithmique, on appelle cela une concaténation).

Le programme suivant affichera la chaîne Bonjour Paul a l’écran :

int main () {
char chaine1[20]="Bonjour ";
char chaine2[20]="Paul";

strcat(chaine1,chaine2); /* ajoute chaine2 */


/* à la fin de chaine1 */

printf("%s",chaine1);

return 0;
}

On remarquera qu’il est important de dimensionner la chaine1 à une taille suffisante, sans quoi on pour-
rait avoir des difficultés pour faire tenir la chaîne "Bonjour Paul" dans la chaine1.

10.3.2 La fonction strncpy

strncpy (<s>, <t>, <n>) : fonctionne comme strcpy mais copie au plus <n> caractères de
<t> vers <s>.

10.3.3 La fonction strncat

strncat (<s>, <t>, <n>) : ajoute au plus <n> caractères de <t> à la fin de <s>.

10.3.4 La fonction strcmp

Cette fonction désigne l’abréviation de string compare :


strcmp (<s>, <t>) : compare <s> et <t> lexicographiquement et fournit un résultat :

– nul si <s> est égal à <t>


– négatif si <s> précède <t>. Exemple : strcmp("AAAA","BBBB") renverrait -1
– positif si <s> suit <t>. Exemple : strcmp("BBBB","AAAA") renverrait +1
94 CHAPITRE 10. TABLEAUX & CHAÎNES DE CARACTÈRES

10.3.5 Les fonctions sprintf et sscanf

Nous terminerons par deux fonctions très puissantes qui peuvent faire des miracles ;-)

sprintf ( <chaine cible>, <chaine de formatage>, <expr1>, <expr2>, ...)

La fonction sprintf renvoie une valeur négative en cas d’erreur et le nombre de caractères stockés dans
s sinon.

Exemple :

char s[200];
int i=15;
int code;

code=sprintf(s,"%d",i);

La fonction va convertir l’entier i et va le stocker dans la chaine s. A l’arrivée, s vaudra "15".

La fonction sscanf réalise la réciproque :

Exemple :

char s[]="12.5 12.3 11.6";


float a,b,c;
int code;

code=sscanf(s,"%f%f%f",&a,&b,&c);

On retrouvera donc les bonnes valeurs pour a, b et c, à savoir respectivement : 12.5 12.3 et 11.6.

sscanf renvoie une valeur négative en cas d’erreur, et est égal au nombre de variables affectées sinon.

10.4 Tableaux à 2 dimensions

Un tableau à 2 dimensions se déclare de la façon suivante :


<type> <nom du tableau> [<taille 1ère dimension>][<taille 2nde dimension>];

Exemple :

int table [5] [5]; /* représente un tableau d’entiers de 5 lignes *


5 colonnes.*/

Voici un autre exemple :


10.4. TABLEAUX À 2 DIMENSIONS 95

float tab[3][2]= {{ 1.2, -1.3 },


{ 8.5, 12.4 },
{ -123.0, 4.0 }};

Voici à présent un programme qui affiche le contenu d’un tableau à 2 dimensions :

#include <stdio.h>
int main() {
int tab[5][10];
int i,j;
/* Pour chaque ligne ... */
for (i=0; i<5; i++) {
/* ... considérer chaque case */
for (j=0; j<10; j++)
printf("%d ", tab[i][j]);
/* Retour à la ligne */
printf("\n");
}
return 0; /* c’est pareil que return (0) */
}

Si on souhaite initialiser ce tableau avec des valeurs lues au clavier, voici comment faire :

#include <stdio.h>
int main() {
int tab[5][10];
int i,j;
/* Pour chaque ligne ... */
for (i=0; i<5; i++) {
/* ... considérer chaque case */
for (j=0; j<10; j++)
scanf("%d", &tab[i][j]);
/* Retour à la ligne */
printf("\n");
}
return 0;
}

Remarque, on aurait pu écrire ce programme sous la forme suivante :


96 CHAPITRE 10. TABLEAUX & CHAÎNES DE CARACTÈRES

#include <stdio.h>

#define LIGNES 5
#define COLONNES 10

int main() {
int tab[LIGNES][COLONNES];
int i,j;
/* Pour chaque ligne ... */
for (i=0; i<LIGNES; i++) {
/* ... considérer chaque case */
for (j=0; j<COLONNES; j++)
scanf("%d", &tab[i][j]);
/* Retour à la ligne */
printf("\n");
}
return 0;
}

L’usage de #define LIGNES 5 va faire que l’ordinateur va parcourir tout votre programme et faire
un chercher-remplacer : chercher la chaîne LIGNES et remplacer par 5. Il fera ensuite pareil pour
COLONNES.

On comprendra pourquoi il ne faut pas mettre des " ;" à la fin des #define sous cette forme :

#include <stdio.h>

#define LIGNES 5;
#define COLONNES 10; ...

En effet, les rechercher-remplacer aboutiraient au programme suivant qui ne compilerait pas :

int main() {
int tab[5;][10;]; /* compile pas !!! */
int i,j;
/* Pour chaque ligne ... */
for (i=0; i<5;; i++) { /* compile pas !!! */
/* ... considérer chaque case */
for (j=0; j<10;; j++) /* compile pas !!! */
scanf("%d", &tab[i][j]);
/* Retour à la ligne */
printf("\n");
}
return 0;
}
10.5. CORRECTION DES EXERCICES 97

10.5 Correction des exercices

10.5.1 Boucle for

En utilisant une boucle (for), remplissez un tableau de 10 caractères avec les lettres de l’alphabet en
commençant par A (code A SCII 65). Faîtes afficher la chaîne de caractères ainsi obtenue (n’oubliez pas de
rajouter le 0). Faîtes afficher chaque caractère du tableau sous la forme "Caractère no 0 : A".

#include <stdio.h>
#include <stdlib.h>
/* Affichage de la chaîne */
int main () { printf ("Tableau : %s\n",tableau);
/* 10 caractères
+ 0 de fin de chaîne */ /* Saut d’une autre ligne */
char tableau [11]; printf ("\n");
int i=0; /* compteur */
/* Affichage de chacun des caractères */
/* Remplissage du tableau for (i=0; i<10; i++)
avec les caractères */ printf ("Caractère n˚%d
for (i=0; i<10; i++) : %c\n",i,tableau [i]);
tableau [i] = ’A’ + i;
return 0;
/* Ajout du 0 de fin de chaine */
tableau [10] = 0; }

10.6 Un exercice pour finir

Voir sur le campus numérique l’exercice final de ce chapitre (exercice dont la solution n’est pas fournie).

10.7 A retenir

Pour finir un petit programme qui reprend l’essentiel de ce qui a été vu. Il permet de lire une chaine1 au
clavier puis de recopier cette chaine dans une seconde chaine2 et de l’afficher à l’écran :
98 CHAPITRE 10. TABLEAUX & CHAÎNES DE CARACTÈRES

#include <stdio.h>
#include <string.h>
int main () {
int longueur;
char chaine1[81]; /* 80 caractères + ’\0’
char chaine2[81];

printf("Veuillez entrer votre chaine de caractères");


scanf("%s",chaine1); /* identique à scanf("%s",&chaine1[0]); */

strcpy(chaine2,chaine1); /* !!! attention à l’ordre !!! */

printf("chaine2 vaut: %s \n",chaine2);

strcpy(chaine1,""); /* et pas chaine1=""; !!!!!!! */


strcat(chaine1,"Pierre ");
printf("Veuillez entrer votre chaine de caractères");
scanf("%s",chaine2);
strcat(chaine1,chaine2);
strcat(chaine1," Paul Jacques...");
return 0;
}
Chapitre 11

Structures et Fichiers

11.1 Les types synonymes

Cette notion de type synonyme va nous servir tout à l’heure...

Il est possible grâce au déclarateur typedef de définir un type nouveau qui est un type synonyme. Ainsi
la définition suivante :
typedef int entier ;
définit un type synonyme appelé entier ayant les mêmes caractéristiques que le type prédéfini int.
Une fois cette définition réalisée, nous pouvons utiliser ce nouveau type pour définir des variables et nous
pouvons mélanger les variables de ce type avec des variables entières pour réaliser des expressions.

entier e1=23, e2=5, te[50]={1,2,3,4,5,6,7};


int i;
i = e1 + e2;
te[20] = i - 60;

11.2 Structures

Une structure est un objet composé de plusieurs champs qui sert à représenter un objet réel ou un concept.
Par exemple une voiture peut être représentée par les renseignements suivants : la marque, la couleur,
l’année, etc.

11.2.1 Déclaration

2 choix s’offrent à nous.

Solution 1 :

99
100 CHAPITRE 11. STRUCTURES ET FICHIERS

struct nom de la structure {


/* Définition de la structure */
} nom de la variable qui aura comme forme cette structure;

Solution 2 :

typedef struct {
/* Définition de la structure */
} nom de la structure;

Voici respectivement un exemple de chaque :

#define LONGUEUR 40

struct personne{
char nom [LONGUEUR];
char prenom [LONGUEUR];
int age;
};

struct personne p;

#define LONGUEUR 40

typedef struct {
char nom [LONGUEUR];
char prenom [LONGUEUR];
int age;
} personne;

personne p;

La 2ème solution est donc plus simple et plus élégante à l’usage.

L’accès aux éléments d’une structure, que nous appelons aussi champs, se fait selon la syntaxe :

nom_de_variable.nom_du_champ

Voici quelques exemples :


11.3. BASES SUR LES FICHIERS 101

#include <stdio.h>

typedef struct {
char nom [40];
char prenom [20];
int age;
} personne;

int main () {
personne p;
printf("Veuillez entrer le nom de la personne:");
scanf("%s",p.nom);

printf("Veuillez entrer le prénom de la personne:");


scanf("%s",p.prenom);

printf("Veuillez entrer l’age de la personne:");


scanf("%d",&p.age); /* ne pas oublier le ’&’ !!! */

printf("Voici les caractéristiques de cette personne:\n");


printf("nom=%s\n",p.nom);
printf("prenom=%s\n",p.prenom);
printf("age=%d\n",p.age);

return 0;
}

11.3 Bases sur les fichiers

Un fichier représente tout ce qui est enregistré sur votre disque dur ou presque, on va dire tout ce qui porte
un nom. Il est possible de créer, de lire ou d’écrire dans des fichiers. A noter, que certains fichiers par contre
peuvent être protégés en lecture, en écriture ou les deux.

Voici un programme que nous allons détailler ci-après :


102 CHAPITRE 11. STRUCTURES ET FICHIERS

/*00*/ #include <stdio.h>


/*01*/ #include <stdlib.h>
/*02*/ int main() {
/*03*/ FILE *P_FICHIER; /* pointeur sur FILE */
/*04*/ char NOM_FICHIER[30], NOM_PERS[30];
/*05*/ int i,NB_ENREG;
/*06*/ /* 1ère partie: Créer et remplir le fichier */
/*07*/ printf("Entrez le nom du fichier à créer : ");
/*08*/ scanf("%s", NOM_FICHIER);
/*09*/
/*10*/ /* w: write r: read a: append*/
/*11*/ P_FICHIER = fopen(NOM_FICHIER, "w");
/*12*/ if (P_FICHIER == NULL) {
/*13*/ printf("Impossible de créer le fichier \n");
/*14*/ exit(-1); // Abandonner le programme
/*15*/ }
/*16*/
/*17*/ printf("Nombre d’enregistrements à créer : ");
/*18*/ scanf("%d", &NB_ENREG);
/*19*/
/*20*/ for (i = 0; i<NB_ENREG; i++) {
/*21*/ printf("Entrez le nom de la personne : ");
/*22*/ scanf("%s", NOM_PERS);
/*23*/ fprintf(P_FICHIER, "%s\n", NOM_PERS);
/*24*/ }
/*25*/ fclose(P_FICHIER);
/*26*/
/*27*/ /* 2ème partie : Lire et afficher le fichier */
/*28*/ P_FICHIER = fopen(NOM_FICHIER,"r"); /* read */
/*29*/ if (P_FICHIER == NULL) {
/*30*/ printf("\aImpossible d’ouvrir le fichier \n");
/*31*/ exit(-2); // Abandonner le programme
/*32*/ }
/*33*/ while (!feof(P_FICHIER)) {
/*34*/ fscanf(P_FICHIER, "%s ", NOM_PERS);
/*35*/ printf("NOM : %s\n", NOM_PERS);
/*36*/ }
/*37*/ fclose(P_FICHIER);
/*38*/ return 0;
/*39*/ }

– ligne /*03*/ : on crée une variable P_FICHIER qui va pointer sur un type FILE. Sans entrer dans les
détails, le type FILE est du type structure (vu au paragraphe précédent).

– ligne /*08*/ : l’utilisateur va saisir une chaîne au clavier. Cette dernière sera stockée dans la variable
NOM_FICHIER. Supposons pour fixer les idées que l’utilisateur tape au clavier essai.txt. Le fi-
chier qui sera donc crée sur le disque dur portera ce nom.

– ligne /*11*/ : fopen va créer une sorte de lien entre le fichier du disque dur qui s’intitule essai.txt et
la variable P_FICHIER. Ainsi dans la suite, vous allez faire des opérations sur la variable P_FICHIER
et toutes ces opérations seront répercutées par le langage C au niveau du disque dur, sur le fichier
essai.txt. Dans ce cas précis, on peut faire les 3 opérations suivantes :
– P_FICHIER=fopen(NOM_FICHIER, "w"); : si le fichier essai.txt existait déjà sur le
11.3. BASES SUR LES FICHIERS 103

disque dur, il est purement et simplement écrasé puis réinitialisé à vide. Si il n’existait pas encore,
le fichier est crée sur le disque dur, pour l’instant il est vide.
– P_FICHIER=fopen(NOM_FICHIER, "r"); : si le fichier essai.txt existait déjà sur le
disque dur, il est simplement ouvert en lecture. On se positionne sur le premier caractère du fichier. Si
le fichier n’existe pas (typiquement, on s’est trompé de nom), la fonction fopen renvoie alors NULL.
– P_FICHIER=fopen(NOM_FICHIER, "a"); : si le fichier essai.txt existait déjà sur le
disque dur, il est simplement ouvert. Ensuite, on se positionne sur la fin de ce fichier, prêt à ajou-
ter quelque chose après la dernière ligne. On comprend mieux le "a" : append. Si le fichier n’existe
pas (typiquement, on s’est trompé de nom), le fichier est crée sur le disque dur, pour l’instant il est vide.

– ligne /*12*/ : il est toujours prudent de faire ce test. Le pointeur sera nul s’il y a eu un problème lors de
l’accès au fichier (nom incorrect...).

– ligne /*14*/ : sortie catastrophe, le programme s’arrête immédiatement. La valeur -1 est renvoyée au
système d’exploitation (UNIX par exemple). Il est à noter que l’usage de la fonction exit impose d’in-
clure #include <stdlib.h>.

– ligne /*22*/ : on lit au clavier le nom d’une personne.

– ligne /*23*/ : en fait, un fprintf n’est pas très différent d’un printf. La seule différence est qu’au
lieu d’être écrite sur l’écran, la chaîne NOM_PERS sera écrite dans le fichier essai.txt.

– ligne /*25*/ : on indique au programme C que l’on a fini de travailler sur le fichier essai.txt pour
l’instant. Il faut toujours penser à faire cette opération.

– ligne /*28*/ : on commence par ré-ouvrir le fichier, en lecture, cette fois-ci. Si le fopen se passe bien
(ce que l’on peut supposer !), on se positionne alors au début de la 1ère ligne du fichier.

– ligne /*33*/ : feof désigne l’abréviation de file end of file. Donc cette ligne se traduit par tant que l’on
atteint pas la fin du fichier P_FICHIER...

Enfin, voici une autre fonction qui peut se montrer très utile :

char *fgets(char *ligne, int maxligne, FILE *P_FICHIER)

La fonction fgets lit à partir du fichier au maximum maxligne-1 caractères et les stocke dans la chaîne
de caractères ligne. La lecture s’arrête sur \n qui est alors inclus dans la chaîne. La chaîne est complétée
par \0. La fonction renvoie NULL si la fin de fichier est atteinte.

Voici un exemple de programme qui va simplement afficher le contenu du fichier essai.txt à l’écran
(lisez le puis étudiez la remarque qui le suit) :
104 CHAPITRE 11. STRUCTURES ET FICHIERS

#include <stdio.h>

const maxligne=100; /* à peu de chose près :*/


/* équivalent à : */
/* #define maxligne 100 */

char ligne[100];
FILE *p_fichier;

int main() {
p_fichier=fopen("essai.txt","r");

while (! feof(p_fichier)) {
fgets(ligne,maxligne,p_fichier);
if (! feof(p_fichier))
printf("J’ai lu:%s\n",ligne);
}
fclose(p_fichier);
}

Le test suivant peut paraître curieux :

if (! feof(p_fichier))
printf("J’ai lu:%s\n",ligne);

En fait, il est nécessaire du fait que le feof (p_fichier) sera positionné à vrai dès qu’il y aura eu une
tentative infructueuse de la fonction fgets. Ainsi, lorsque le fgets lit la dernière ligne du fichier, un
appel, dans la foulée au test feof (p_fichier) renverrait faux. Ce n’est que si l’on refait un fgets
(qui sera donc infructueux) que là, le test feof (p_fichier) renverrait vrai. Donc finalement, on voit
bien le problème, pour la toute dernière ligne, le fgets va échouer et l’instruction printf("J’ai
lu:%s\n",ligne); risque bien de renvoyer n’importe quoi !

11.3.1 Fichiers et structures

Voici un exemple qui mêle fichiers et structures :


11.3. BASES SUR LES FICHIERS 105

#include <stdio.h>
#include <stdlib.h>

typedef struct {
char nom [40];
char prenom [20];
int age;
} personne;

int main() {
FILE *P_FICHIER; /* pointeur sur FILE */
/* Créer et remplir le fichier */

P_FICHIER = fopen("essai.txt","w");
if (P_FICHIER == NULL) {
printf("\aImpossible de créer le fichier \n");
exit(-1); // Abandonner le programme
}

personne p;
printf("Veuillez entrer le nom de la personne:");
scanf("%s",p.nom);
printf("Veuillez entrer le prénom de la personne:");
scanf("%s",p.prenom);
printf("Veuillez entrer l’age de la personne:");
scanf("%d",&p.age); /* ne pas oublier le ’&’ !!! */

fprintf(P_FICHIER, "%s\n",p.nom);
fprintf(P_FICHIER, "%s\n",p.prenom);
fprintf(P_FICHIER, "%d\n",p.age);

fclose(P_FICHIER);
return 0;
}

Exercice :

A l’aide de votre éditeur préféré, créez un fichier intitulé nombres.txt. Vous y stockerez les nombres
suivants :

1
2
3
4
50

Ecrire un programme qui va lire ce fichier puis créer un second fichier intitulé doublage.txt et dont le
contenu sera le suivant :
106 CHAPITRE 11. STRUCTURES ET FICHIERS

1
1
2
2
3
3
4
4
50
50
11.4. CORRECTION DE L’EXERCICE 107

11.4 Correction de l’exercice

Remarque : ici, nous utilisons une variante afin de détecter la fin du fichier. En effet, il faut savoir que
fscanf n’est rien d’autre qu’une fonction qui présente la particularité de renvoyer le nombre de valeurs
correctement lues.

Dès lors, on peut faire des choses de ce type :

...
int valeur_lue;
while (fscanf(p_source,"%d",&valeur_lue)==1) { // tant que l’on
//réussit à lire une valeur
fprintf(p_destination,"%d\n", valeur_lue) ;
fprintf(p_destination,"%d\n", valeur_lue) ;
}
...

#include <stdio.h>
#include <stdlib.h>

void creer_fichier_double (char source[], char destination[]) {


FILE *p_source, *p_destination;

/* Ouvrir le fichier dont le nom est dans la variable source,


en lecture */
p_source=fopen("nombres.txt","r") ;

/* Ouvrir le fichier dont le nom est dans la variable destination,


en écriture */
p_destination=fopen("doublage.txt","w") ;

/* Renvoyer un message si il y a eu un souci au moment de l’ouverture


d’un des 2 fichiers */
if ((p_source==NULL)||(p_destination==NULL)) {
printf("Gros problème au niveau de l’un des fichiers !!!\n") ;
exit(-1) ;
}
108 CHAPITRE 11. STRUCTURES ET FICHIERS

/* Réaliser le coeur de l’opération de transfert : */


int valeur_lue;
while (fscanf(p_source,"%d",&valeur_lue)==1) { // tant que l’on
// réussit à lire une valeur
fprintf(p_destination,"%d\n", valeur_lue) ;
fprintf(p_destination,"%d\n", valeur_lue) ;

}
fclose(p_source);
fclose(p_destination);
}

int main () {
creer_fichier_double ("nombres.txt","doublage.txt") ;

return 0 ;
}

11.5 Un exercice pour finir

Voir sur le campus numérique l’exercice final de ce chapitre (exercice dont la solution n’est pas fournie).
Chapitre 12

Debugger un programme

12.1 La chasse aux bugs

L’objectif de ce chapitre est de vous permettre de vous débrouiller tout seul lorsque vous rencontrez un
bug...

Tout d’abord on fera la différence entre une erreur de compilation et une erreur à l’exécution (appelée
bug).

12.1.1 Deux types d’erreurs

- erreur de compilation : vous tapez gcc -o essai essai.c pour le programme suivant :

#include <stdio.h>
int main () {
floatt f ;

return 0 ;
}

Le compilateur vous renvoie un message du type :


Essai.c :3 : ’floatt’ undeclared (first use in this function).

- erreur à l’exécution : pour le programme suivant :

109
110 CHAPITRE 12. DEBUGGER UN PROGRAMME

#include <stdio.h>
int main () {
float f=1/0;

return 0 ;
}

une fois compilé, vous le lancez par ./essai et l’ordinateur affiche un message du type : Floating point
exception. En effet, il n’aime pas trop les divisions par zéro ;-)

12.1.2 Un phénomène surprenant...

Tapez le programme suivant puis compilez le et enfin testez le...

#include <stdio.h>
int main () {
printf ("Je suis ici") ;
while (1)
;

return 0 ;
}

Rien ne s’affiche ? ! En fait cela provient du fait que les entrées sorties sont optimisées en Langage C.
Ainsi, lorsque vous faites un printf vous pouvez penser que ce dernier apparaîtra directement à l’écran.
Il n’en est rien ! Dans un souci d’optimisation, ce qui doit être affiché à l’écran atterrit temporairement dans
une zone mémoire de l’ordinateur. Cette zone mémoire est affichée à l’écran soit lorsqu’elle est pleine soit
lorsque l’on place un " \n " dans le printf :

Essayez :

#include <stdio.h>
int main () {
printf ("Je suis ici\n") ;
while (1)
;

return 0 ;
}

Essayez ensuite :
12.1. LA CHASSE AUX BUGS 111

#include <stdio.h>
int main () {
while (1) {
printf ("*");
usleep(1000); /* pause 1000 micro secondes...*/
}
return 0 ;
}

Ici, quelque chose s’affiche car la zone mémoire est pleine au bout d’un instant...

12.1.3 La chasse aux bugs...

Nous laissons de côté l’usage des debuggers " élaborés " tels que ddd par exemple (dans l’immédiat). La
démarche pour corriger un bug est toujours la même :

1˚) localiser où est le bug. Par exemple, dans le programme suivant qui n’affiche rien pour la raison précé-
demment évoquée (le problème de l’" \n ") :

#include <stdio.h>

int main () {
int i ;
for (i=0 ; i<100 ; i++)
printf ("i=%d",i) ;
while (1)
;
return 0 ;
}

Le rajout des " mouchards " (un mouchard est simplement un printf) dans le programme ci-dessous :

#include <stdio.h>
int main () {
int i ;
printf ("1˚) Je suis ici\n") ; /* 1 er mouchard */

for (i=0 ; i<100 ; i++)


printf ("i=%d",i) ;
printf ("2˚) Je suis ici\n") ; /* 2 eme mouchard */

while (1)
;
return 0 ;
}

2˚) Il se peut que la phase 1˚ précédente vous ait aidé à localiser l’endroit où " ça coince " mais que vous
112 CHAPITRE 12. DEBUGGER UN PROGRAMME

ne voyiez pas pourquoi. Prenons l’exemple suivant :

#include <stdio.h>
int main () {
int i,j;
i=0;
j=0;
printf ("1˚) Je suis ici\n") ;
if ((i==0) && (i=j)) {
printf ("2˚) Je suis ici\n");
...
}
return 0 ;
}

Vous êtes persuadé que l’ordinateur devrait afficher 2˚) Je suis ici or il n’en est rien ? ! Vous en êtes à
invoquer un bug dans le compilateur ? ! La démarche alors est toujours la même : l’ordinateur ne fait pas ce
que vous voulez et ce à un endroit précis que vous avez localisé. Vous allez placer des mouchards jusqu’à
voir précisément le contenu de quelle(s) variable(s) pose problème :

#include <stdio.h>
int main () {
int i,j;
i=0;
j=0;
printf ("1˚) Je suis ici\n");
printf("i=%d j=%d\n",i,j); // AJOUT D’UN MOUCHARD ICI
if (i==0) && (i=j) { /* (i=j) => i vaudra 0, et 0=faux !!! */
printf ("2˚) Je suis ici\n");
...
}
return 0 ;
}

Avant d’attaquer la suite, lisez juste les deux paragraphes Conversions de type et Un usage très utile des
conversions de type à la page 119, revenez ensuite à la suite de ce chapitre...

12.1.4 Bonne chasse...

Remarque : comme toujours, les solutions des exercices qui vont suivre se trouvent à la fin du chapitre...
cependant, cherchez un peu avant de vous y reporter.

Copiez le programme suivant tel quel, puis débuggez le. Ce programme est censé afficher ceci à l’écran :
i=0
i=1
...
12.1. LA CHASSE AUX BUGS 113

i=9
La moyenne vaut : 4.50000

#include <stdio.h>
int main () {
int i, somme;
for (i=0 ; i<10 ; i++);
printf ("i=%d\n",i) ;
somme = somme + i;
printf("La moyenne vaut:%d",somme/i) ;
return 0 ;
}

12.1.5 Les erreurs de segmentation...

Vous rencontrez ce type d’erreur lorsque votre programme accède à une zone de la mémoire qui lui est
interdite : vous êtes sur un segment de la mémoire sur lequel vous n’avez pas le droit de travailler. Le
programme suivant peut provoquer de telles erreurs :

#include <stdio.h>
int main () {
int i=1;
scanf("%d",i);

return 0 ;
}

Soit vous voyez tout de suite l’erreur... soit vous ajoutez des mouchards :

#include <stdio.h>
int main () {
int i=1;
printf ("1˚) Je suis ici\n") ;

scanf("%d",i);
printf ("2˚) Je suis ici\n");

return 0 ;
}

qui vous permettront rapidement de voir que le problème provient de la ligne scanf("%d",i) car seul
le message "1˚) Je suis ici" s’affiche et pas le message "2˚) Je suis ici" 1

1. P.S : n’oubliez pas les \n dans vos printf pour la raison évoquée plus haut... ! ! ! ! ! ! ! ! !
114 CHAPITRE 12. DEBUGGER UN PROGRAMME

Le problème vient donc de i qui vaut 1... le scanf va tenter de stocker ce que vous venez d’entrer au
clavier à l’adresse mémoire 1 ! Cette dernière est traditionnellement réservée au système d’exploitation,
d’où l’erreur...

Il en va de même du programme ci-dessous qui pourrait poser des problèmes car on sort des bornes du
tableau :
! ! ! : On ne vous demande pas de tester ce programme... juste de vous rappeler qu’on doit accéder aux
bornes d’un tableau entre les indices 0 et TAILLE-1...

#include <stdio.h>
#define TAILLE 10

. . .
. . .

int main () {
. . .
int i;
int tab[TAILLE];

. . .

for (i=0; i<TAILLE*TAILLE; i++) /* !!! */


tab[i]=i;

. . .

return 0 ;
}

12.1.6 Le debugger ddd

Ce debugger est très efficace pour trouver les erreurs de segmentation. Copiez le programme suivant :

#include <stdio.h>
#include <string.h>

int main () {
int * p;
p=NULL;
*p=123;
printf("\n je suis ici...\n");
}

Vous le compilez puis vous le lancez... ça devrait effectivement mal fonctionner...


12.1. LA CHASSE AUX BUGS 115

A présent, faites :
gcc -o essai essai.c -g
ddd essai
puis fermez les petites fenêtres " parasites " qui apparaissent au lancement de ddd puis cliquez sur le
bouton run (il faut parfois chercher un peu dans les menus)...

Maintenant, comme expliquer cette erreur ? Lorsque vous faites p=NULL;, vous placez donc la valeur
0 dans cette variable. Ce qui revient à dire que p pointe sur la première case de la mémoire. Si dans la
foulée vous faites *p=123; celà revient à vouloir mettre la valeur 123 dans la case 0 de la mémoire.
Le problème, c’est que cette case est utilisée par le système d’exploitation, d’où l’erreur car le système
d’exploitation va vous interdire de modifier cette case.

Moralité : en cas d’erreur de segmentation, tentez tout d’abord un ddd...

12.1.7 Une dernière chasse...

Soit le programme suivant qui doit afficher la chaîne de caractères chaîne à l’envers, et ce caractère par
caractère :

#include <stdio.h>
#include <string.h>

int main () {
int i;
char chaine[]="! euggubed tse emmargorp el";

for (i=strlen(chaine); i =! 0; i--)


printf("%s",chaine[i]) ;
return 0 ;
}

A vous de :
– 1˚) retaper ce dernier,
– 2˚) débugguer ce dernier...

12.1.8 Une dernière sournoiserie...

Soit le programme suivant que vous allez tester :


116 CHAPITRE 12. DEBUGGER UN PROGRAMME

#include <stdio.h>

int main () {
int i;
int i1,i2 ;
char c1,c2;

printf("1) Entrez un nombre: ");


scanf("%d",&i1);
printf("2) Entrez un nombre: ");
scanf("%d",&i2);

printf("1) Entrez une lettre: ");


scanf("%c",&c1);
printf("2) Entrez une lettre: ");
scanf("%c",&c2);

printf("1) J’ai récupéré lettre 1:%d\n",(int)c1);


% renverra 10 càd le code ascii de ’\n’
printf("2) J’ai récupéré lettre 2:%d\n",(int)c2);
}

On voit donc que l’\n subsiste du premier caractère et pollue la variable c2. en effet, quand vous faites
un :

scanf ("%c",&c1)

vous demandez à l’ordinateur de lire un unique caractère. Du coup l’\n restera de côté (pour l’instant).
Une solution pour remédier à ce travers consiste à utiliser systématiquement la fonction gets(chaine)
à la place de chaque scanf.

donc, en cas de problème avec des \n - remplacer tous les scanf ("%d", ....) ... scanf("%c",....) par du
gets

Cette dernière solution est normalement celle qui vous fera gagner le plus de temps.

Inutile de retaper les 2 programmes qui vont suivre :


12.1. LA CHASSE AUX BUGS 117

/* ancienne version: */
int i;
char c;
printf("Entrez un caractère:");
scanf ("%c",&c);

printf("Entrez un chiffre:");
scanf("%d",&i);

Rappel : la fonction sscanf que nous utilisons ci-dessous a été vue à la page 94.

/* nouvelle version: */
int i;
char c;
char chaine[100];

printf("Entrez un caractère:");
gets(chaine);
c=chaine[0];

printf("Entrez un chiffre:");
gets(chaine);
sscanf(chaine,"%d",&i) ;

12.1.9 Solutions

Bonne chasse...

#include <stdio.h>
int main () {
int i, somme=0;
for (i=0 ; i<10 ; i++) {
printf ("i=%d\n",i) ;
somme = somme + i;
}
printf("La moyenne vaut:%f",(float) somme/i) ;
return 0 ;
}

Une dernière chasse...


118 CHAPITRE 12. DEBUGGER UN PROGRAMME

#include <stdio.h>
#include <string.h>

int main () {
int i;
char chaine[]="! euggubed tse emmargorp el";

for (i=strlen(chaine); i =! 0; i--) /* au choix : != ou >= */


printf("%s",chaine[i]) ; /* mettez %c à la place de %s */
return 0 ;
}

12.1.10 A retenir

On retiendra que pour trouver un bug, ça n’est pas si compliqué que cela :
1. on s’aperçoit que le programme n’a pas le comportement que l’on veut,
2. on cherche à isoler le plus précisément possible l’endroit où le choix incorrect a pu se produire. Par
exemple, si le programme affiche 1 alors qu’il devrait afficher 111 on fait afficher la/les variables
qui sont impliquées dans cet affichage,
3. ! ! ! ne pas oublier les \n dans les printf :
4. poursuivre le rajout de mouchards jusqu’à comprendre où et pourquoi le programme ne fait pas ce
qu’il devrait faire.
Enfin, si vous avez une erreur de segmentation, tentez d’utiliser le debugger ddd (ne pas oublier de com-
piler avant avec l’option -g) :
gcc -o essai essai.c -g
puis tapez :
ddd essai
Chapitre 13

Complements

13.1 Conversions de type

Pour convertir un type en un autre, on fait ce que l’on appelle un "cast". Imaginons que l’on souhaite
convertir un nombre du type float en un entier du type int. Voici comment procéder à l’aide de l’usage
des parenthèses "(" et ")" :

int main () {
float f;
int i;

f=3.1415;
i=(int) f; /* résultat dans i: 3 */
/* donc la partie décimale */
/* est perdue... */

return 0 ;
}

13.2 Un usage très utile des conversions de type

Le programme suivant :

119
120 CHAPITRE 13. COMPLEMENTS

int main () {
printf("Résultat : %f",3/4);

return 0 ;
}

afficherait Résultat : 0 ! ! ! En effet, l’opérateur "/" réaliserait ici une division entière de l’entier 3 par
l’entier 4 qui vaudrait donc 0.

Deux solutions sont possibles pour remédier à ce problème :

On écrit 3.0 à la place de 3 ce qui va forcer le programme à faire une division en flottants :

int main () {
printf("Résultat : %f",3.0/4);

return 0 ;
}

autre solution : on convertit le nombre 3 en un flottant :

int main () {
printf("Résultat : %f",(float)3/4);

return 0 ;
}

la division se fera alors en flottants.

On notera que la solution suivante ne fonctionnerait pas :

int main () {
printf("Résultat : %f",(float)(3/4));

return 0 ;
}

car on ferait tout d’abord la division de 3 par 4 avec pour résultat l’entier 0, puis on convertit ce dernier en
un flottant pour obtenir à l’affichage :
Résultat : 0.0
13.3. LA FONCTION PUTCHAR 121

13.3 La fonction putchar

Le programme suivant :

int main () {
char c;
c=’A’;
putchar(c);

return 0 ;
}

est équivalent à :

int main () {
char c;
c=’A’;
printf("%c",c);

return 0 ;
}

donc on peut voir que la fonction putchar affiche un unique caractère à l’écran. Elle est donc moins
puissante que printf et pourrait donc être oubliée (en première lecture), en revanche elle est très utilisée
par la communauté...

13.4 Allocation dynamique de mémoire

13.4.1 Les fonctions malloc et sizeof

La fonction malloc de la bibliothèque stdlib.h nous aide à réserver un bloc mémoire au cours de
l’exécution du programme.

malloc(N)

fournit l’adresse d’un bloc en mémoire de N octets libres ou la


valeur zéro s’il n’y a pas assez de mémoire.

Exemple : supposons que nous ayons besoin d’un bloc en mémoire pour un texte de 4000 caractères.
Nous disposons d’un pointeur tab sur char, c’est-à-dire : char *tab. Alors l’instruction : tab =
122 CHAPITRE 13. COMPLEMENTS

malloc(4000); fournit l’adresse d’un bloc de 4000 octets libres et l’affecte à tab.

S’il n’y a plus assez de mémoire, tab obtient la valeur zéro (rappel : c’est la même chose que NULL). Si
nous voulons réserver de la mémoire pour des données d’un type dont la grandeur varie d’une machine
à l’autre, nous avons besoin de la grandeur effective d’une donnée de ce type. L’opérateur sizeof nous
aide alors à préserver la portabilité du programme.

– sizeof (<var>) : fournit la grandeur de la variable <var>


– sizeof (<const>) : fournit la grandeur de la constante <const>
– sizeof (<type>) : fournit la grandeur pour un objet du type <type>

Après la déclaration,

short tab1[10];
char tab2[5][10];

sizeof(tab1) s’évalue à 20
sizeof(tab2) s’évalue à 50
sizeof(double) s’évalue à 8 (généralement... !)
sizeof("bonjour") s’évalue à 8 (pensez à l’\0)
sizeof(float) s’évalue à 4 (généralement... !)

Nous voulons réserver de la mémoire pour x valeurs du type int la valeur de x est lue au clavier :

int x;
int *pNum;
printf("Introduire le nombre de valeurs :");
scanf("%d", &x);
pNum = malloc(x*sizeof(int));

Remarque : certains compilateurs imposent d’écrire

pNum = (int *) malloc(x*sizeof(int));

En fait, étant donné que malloc renvoie un pointeur sur une adresse, on s’assure simplement (par la
conversion de type, ou "cast") que cette adresse est bien un pointeur sur un entier, d’où l’usage de (int
*).

Autre exemples :
13.4. ALLOCATION DYNAMIQUE DE MÉMOIRE 123

char * pointeur_sur_chaine;
float * pointeur_sur_float;

pointeur_sur_chaine = (char *) malloc(1000*sizeof(char));


pointeur_sur_float = (float *) malloc(10000*sizeof(float));

Exemple : le programme suivant lit 10 phrases au clavier, recherche des blocs de mémoire libres assez
grands pour la mémorisation et passe les adresses aux composantes du tableau texte[]. S’il n’y a pas
assez de mémoire pour une chaîne, le programme affiche un message d’erreur et interrompt le programme
avec le code d’erreur -1. Nous devons utiliser une variable d’aide intro comme zone intermédiaire (non
dynamique). Pour cette raison, la longueur maximale d’une phrase est fixée à 500 caractères :

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
/* Déclarations */
char intro[500];
char *texte[10];
int i;
/* Traitement */
for (i=0; i<10; i++) {
gets(intro);
/* Réservation de la mémoire */
texte[i] = malloc(strlen(intro)+1);
/* S’il y a assez de mémoire, ... */
if (texte[i]!=NULL)
/* copier la phrase à l’adresse */
/* fournie par malloc, ... */
strcpy(texte[i],intro);
else {
/* sinon quitter le programme */
/* après un message d’erreur. */
printf("ERREUR: Pas assez de mémoire \n");
exit(-1);
}
}
return 0;
}

13.4.2 La fonction free

Si nous n’avons plus besoin d’un bloc de mémoire que nous avons réservé à l’aide de malloc, alors nous
pouvons le libérer à l’aide de la fonction free de la bibliothèque <stdlib.h>.

free( <Pointeur> ) : libère le bloc de mémoire désigné par le <Pointeur>; n’a pas d’effet si le
pointeur a la valeur zéro.

Attention !
124 CHAPITRE 13. COMPLEMENTS

* La fonction free peut aboutir à un désastre si on essaie de libérer de la mémoire qui n’a pas été allouée
par malloc.
* La fonction free ne change pas le contenu du pointeur ; il est conseillé d’affecter la valeur NULL au
pointeur immédiatement après avoir libéré le bloc de mémoire qui y était attaché.
* Si nous ne libérons pas explicitement la mémoire à l’aide free, alors elle est libérée automatiquement à
la fin du programme.

Exemple :

char * pointeur_sur_chaine;

pointeur_sur_chaine = (char *) malloc(1000*sizeof(char));


...

/* à présent, on a plus besoin de cette zone mémoire : */


free(pointeur_sur_chaine);
pointeur_sur_chaine=NULL;

13.5 Avez-vous bien compris ceci ?

Soit le programme suivant :

#include <stdio.h>

int main() {
int i; /* Ligne 0 */
printf ("%d\n",i); /* Ligne 1 */

int * p; /* Ligne 2 */
* p = 12; /* Ligne 3 */

return 0;
}

A la ligne 1, le Langage C se fera un plaisir de vous afficher n’importe quoi car la variable i n’a pas été
initialisée.

La ligne 3 a de fortes chances de planter le programme ! La raison en est que p n’a pas été initialisé non
plus. En particulier si on faisait un printf("%p",p), on pourrait obtenir n’importe quoi ! En effet, p,
n’est qu’un pointeur, c’est-à-dire une variable contenant une valeur. Bien entendu, dans la pratique, cette
valeur n’est pas quelconque : elle désigne bien souvent l’adresse d’une variable.

Donc le plantage sera dû au fait que p pointe n’importe où et que vous essayez d’initialiser ce n’importe
où.
13.6. UN EXEMPLE POUR SE RAFRAÎCHIR LA MÉMOIRE SUR L’UTILITÉ DES POINTEURS125

C’est important d’avoir bien compris ceci...

13.6 Un exemple pour se rafraîchir la mémoire sur l’utilité des poin-


teurs

Vous savez qu’une fonction n’est pas capable de modifier ses arguments :

Exemple :

void calcule_double (int x){


x=x+x ;
}

main () {
int i =2;
calcule_double(i);
printf("i vaut à présent :%d",i); /* i vaut toujours 2 !!! */
}

La solution consiste à faire (voir le déroulé ci-dessous) :

void calcule_double (int * pointeur_int){


* pointeur_int = (* pointeur_int) + (* pointeur_int) ;
}

main () {
int i =2;
calcule_double(&i);
printf("i vaut à présent :%d",i); /* i vaut bien 4 !!! */
}

Imaginons que pointeur_int se trouve en mémoire logé à l’adresse 20 et i à l’adresse 10 :

Nom de la variable: i pointeur_int


-------------------------------
Contenu : | | | |
-------------------------------
Position en mémoire: 10 20

A présent, déroulons le programme principal :

1. int i =2 : déclaration et initialisation de i à 2

2. calcule_double(&i); : appel de la fonction calcule_double, avec la valeur 10 (l’adresse de i)


126 CHAPITRE 13. COMPLEMENTS

3. void calcule_double (int * pointeur_int) : On lance cette fonction et pointeur_int


vaut donc 10

i pointeur_int
------------------------------
| 2 | |10 |
------------------------------
10 20

4. * pointeur_int = (* pointeur_int) + (* pointeur_int) ; : (* pointeur_int) pointe


sur i en fait. L’ancienne valeur de i va être écrasée par la nouvelle valeur : 2+2

5. printf("i vaut à présent :%d",i); : on va bien se retrouver avec 4 comme valeur.

13.7 Un second exemple

Etudions le programme suivant dont le but est simplement de modifier la valeur de n :

#include <stdio.h>
void saisie (int *pointeur);

int main() {
int n;
saisie(&n);
printf("n vaut:%d",n);

return 0;
}

void saisie (int *pointeur) {


printf("Entrez un nombre:");
scanf ("%d",pointeur);
}

Imaginons que pointeur se trouve en mémoire logé à l’adresse 100 et n à l’adresse 1000 :
13.8. UN MOT SUR LES WARNINGS 127

pointeur n
------------------------------
| | | |
------------------------------
100 1000

A présent, déroulons le programme :

1. int n; déclaration de n : n vaut n’importe quoi vu qu’il n’a pas été initialisé !

2. saisie(&n); : appel de la fonction saisie, avec la valeur 1000 (l’adresse de n)

3. void saisie (int *pointeur) : pointeur vaut donc 1000

4. printf("Entrez un nombre:");

5. scanf (); : la fonction scanf va stocker ce que l’utilisateur tape au clavier à partir de l’adresse
mémoire 1000.

13.8 Un mot sur les warnings

Lorsque vous compilez vous pouvez obtenir des erreurs ou des warnings ou parfois les deux. Les erreurs,
vous savez ce que c’est par exemple lorsque vous écrivez floattt au lieu de float par exemple. Dans
ce cas le compilateur ne génèrera pas de fichier exécutable (par exemple, après avoir tapé gcc -o essai
essai.c, le fichier essai ne sera pas crée sur le disque dur). Pour les warnings, le fichier essai sera
crée, mais le compilateur a souhaité vous alerter sur une erreur possible.

Il est important de comprendre qu’en langage C, un warning équivaut à une "erreur larvée". Ainsi, vous
pouvez effectivement exécuter votre programme et ce malgré les warnings mais le problème c’est que vous
risquez de "payer" cela par la suite. Un exemple :
128 CHAPITRE 13. COMPLEMENTS

#include <stdio.h>
#include <stdlib.h>

/****************************************/
void Affiche_matrice(int matrice[9][9]); / *prototype */
/****************************************/
...

int main() {
int i;
int matrice[9][9];
...
Affiche_matrice(matrice[9][9]); /* ligne A */
}

...

/****************************************/
void Affiche_matrice(int matrice[9][9]) {
/****************************************/
int i,j;

for(i=1; i<=7; i++) {


for(j=1; j<=7; j++)
printf("|%d", matrice[i][j]);
printf("|\n");
}
}

La ligne A vous renverra un warning... Pourquoi ? Prenons le cas où la ligne A serait remplacée par :

Affiche_matrice(matrice[1][1]); /* ligne B */

Dès lors, on voit bien le problème, la ligne B ne passe pas tout le tableau matrice à la fonction
Affiche_matrice, mais uniquement la case [1][1]. La solution consisterait donc à écrire :

Affiche_matrice(matrice); /* ligne C */

Conclusion : considérez tous les warnings comme des erreurs et éliminez les (cela ne concerne cependant
pas les warnings provenant de l’usage de gets).
Chapitre 14

Code Ascii

Petit extrait de la table ASCII :

... ...
32: 33: ! 34: " 35: # 36: $ 37: % 38: & 39: ’
40: ( 41: ) 42: * 43: + 44: , 45: - 46: . 47: /
48: 0 49: 1 50: 2 51: 3 52: 4 53: 5 54: 6 55: 7
56: 8 57: 9 58: : 59: ; 60: < 61: = 62: > 63: ?
64: @ 65: A 66: B 67: C 68: D 69: E 70: F 71: G
72: H 73: I 74: J 75: K 76: L 77: M 78: N 79: O
80: P 81: Q 82: R 83: S 84: T 85: U 86: V 87: W
88: X 89: Y 90: Z 91: [ 92: \ 93: ] 94: ^ 95: _
96: ‘ 97: a 98: b 99: c 100: d 101: e 102: f 103: g
104: h 105: i 106: j 107: k 108: l 109: m 110: n 111: o
... ...

129
130 CHAPITRE 14. CODE ASCII
Chapitre 15

Annexes utiles

15.1 Les types numériques les plus utiles

Type de donnée Signification Taille (en octets) Plage de valeurs


char caractère 1 −128 à 127
int entier 4 −2147483648 à 2147483647
float flottant (réel) 4 3.4 ∗ 10−38 à 3.4 ∗ 1038
double flottant double 8 1.7 ∗ 10−4932 à 1.7 ∗ 104932

15.2 Les formats les plus utiles pour printf

Format Conversion en
%d int
%f float
%f double
%c char
%s char*

15.3 Les formats les plus utiles pour scanf

Format Conversion en
%d int
%f float
%lf double
%c char
%s char*

131
132 CHAPITRE 15. ANNEXES UTILES

15.4 Les 12 erreurs les plus classiques en Langage C

Erreur Version Correcte


if (i=0) if (i==0)
scanf ("%d",n); scanf ("%d",&n);
scanf ("%s",s) est équivalent à gets(s) permet de lire une
gets(s) phrase complète
if (a & b) if (a && b)
oublier d’initialiser une variable
tab[i,j] tab[i][j]
les bornes d’un tableau varient entre 1 et N les bornes d’un tableau varient entre 0 et N-1
char c ; char c ;
printf ("%s",c) ; printf ("%c",c) ;
char * s; char * s;
printf ("%c",c) ; printf ("%s",c) ;
char chaine[3]="12345"; char chaine[6]="12345";
ou mieux :
char chaine[]="12345";
char chaine[]=’12345’; char chaine[]="12345";
si vous utilisez des fonctions compiler le programme avec
mathématiques telles que sqrt... gcc -o essai essai.c -lm
Chapitre 16

Quelques exemples de programmes

Le but de ce chapitre est de vous montrer quelques problèmes et leur solution. Ainsi en lisant des pro-
grammes finis, on peut aussi apprendre pas mal de choses ;-)

16.1 Convertisseur francs/euros

Voici un programme qui réalise cette tâche :

#include <stdio.h>

int main () {
float francs;

francs=0;
while (francs<=10) {
printf("%.1f francs = %.2f euros\n",francs,francs/6.559);
francs=francs+0.5;
}

return 0;
}

L’exécution sera la suivante :

133
134 CHAPITRE 16. QUELQUES EXEMPLES DE PROGRAMMES

0.0 francs = 0.00 euros


0.5 francs = 0.08 euros
1.0 francs = 0.15 euros
1.5 francs = 0.23 euros
2.0 francs = 0.30 euros
2.5 francs = 0.38 euros
3.0 francs = 0.46 euros
3.5 francs = 0.53 euros
4.0 francs = 0.61 euros
4.5 francs = 0.69 euros
5.0 francs = 0.76 euros
5.5 francs = 0.84 euros
6.0 francs = 0.91 euros
6.5 francs = 0.99 euros
7.0 francs = 1.07 euros
7.5 francs = 1.14 euros
8.0 francs = 1.22 euros
8.5 francs = 1.30 euros
9.0 francs = 1.37 euros
9.5 francs = 1.45 euros
10.0 francs = 1.52 euros

16.2 Proportion de nombres pairs et impairs

Voici un programme dont le but est de générer 1000 nombres aléatoires et de compter combien son pairs.
en théorie, on devrait se rapprocher de 50 %
16.3. INVERSER LES ÉLÉMENTS D’UN TABLEAU 135

#include <stdio.h>
#include <stdlib.h> /* pour les valeurs aléatoires */
#include <time.h>

int main () {
int nb_hasard = 0;
int i;

float nb_pairs=0; /* compte le nombre de nombres pairs */


float nb_impairs=0; /* compte le nombre de nombre impairs */

srand (time (NULL));

i=1;
do {
nb_hasard = rand ();
if (nb_hasard % 2==0) /* c’est un nombre pair */
nb_pairs=nb_pairs+1;
else
nb_impairs=nb_impairs+1;

i++;
}
while (i<=1000);

printf("proportion de nombres pairs=%f",nb_pairs/1000);


printf("proportion de nombres impairs=%f",nb_impairs/1000);

return 0;
}

16.3 Inverser les éléments d’un tableau

Remarque préliminaire : les deux programmes suivants sont rigoureusement équivalents :

#include <stdio.h>

#define TAILLE 10

int main () {
int i,j;

for (i=0, j=TAILLE-1 ; i<j ; i++,j--)


printf("i=%d j=%d\n",i,j);

return 0;
}
136 CHAPITRE 16. QUELQUES EXEMPLES DE PROGRAMMES

#include <stdio.h>

#define TAILLE 10

int main () {
int i,j;

i=0;
j=TAILLE-1;

while (i<j) {
i++;
j--;
printf("i=%d j=%d\n",i,j);
}
return 0;
}

Voici un programme qui remplit un tableau de 10 cases par des valeurs saisies au clavier. Dans un se-
cond temps, le programme inverse tout les éléments du tableau. Ainsi, si au départ on avait les 10 valeurs
suivantes dans le tableau : 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, à l’arrivée, nous aurons le tableau
suivant : 10, 9, 8, 7, 6, 5, 4, 3, 2, 1. Idée : échanger les éléments du tableau à l’aide de
deux indices qui parcourent le tableau en commençant respectivement au début et à la fin du tableau et qui
se rencontrent en son milieu. Tandis que l’indice i va démarrer au début du tableau, l’indice j va démarrer
à la fin.
16.4. AFFICHAGE D’UNE TABLE DE MULTIPLICATION 137

#include <stdio.h>

#define TAILLE 10

int main() {
/* Déclarations */
int tab[TAILLE]; /* tableau donné */
int i,j; /* indices courants */
int aide; /* pour l’échange */

for (i=0; i<TAILLE; i++) {


printf("Elément %d : ", i+1);
scanf("%d", &tab[i]);
}
/* Affichage du contenu du tableau */
printf("Tableau donné : \n");
for (i=0; i<TAILLE; i++)
printf("%d ", tab[i]);
printf("\n");

/* Inverser le tableau */
for (i=0, j=TAILLE-1 ; i<j ; i++,j--) {
/* Echange de tab[i] et tab[j] */
aide = tab[i];
tab[i] = tab[j];
tab[j] = aide;
}
/* Affichage du tableau inversé */
printf("Tableau résultat :\n");
for (i=0; i<TAILLE; i++)
printf("%d ", tab[i]);
printf("\n");
return 0;
}

16.4 Affichage d’une table de multiplication

On voudrait obtenir le résultat suivant :


138 CHAPITRE 16. QUELQUES EXEMPLES DE PROGRAMMES

1 2 3 4 5 6 7 8 9 10
2 4 6 8 10 12 14 16 18 20
3 6 9 12 15 18 21 24 27 30
4 8 12 16 20 24 28 32 36 40
5 10 15 20 25 30 35 40 45 50
6 12 18 24 30 36 42 48 54 60
7 14 21 28 35 42 49 56 63 70
8 16 24 32 40 48 56 64 72 80
9 18 27 36 45 54 63 72 81 90
10 20 30 40 50 60 70 80 90 100

Voici le programme qui réalise cette tâche :

#include <stdio.h>

int main () {
int ligne, colonne;

for (ligne=1;ligne<=10;ligne++) {
for (colonne=1;colonne<=10;colonne++)
printf("%4d",ligne*colonne); /* affichage sur 4 caractères */
printf("\n");

return 0;
}

16.5 Quelques manipulations sur les tableaux à l’aide de fonctions

Voici un exemple de fonction qui renvoie le maximum d’un tableau qui lui est passé en paramètre. Voir le
commentaire en dessous.
16.6. EFFECTUER UN TRI 139

#include <stdio.h>
int Max_Tableau (int Tab[], int taille);

int main() {
int T1[] = {1,10,4,5,-7}, T2[] = {2,1,14,3} ;
printf("Maximum de T1 : %d\n", Max_Tableau(T1,5) );
printf("Maximum de T2 : %d\n", Max_Tableau(T2,4) );

return 0;
}

int Max_Tableau (int Tab [], int taille) {


int i, max;
for (i=1, max=Tab[0]; i< taille; i++)
if (max<Tab[i])
max=Tab[i];
return max;
}

Rappel : en fait, il n’y a que l’adresse de la 1ère case du tableau qui est passée en paramètre...

16.6 Effectuer un tri

Supposons que l’on dispose d’un tableau tab qui contient 10 valeurs. On souhaite trier ce tableau. Une
solution toute simple consite à faire un passage sur le tableau et comparer la case d’indice n avec celle
d’indice n+1. Si celle se trouvant à l’indice n contient une valeur plus grande que celle de l’indice n+1,
alors on inverse les 2 valeurs, et ainsi de suite. Voici un exemple sur un tableau de 10 cases :

Tableau initial :

Indice de la case : 0 1 2 3 4 5 6 7 8 9
Valeur stockée : 12 10 4 5 6 7 8 9 10 1

On teste la case d’indice 0 et la case d’indice 1, si besoin est, on permute :

0 1 2 3 4 5 6 7 8 9
10 12 4 5 6 7 8 9 10 1

On teste la case d’indice 1 et la case d’indice 2, si besoin est, on permute :

0 1 2 3 4 5 6 7 8 9
10 4 12 5 6 7 8 9 10 1

...

Au bout d’un parcours complet du tableau, on obtient :


140 CHAPITRE 16. QUELQUES EXEMPLES DE PROGRAMMES

0 1 2 3 4 5 6 7 8 9
10 4 5 6 7 8 9 10 1 12

On peut constater que le tableau est mieux trié, mais ça n’est pas encore parfait. Dans la pratique, pour
un tableau de n cases, si on fait n passages comme celui-ci, on peut montrer que le tableau sera trié à
l’arrivée...

Voici le programme qui réalise ce travail :

#include <stdio.h>

#define TAILLE 10

int main () {

int tab[TAILLE];
int i,j;

for (i=0; i<TAILLE; i++) {


printf("Veuillez entrer la %d valeur:",i);
scanf("%d",&tab[i]);
}

/* tri du tableau */
int temp;
for (i=0; i<TAILLE; i++)
for (j=0; j<TAILLE-1; j++)
if (tab[j]>tab[j+1]) { /* échange de valeurs */
temp=tab[j];
tab[j]=tab[j+1];
tab[j+1]=temp;
}

printf("Voici votre tableau trié:\n");


for (i=0; i<TAILLE; i++)
printf("%d ",tab[i]);

return 0;
}

Enfin, voici le même programme mais cette fois-ci avec des fonctions. Le principal intérêt des fonctions
est de rendre le programme plus clair et d’alléger le main. Enfin, les fonctions sont plus faciles à être
réutilisées par la suite :
16.6. EFFECTUER UN TRI 141

#include <stdio.h>

#define TAILLE 10

/*****************************/
void init_tableau (int tab[]);
void tri_tableau (int tab[]);
void affiche_tableau(int tab[]);
/*****************************/

int main () {
int tab[TAILLE];
int i,j;
int temp;

init_tableau(tab);
tri_tableau(tab);
affiche_tableau(tab);

return 0;
}

void init_tableau (int tab[]) {


int i;
for (i=0; i<TAILLE; i++) {
printf("Veuillez entrer la %d valeur:",i);
scanf("%d",&tab[i]);
}
}

void tri_tableau (int tab[]) {


int i,j;
int temp;

/* tri du tableau */
for (i=0; i<TAILLE; i++)
for (j=0; j<TAILLE-1; j++)
if (tab[j]>tab[j+1]) { /* échange de valeurs */
temp=tab[j];
tab[j]=tab[j+1];
tab[j+1]=temp;
}
}

void affiche_tableau(int tab[]) {


int i;
printf("Voici votre tableau trié:\n");
for (i=0; i<TAILLE; i++) {
printf("%d ",tab[i]);

}
}

Il est important d’avoir bien tout compris dans ce programme...


142 CHAPITRE 16. QUELQUES EXEMPLES DE PROGRAMMES

16.7 Le jeu de la vie

16.7.1 Historique

John Conway était un mathématicien de l’Université de Cambridge. Très prolifique en matière de jeux
mathématiques, il décrivit en 1970 le jeu de la vie, visant à modéliser d’une façon simple l’évolution
d’organismes vivants.

16.7.2 Règles du jeu

Le jeu de la vie évolue normalement sur un damier infini. Chaque case est occupée par une cellule qui peut
être vivante ou morte. À chaque génération, chaque cellule peut naître, mourir, ou rester dans son état. Les
règles qui permettent de passer d’une génération à l’autre sont précises et ont été choisies avec soin pour
que l’évolution des organismes soit intéressante et imprévisible. En premier lieu, notons que sur un damier
infini, chaque case a exactement 8 voisins. Les règles données par J. Conway sont les suivantes :
– Une cellule ayant exactement 2 ou 3 voisins vivants survit à la génération suivante.
– Une cellule ayant au moins 4 cellules voisines vivantes meurt d’étouffement à la génération suivante.
– Une cellule ayant au plus une cellule voisine vivante meurt d’isolement à la génération suivante.
– Sur une case vide ayant exactement 3 voisins vivants, une cellule naîtra à la génération suivante.
Notons que c’est l’ensemble de la génération actuelle qui doit être pris en compte pour l’établissement de
l’état des cellules à la génération suivante.

Voici un exemple de figure sur un petit damier. Les cellules qui devront mourir à la génération suivante
sont grisées :

Générations : 1 2 3 4 5

16.7.3 Exemples obtenus

Si on faisait une version graphique, voici ce que l’on pourrait obtenir :


16.7. LE JEU DE LA VIE 143

Configuration aléatoire de départ...

Formations d’amalgames de cellules... Configuration finale de motifs de période 1 ou 2...

Apparition d’un planeur... ...qui se balade.

16.7.4 Exemple de corrigé

De notre côté, afin de simplifier, nous n’allons considérer que 4 voisins pour une cellule : la cellule de
droite, celle de gauche, celle du dessous en enfin celle du dessus.

Par ailleurs, nous allons considérer que toutes les cellules sont stockées dans une matrice m de taille n ∗ n.
Pour une case m[i][j], on définit quatre voisins :
m[i-1][j], m[i+1][j], m[i][j-1] et m[i][j+1].

Enfin, on notera que le problème peut être simplifié en prenant une matrice (n + 2) ∗ (n + 2) dont les
contours contiennent des cellules mortes : en effet, comme les cellules mortes n’interviennent pas dans la
décision de modification de l’état d’une case, cette bordure rajoutée artificiellement permettra de ne pas
faire de traitement particulier pour les cases de bordure ou pour les cases d’angle de la matrice n ∗ n, qui
sans cela ne posséderaient que deux ou trois voisins chacune.

On se propose donc de découper le programme comme ceci (lisez ce programme, qui n’est pas encore
complet, ainsi que les explications ci-dessous) :
144 CHAPITRE 16. QUELQUES EXEMPLES DE PROGRAMMES

/***************************************/
/* JEU DE LA VIE */
/***************************************/

#include <stdio.h>
#include <stdlib.h>
#define TAILLE_SOUS_MATRICE 7
#define TAILLE_SUR_MATRICE 9
/* Taille de la matrice contenant */
/* les cellules + 2 pour la bordure */

/****************************************/
/******* P R O T O T Y P E S ********/
/****************************************/

/* Initialisation de la matrice de départ */


void init(int matrice [][TAILLE_SUR_MATRICE ]);

/* Indique pour la cellule de coordonnées (ligne,colonne) */


/* le nombre de cellules voisines vivantes */
int nombre_voisins (int matrice [][TAILLE_SUR_MATRICE], int ligne, int colonne);

/* Réalise une étape de plus dans la mise à jour de la matrice: */


/* autrement dit, on réalise un cycle de vie */
void mise_a_jour(int matrice[][TAILLE_SUR_MATRICE]);

/* Affichage de la matrice en cours */


void affiche_matrice(int matrice [][TAILLE_SUR_MATRICE]);
/****************************************/
/******* F O N C T I O N S **********/
/****************************************/

int main() {
int i;
int nbr_cycles;
int matrice[TAILLE_SUR_MATRICE] [TAILLE_SUR_MATRICE ];

void init(int matrice [][TAILLE_SUR_MATRICE]) { ... }

void mise_a_jour(int matrice[][TAILLE_SUR_MATRICE]) {


int matrice_densite[TAILLE_SOUS_MATRICE][ TAILLE_SOUS_MATRICE];
...
}

Lisons le prototype de la fonction init :


16.7. LE JEU DE LA VIE 145

void init(int matrice[] [TAILLE_SUR_MATRICE]);

nous laissons de côté, pour l’instant le fait que nous n’avons pas écrit à la place :

void init(int matrice[TAILLE_SUR_MATRICE] [TAILLE_SUR_MATRICE]);

on pourrait se dire que la fonction reçoit la matrice T AILLE_SU R_M AT RICE∗T AILLE_SU R_M AT RICE
en entrée et qu’à la sortie elle ne sera pas modifiée. Dès lors, dans la fonction Init, l’instruction matrice[i][j]
= ... serait vaine. En fait ce qui est passé à la fonction Init n’est autre que l’adresse mémoire de la
matrice, en gros &matrice[0][0]. Dès lors la fonction ne pourra effectivement pas modifier l’adresse
de matrice[0][0] en revanche, elle pourra modifier matrice[0][0], matrice[1][0]...

Avant de voir la correction complète de cet exercice, on notera que lors d’un cycle de vie, on ne doit pas
modifier la matrice de vie courante au fur et à mesure, elle doit être modifiée d’un coup. Il est donc néces-
saire de passer par 2 étapes :
1˚) construire une matrice intermédiaire qui contient le nombre de voisins pour chaque cellule.
2˚) parcourir cette matrice en une passe et modifier la matrice contenant les cellules vivantes.

Voici donc le programme complet :

/***************************************
/* JEU DE LA VIE
/***************************************/

#include <stdio.h>
#include <stdlib.h>
#define TAILLE_SOUS_MATRICE 7
/* On peut avoir 7 * 7 cellules vivantes */
#define TAILLE_SUR_MATRICE 9
/* On place une bordure autour qui facilite la " vie " */
/* au programmeur... */

/***************** PROTOTYPES :***********************/


void init(int matrice [][TAILLE_SUR_MATRICE ]);
int nombre_voisins (int matrice [][TAILLE_SUR_MATRICE ],
int ligne, int colonne);
void mise_a_jour(int matrice[][TAILLE_SUR_MATRICE ]);
void affiche_matrice(int matrice [][TAILLE_SUR_MATRICE ]);
void ligne(int largeur);
/****************************************/
146 CHAPITRE 16. QUELQUES EXEMPLES DE PROGRAMMES

int main( ) {
int i;
int nbr_cycles;
int matrice[TAILLE_SUR_MATRICE] [TAILLE_SUR_MATRICE ];
char s[2];

printf("Nombre de cycles : ");


scanf("%i", &nbr_cycles);

init(matrice);
printf("La population au départ : \n");
affiche_matrice(matrice);
printf("Pressez sur ENTER pour continuer...\n");
gets(s);

for(i=0; i<nbr_cycles; i++) {


mise_a_jour (matrice);
printf("La population après %d cycles : \n", i+1);
affiche_matrice (matrice);

printf("Pressez sur ENTER pour continuer...\n");


gets(s);
}
return 0;
}

/****************************************/
/* Initialisation de la matrice */
void init(int matrice [][TAILLE_SUR_MATRICE ]) {
/****************************************/
int i,j;

for(i=0; i<TAILLE_SUR_MATRICE ; i++)


for(j=0; j<TAILLE_SUR_MATRICE ; j++)
if (i<=j && i>0 && j<=7)
matrice[i][j]=1;
else
matrice[i][j]=0;
}

/****************************************/
/* Calcul du nombre de voisins vivants */
int nombre_voisins (int matrice[][TAILLE_SUR_MATRICE ],
int ligne, int colonne) {
/****************************************/
return matrice[ligne-1][colonne] +
matrice[ligne+1][colonne]+
matrice[ligne][colonne-1] +
matrice[ligne][colonne+1];
}
16.7. LE JEU DE LA VIE 147

/****************************************/
/* Correspond à l’étape n+1 */
void mise_a_jour(int matrice[ ][TAILLE_SUR_MATRICE ]) {
/****************************************/
int i,j;
int nbr_voisins;
int matrice_densite[TAILLE_SOUS_MATRICE][ TAILLE_SOUS_MATRICE];
/* matrice qui comptabilise le nombre de voisins */
/* et cela, case par case */

for(i=0; i< TAILLE_SOUS_MATRICE; i++)


for(j=0; j< TAILLE_SOUS_MATRICE; j++)
matrice_densite[i][j]=nombre_voisins(matrice,i+1,j+1);
/* i+1 et j+1 car on passe de la SOUS_MATRICE à la MATRICE */

for(i=0; i< TAILLE_SOUS_MATRICE; i++)


for(j=0; j< TAILLE_SOUS_MATRICE; j++) {
nbr_voisins=matrice_densite[i][j];
if(nbr_voisins==2)
matrice[i+1][j+1]=1;
else if (nbr_voisins==0 || nbr_voisins==4)
matrice[i+1][j+1]=0;
}
}

/****************************************/ /* Affichage à l’écran


des cellules vivantes */ void affiche_matrice(int matrice[
][TAILLE_SUR_MATRICE ]) { /****************************************/
int i,j;

for(i=1; i<=TAILLE_SOUS_MATRICE; i++) {


ligne(7);
for(j=1; j<= TAILLE_SOUS_MATRICE; j++)
if (matrice[i][j]==1)
printf("|%c",’*’);
else
printf("%c",’|’);
printf("|\n");
}
ligne(TAILLE_SOUS_MATRICE);
}

/****************************************/
/* Tracé d’une ligne */
void ligne(int largeur) {
/****************************************/
int i;
for(i=0; i<largeur; i++)
printf("+-");

printf("+\n");
}
148 CHAPITRE 16. QUELQUES EXEMPLES DE PROGRAMMES
Chapitre 17

En 2ème lecture...

17.1 Quelques fonctions mathématiques

Pensez à inclure la bibliothèque math.h et compilez en tapant : gcc -o essai essai.c - lm

Fonction Explication Exemple


exp(x) fonction exponentielle y=exp(x)
log(x) logarithme naturel y=log(x)
log10(x) logarithme à base 10 y=log10(x)
pow(x,y) xy z=pow(x,y)
sqrt(x) racine carrée de x y=sqrt(x)
fabs(x) valeur absolue y=fabs(x)
floor(x) arrondir en moins floor(1.9) renverra 1
ceil(x) arrondir en plus ceil(1.4) renverra 2
sin(x) sinus de x y=sin(x)
cos(x) cosinus de x y=cos(x)
tan(x) tangente de x y=tan(x)

17.2 Pointeurs et structures

Voici un programme qui utilise un pointeur sur une structure :

149
150 CHAPITRE 17. EN 2ÈME LECTURE...

#include <stdio.h>

typedef struct {
char nom [40];
char prenom [20];
int age;
} personne;

int main () {
personne p;
personne * pointeur_sur_une_personne;
printf("Veuillez entrer le nom de la personne:");
scanf("%s",p.nom);

printf("Veuillez entrer le prénom de la personne:");


scanf("%s",p.prenom);

printf("Veuillez entrer l’age de la personne:");


scanf("%d",&p.age); /* ne pas oublier le ’&’ !!! */

pointeur_sur_une_personne=&p;
printf("Voici les caractéristiques de cette personne:\n");
printf("nom=%s\n",(*pointeur_sur_une_personne).nom);

/* autre façon de l’écrire */


printf("nom=%s\n",pointeur_sur_une_personne->nom);

return 0;
}

On notera que cette seconde écriture (plus pratique à l’usage) repose sur une flèche qui est construite avec
le signe moins (-) et le signe supérieur (>).

17.3 En finir avec les warnings du gets

En première lecture, on pourrait dire que ce type de warning est " normal ". Prenons un exemple :

char chaine[10];
gets(chaine) ;

Supposons que votre programme soit utilisé sur internet et qu’un utilisateur malveillant entre une chaîne
de caractères plus grande que 10 caractères, par exemple : "ABCDEFGHIJKLMONPQR". Dans ce cas,
à l’exécution, votre programme va recopier à partir de l’adresse de la variable chaine les caractères A,
B,.... Dès lors, les zones mémoires qui suivent la variable chaine seront écrasées. Ceci explique que le
compilateur vous indique un warning...

Pour y remédier, vous pouvez utiliser :


17.4. STDOUT 151

char buffer[128];
fgets(buffer, 128, stdin);

en sachant que stdin est synonyme du clavier : standardinput.

Remarque : fgets présente un petit défaut, elle vous place un \n à la fin de la chaîne qui a été lue.

17.4 Stdout

On vient de voir que stdin était synomyme du clavier : standardinput.

Il faut savoir que stdout désigne la même chose que l’écran : standardoutput.

ainsi les deux programmes suivants sont équivalents :

int i=123;
printf("%d",i);

int i=123;
fprintf(stdout,"%d",i);

17.5 Utilité des prototypes

Rappelez-vous qu’il est plus prudent de placer tous les prototypes de fonctions que vous appelez juste
en dessous de la suite des #include. Un prototype représente l’entête d’une fonction ; exemple (ligne
ci-dessous) :

#include <stdio.h>

float mystere (int i); // prototype de la fonction mystere (ligne A)

int main () {
int j;
printf("Entrez une valeur:");
scanf("%d",&j);
printf("Résultat de la fonction mystere:%f\n",mystere(j)); // (ligne B)
}

float mystere (int i) {


return i*i;
}
152 CHAPITRE 17. EN 2ÈME LECTURE...

Si vous omettez le prototype comme dans le programme ci-dessous :

#include <stdio.h>

int main () {
int j;
printf("Entrez une valeur:");
scanf("%d",&j);
printf("Résultat de la fonction mystere:%f\n",mystere(j)); // (ligne B)
}

float mystere (int i) {


return i*i;
}

au moment où le programme rencontre l’appel à la fonction mystere (ligne B), le compilateur rencontre
pour la première fois cette fonction. Or il ne sait pas ce que fait cette fonction et a fortiori il ignore le type
du résultat qui sera renvoyé. Dans ce cas, le compilateur suppose à tort que ce résultat sera du type int
(qui est le type par défaut) ce qui risque de poser des problèmes dans la suite quand il s’apercevra qu’en
fait, la fonction renvoyait un float.

Ce type par défaut explique pourquoi on peut se permettre d’écrire un main comme ceci :

main () {
...
}

plutôt que comme cela :

int main () {
...
}

17.6 Opérateur ternaire

Le langage C possède une paire d’opérateurs un peu exotiques qui peut être utilisée comme alternative
à if - else et qui a l’avantage de pouvoir être intégrée dans une expression. L’expression suivante :
<expr1> ? <expr2> : <expr3> est traduite comme ceci :

* Si <expr1> fournit une valeur différente de zéro, alors la valeur de <expr2> est fournie comme résul-
tat.
17.7. CHOIX MULTIPLE 153

* Si <expr1> fournit la valeur zéro, alors la valeur de <expr3> est fournie comme résultat.

Exemple : la suite d’instructions :

if (a>b)
maximum=a;
else
maximum=b;

peut être remplacée par :

maximum = (a > b) ? a : b;

Employé de façon irréfléchi, l’opérateur ternaire peut nuire à la lisibilité d’un programme, mais si on
l’utilise avec précaution, il fournit des solutions très élégantes.

Exemple

printf("Vous avez %i carte%c \n", n, (n==1) ? ’ ’ : ’s’);

17.7 Choix multiple

17.8 Tableaux de chaînes de caractères

La déclaration char JOUR[7][9] réserve l’espace mémoire pour 7 mots contenant 9 caractères (dont
8 caractères significatifs, c’est à dire sans \0), voir figure.
154 CHAPITRE 17. EN 2ÈME LECTURE...

Par ailleurs, lors de la déclaration, il est possible d’initialiser toutes les cases du tableau par des chaînes de
caractères constantes :

char JOUR[7][9]={"lundi","mardi","mercredi","jeudi",
"vendredi","samedi","dimanche"};

Voir figure :
17.8. TABLEAUX DE CHAÎNES DE CARACTÈRES 155

Il est possible d’accéder aux différentes chaînes de caractères d’un tableau, en indiquant simplement la
ligne correspondante.

Exemple : l’exécution des trois instructions suivantes :

char JOUR[7][9]= {"lundi", "mardi", "mercredi",


"jeudi", "vendredi",
"samedi", "dimanche"};
int i = 2;
printf("Aujourd’hui, c’est %s !\n", JOUR[i]);

affichera la phrase : Aujourd’hui, c’est mercredi ! Des expressions comme JOUR[i] repré-
sentent l’adresse du premier élément d’une chaîne de caractères.

Cette dernière remarque est très pratique pour résoudre l’exercice suivant :

Exercice : on va écrire un programme qui lit un verbe régulier en "er" au clavier et qui affiche la conjugaison
au présent de l’indicatif de ce verbe. Contrôlez s’il s’agit bien d’un verbe en "er" avant de conjuguer.

Exemple :

Verbe : fêter
je fête
tu fêtes
il fête
nous fêtons
vous fêtez
ils fêtent

Vous utilisez deux tableaux de chaînes de caractères : SUJ pour les sujets et TERM pour les terminaisons.

Solution possible :
156 CHAPITRE 17. EN 2ÈME LECTURE...

#include <stdio.h>
#include <string.h>
int main() {
/* Déclarations */
/* Sujets et terminaisons */
char SUJ[6][5] = {"je","tu","il","nous","vous","ils"};
char TERM[6][5] = {"e","es","e","ons","ez","ent"};
char verbe[20]; /* chaîne contenant le verbe */
int longueur; /* longueur de la chaîne */
int i; /* indice courant */

/* Saisie des données */


printf("Verbe : ");
scanf("%s", verbe);

/* Contrôler s’il s’agit d’un verbe en ’er’ */


longueur=strlen(verbe);
if ((verbe[longueur-2] != ’e’) || (verbe[longueur-1] != ’r’))
printf("\"%s\" n’est pas un verbe du
premier groupe.\n",verbe);
else {
/* Couper la terminaison ’er’. */
verbe[longueur-2]=’\0’;
/* Conjuguer ... */
for (i=0; i<6; i++)
printf("%s %s%s\n",SUJ[i], verbe, TERM[i]);
}
return 0;
}

17.9 Pointeurs et tableaux

Nous allons à présent examiner les liens très étroits qu’il y a entre pointeurs et tableaux. En fait, on va
voir qu’à chaque fois que vous manipulez des tableaux, comme par exemple à la ligne 1 du programme
ci-dessous :

int tab[10]={1,2,3,4,5,6,7,8,9,10};

int i;
for (i=0;i<10;i++)
printf("%d ",tab[i]); /* ligne 1 */

le langage C, va transformer vote tab[i] sous forme de pointeurs... Comme nous l’avons déjà constaté
précédemment, le nom d’un tableau représente l’adresse de son premier élément. En d’autre termes :
&tab[0] et tab sont une seule et même adresse.
17.9. POINTEURS ET TABLEAUX 157

En simplifiant, nous pouvons retenir que le nom d’un tableau est un pointeur constant sur le premier élément
du tableau. Exemple : en déclarant un tableau tab de type int et un pointeur p sur int,

int tab[10];
int *p;

l’instruction : p = tab; est équivalente à p = &tab[0];

Si p pointe sur une case quelconque d’un tableau, alors p+1 pointe sur la case suivante.

Ainsi, après l’instruction, p = tab;

le pointeur p pointe sur tab[0],

*(p+1) désigne le contenu de tab[1]


*(p+2) désigne le contenu de tab[2]
... ...
* (p+i) désigne le contenu de tab[i]

Au premier coup d’oeil, il est bien surprenant que p+i n’adresse pas le i-ième octet derrière p, mais la
i-ième case derrière p ...

Ceci s’explique par la stratégie de programmation ’défensive’ des créateurs du langage C : si on travaille
avec des pointeurs, les erreurs les plus perfides sont causées par des pointeurs mal placés et des adresses
mal calculées. En C, le compilateur peut calculer automatiquement l’adresse de l’élément p+i en ajoutant
à p la taille d’une case multipliée par i. Ceci est possible, parce que :
- chaque pointeur est limité à un seul type de données, et
- le compilateur connaît le nombre d’octets des différents types.

Enfin, comme tab représente l’adresse de tab[0] :

*(tab+1) désigne le contenu de tab[1]


*(tab+2) désigne le contenu de tab[2]
... ...
*(tab+i) désigne le contenu de tab[i]

Voici un récapitulatif de tout ceci. Soit un tableau tab d’un type quelconque et i un indice entier alors :

tab désigne l’adresse de tab[0]


tab+i désigne l’adresse de tab[i]
*(tab+i) désigne le contenu de tab[i]

Si p = tab, alors :

p désigne l’adresse de tab[0]


p+i désigne l’adresse de tab[i]
*(p+i) désigne le contenu de tab[i]
158 CHAPITRE 17. EN 2ÈME LECTURE...

Exemple : les deux programmes suivants copient les éléments positifs d’un tableau tab dans un deuxième
tableau positifs.

Formalisme tableau :

int main() {
int tab[10] = {-3, 4, 0, -7, 3, 8, 0, -1, 4, -9};
int positifs[10];
int i,j; /* indices courants dans tab et positifs */
for (i=0,j=0 ; i<10 ; i++)
if (tab[i]>0){
positifs[j] = tab[i];
j++;
}
return 0;
}

Nous pouvons remplacer systématiquement la notation tableau[i] par *(tableau+i), ce qui conduit
à ce programme :

Formalisme pointeur

int main() {
int tab[10] = {-3, 4, 0, -7, 3, 8, 0, -1, 4, -9};
int positifs[10];
int i,j; /* indices courants dans tab et positifs */
for (i=0,j=0 ; i<10 ; i++)
if (*(tab+i)>0){
*(positifs+j) = *(tab+i);
j++;
}
return 0;
}

Voici un exemple de programme qui range les éléments d’un tableau tab dans l’ordre inverse. Le pro-
gramme utilise des pointeurs p1 et p2 et une variable numérique aide pour la permutation des éléments :
17.10. TABLEAUX DE POINTEURS 159

#include <stdio.h>
int main() {
/* Déclarations */
int tab[50]; /* tableau donné */
int N; /* dimension du tableau * /
int aide; /* pour la permutation */
int *p1, *p2; /* pointeurs d’aide */
int i;
/* Saisie des données */
printf("Dimension du tableau (max.50) : ");
scanf("%d", &N );

i=1;
for (p1=tab; p1<tab+N; p1++) {
printf("Elément %d : ", i++);
scanf("%d", p1);
}
/* Affichage du tableau */
for (p1=tab; p1<tab+N; p1++)
printf("%d ", *p1);
printf("\n");
/* Inverser la tableau */
for (p1=tab,p2=tab+(N-1); p1<p2; p1++,p2--) {
aide = *p1;
*p1 = *p2;
*p2 = aide;
}
/* Edition du résultat */
for (p1=tab; p1<tab+N; p1++)
printf("%d ", *p1);
printf("\n");

return 0;
}

17.10 Tableaux de pointeurs

Un peu de théorie...

Si nous avons besoin d’un ensemble de pointeurs du même type, nous pouvons les réunir dans un tableau
de pointeurs. Déclaration d’un tableau de pointeurs :

<Type> *<NomTableau>[<N>]

déclare un tableau <NomTableau> de <N> pointeurs sur des données du type <Type>.

Exemple :
double * a[10];
160 CHAPITRE 17. EN 2ÈME LECTURE...

1˚) les crochets [ ] ont une prorité supérieure à l’étoile *


2˚) en lisant de droite à gauche on voit que a[10] sera du type double *
3˚) on déclare donc un tableau de 10 pointeurs sur des double.

Un peu de pratique...

char *JOUR[] = {"dimanche", "lundi", "mardi",


"mercredi", "jeudi", "vendredi",
"samedi"};

déclare un tableau JOUR[] de 7 pointeurs sur char. Chacun des pointeurs est initialisé avec l’adresse de
l’une des 7 chaînes de caractères (voir figure) :

On peut afficher les 7 chaînes de caractères en fournissant les adresses contenues dans le tableau JOUR à
printf :

int i;
for (i=0; i<7; i++)
printf("%s\n", JOUR[i]);
17.11. SWITCH 161

17.11 switch

Si vous avez une cascade de if et else imbriqués à écrire :

int i;
printf ("Entrer une valeur:");
scanf("%d",&i);

if (i==0)
printf ("Nombre nul\n");
else
if (i==1)
printf ("Nombre non égal à un\n");
else
printf ("Autre type de nombre\n");

vous pouvez utiliser un switch :

int i;
printf ("Entrer une valeur:");
scanf("%d",&i);

switch (i) {
case 0: printf ("Nombre nul\n");
break;

case 1: printf ("Nombre non égal à un\n");


break;

defaut: printf("Autre type de nombre\n");


break;
}

Remarquez l’usage du break, en effet, si vous ne faites pas de break, le programme (après être entré
dans un case continuera sur le case suivant et ainsi de suite). Exemple : vous testez le programme suivant
en entrant la valeur 0 :
162 CHAPITRE 17. EN 2ÈME LECTURE...

int i;
printf ("Entrer une valeur:");
scanf("%d",&i);

switch (i) {
case 0: printf ("Nombre nul\n"); /* pas de break */

case 1: printf ("Nombre non égal à un\n");


break;

defaut: printf("Autre type de nombre\n");


break;
}

Le programme affichera à l’exécution :

Nombre nul
Nombre non égal à un

17.12 Edition de liens

Dans la pratique, lorsque vous faites une compilation :


gcc -o essai essai.c

il y a différentes étapes qui sont réalisées par le compilateur sans que vous vous en rendiez compte. Ainsi
en théorie, il faudrait tout d’abord compiler votre programme comme suit :
gcc -c essai.c

Cette étape a pour effet de générer un fichier essai.o qui ne contient pas encore toutes les fonctions
d’entrées sorties telles que printf, scanf, feof... On appelle ce fichier, le fichier objet.

Ensuite, il faudrait taper :


gcc -o moyenne moyenne.o

à présent, les fonctions printf, scanf, feof... cette fois sont présentes dans le fichier. Cela fait
partie du travail de l’éditeur de liens d’aller chercher, dans les différentes bibliothèques système, les fonc-
tions nécessaires à l’exécution de l’application, et de les incoporer au fichier exécutable.

Bien entendu, c’est plus rapide de taper directement :


gcc -o essai essai.c

Cela présente toutefois un intérêt ainsi que nous allons le voir juste après le paragraphe suivant...
17.13. LA COMPILATION CONDITIONNELLE 163

17.13 La compilation conditionnelle

La notion de compilation conditionnelle va nous permettre d’inclure ou non des lignes de code en fonction
d’une condition. Ces commandes sont utilisées de la manière suivante :

#if expression_1
groupe_de_lignes_1
#else
groupe_de_lignes_2
#endif

On notera que expression_1 doit être une constante arithmétique. Si cette dernière n’est pas nulle, alors le
groupe de ligne 1 est analysé et le groupe 2 est ignoré. Dans le cas contraire où la valeur de l’expression
est nulle, c’est le groupe 1 qui est ignoré et le groupe 2 qui est analysé.

Les commandes #if def et #if ndef peuvent être utilisées pour tester si un nom est défini ou non comme
macro du préprocesseur. En particulier, on utilise la commande #if ndef dans les fichiers d’entête de la
manière suivante :

/* fichier "entete.h" */

#ifndef ENTETE_H
#define ENTETE_H 1
int fonction1 ();
int fonction2 ();
#define TAILLE_MAX 100
...
#endif

Cette astuce permet de ne pas inclure deux fois les mêmes déclarations. En effet, si l’on inclut pour la
première fois le fichier ”entete.h”, la macro ENTETE_H n’étant pas définie, les lignes seront analysées
et f onction1, f onction2 et TAILLE_MAX seront déclarées. Par le jeu des inclusions imbriquées, si l’on
inclut une deuxième fois cet même fichier ”entete.h”, la macro étant cette fois-ci définie, les lignes seront
ignorées.
Cette astuce doit être utilisée systématiquement pour éviter deux fois l’inclusion d’un même fichier. Ceci
provient du fait qu’il n’est pas possible de faire deux fois la même déclaration.

Nous allons pratiquer sur un exemple tout simple mais la philosophie est là : créer des bibliothèques de
fonctions.

17.14 La compilation séparée

Nous allons créer une toute petite bibliothèque qui permet de calculer le minimum de 2 nombres. Créez les
3 fichiers suivants :
164 CHAPITRE 17. EN 2ÈME LECTURE...

/* fichier "minimum.c" */

#include "minimum.h"
int minimum (int a, int b) {
if (a > b)
return (b);
else
return (a);
}

/* fichier "minimum.h" */

#ifndef MINIMUM_H
#define MINIMUM_H 1

/* on déclare un prototype de fonction qui prend 2 entiers


en paramètres : */
int minimum (int, int);

#endif

/* fichier "essai.c" */

#include <stdio.h>
#include "minimum.h"
int main () {
int a=13;
int b=15;
int c=minimum(a,b);
printf ("Minimum entre %d et %d : %d",a,b,c);

return 0;
}

La compilation se fera de la manière suivante :

gcc -c minimum.c
gcc -c essai.c
gcc -o essai minimum.o essai.o

Il ne restera plus qu’à lancer le programme de la manière classique :

./essai
Chapitre 18

Partie optionnelle : exercices


supplémentaires...

Cette partie est optionnelle, elle ne vise qu’à occuper ;-) ceux qui auraient déjà fini au bout d’un temps bien
inférieur au temps alloué pour cette remise à niveau...

Cette partie devra donc être réalisée une fois que vous aurez fait la partie UNIX...

18.1 Balle rebondissante

On souhaite réaliser un programme qui permettrait de faire rebondir une balle à l’écran. La balle devrait
pouvoir rebondir sur un écran de la forme suivante (figure de gauche) :

0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 * * * * * * * * * *
* *
* *
* *
O * O *
* *
* *
* *
* *
* * * * * * * * * *

165
166 CHAPITRE 18. PARTIE OPTIONNELLE : EXERCICES SUPPLÉMENTAIRES...

Afin de faciliter la programmation, on rajoute artificiellement une couronne d’étoiles qui permettra de
simplifier les tests lorsque la balle arrivera sur un bord (figure de droite).

La balle, qui est symbolisée par le caractère ’O’ démarre en [4; 4]. Elle part suivant le vecteur d’abscisse +1
et d’ordonnée +1. Lorsqu’elle arrive sur un bord en X, il suffit d’inverser le sens de déplacement suivant
cet axe, idem pour l’axe des ordonnées Y . Astuce pour tester si on arrive sur un bord, faire :
if (grille[nouvelle_position_en_y][ nouvelle_position_en_x]==’*’)

Il vous reste donc à compléter le programme suivant que vous pouvez récupérer sur le campus numérique
(htpp://campus.eseo.fr) ... Demandez comment faire si vous n’y parvenez pas...

#include <stdio.h>

/****************** PROTOTYPES DES FONCTIONS ***************************/


/* Initialise la grille de façon à ce qu’elle contienne ce qu’il y a à la
figure de droite */

void init_grille (char grille[][10], int pos_balle_x, int pos_balle_y) ;

/* Affiche le rectangle d’étoiles et la balle (tout ceci en même


temps et non pas le rectangle puis la balle...) */

void affiche_grille (char grille[][10]); /* 10 lignes 10 colonnes */

/* calcule la nouvelle position de la balle en fonction de l’ancienne position


de la balle (old_pos_balle_x, old_pos_balle_y) et du vecteur de déplacement
(deplacement_x, deplacement_y). Cette fonction modifie en conséquence
les paramètres : int *p_new_pos_balle_x, int *p_new_pos_balle_y.
Enfin, la fonction gère aussi le cas où la balle pourrait atterrir
sur la couronne formée d’étoiles. */

void calcule_position_balle (char grille[][10],


int old_pos_balle_x, int old_pos_balle_y, int deplacement_x,
int deplacement_y, int *p_new_pos_balle_x, int *p_new_pos_balle_y) ;

/* la balle se déplace d’une case, il faut modifier la grille en conséquence :


effacer l’ancienne position de la balle et placer la balle à sa nouvelle
position. */
void modifie_grille (char grille[][10], int old_pos_balle_x,
int old_pos_balle_y, int new_pos_balle_x, int new_pos_balle_y);
18.1. BALLE REBONDISSANTE 167

void init_grille (char grille[][10], int pos_balle_x,int pos_balle_y) {...}

void affiche_grille (char grille[][10]) {...}

void calcule_position_balle (char grille[][10], int old_pos_balle_x,


int old_pos_balle_y, int deplacement_x, int deplacement_y,
int *p_new_pos_balle_x, int *p_new_pos_balle_y) {...}

void modifie_grille ( char grille[][10], int old_pos_balle_x, int


old_pos_balle_y,int new_pos_balle_x, int new_pos_balle_y) {...}

int main () {

int pos_balle_x=4, pos_balle_y=4; /* position de la balle au départ */


int deplacement_x=1, deplacement_y=1; /* déplacement de la balle suivant */
/* le vecteur (1,1) */

char grille[10][10] ; /* grille qui peut contenir 3 types de caractères : */


/* ’*’ ou ’O’ ou le caractère espace ’ ’ */

init_grille (grille,pos_balle_x,pos_balle_y) ;
while (1) {
/* system("clear"); */ /* <- enlever ce commentaire une fois que le
programme fonctionnera... */
affiche_grille(grille);

int new_pos_balle_x ; /* pour calculer les nouvelles */


int new_pos_balle_y ; /* coordonnées de la balle */

calcule_position_balle (grille,pos_balle_x,pos_balle_y,
deplacement_x,deplacement_y,&new_pos_balle_x,&new_pos_balle_y) ;

modifie_grille (grille,
pos_balle_x, pos_balle_y,new_pos_balle_x, new_pos_balle_y);

deplacement_x=new_pos_balle_x-pos_balle_x ;
deplacement_y=new_pos_balle_y-pos_balle_y ;

pos_balle_x= new_pos_balle_x ;
pos_balle_y= new_pos_balle_y ;

usleep(500000) ; /* Pause de 500 000 micro secondes donc 1/2 seconde */


}
}
168 CHAPITRE 18. PARTIE OPTIONNELLE : EXERCICES SUPPLÉMENTAIRES...

18.2 Le morpion

18.2.1 Le morpion de base

Il s’agit du morpion classique que l’on pourrait tracer comme ceci dans l’immédiat :

X O .
X O X
O . O

Vous jouez contre l’ordinateur...

Voici quelques suggestions pour démarrer :

#include <stdio.h>
#include <string.h>

/* Initialisation du damier 3*3 avec le caractère ’ ’ */


void initialiser_damier(char damier[][3]) {
int i,j;
for (i=0; i<3; i++)
for (j=0;j<3;j++)
damier[i][j]=’ ’;
}

/* Affichage du damier à l’écran*/


void affiche_damier(char damier[][3]) {
...
}

/* fonction qui réalise le jeu proprement dit */


void jouer(char damier[][3]) {
...
}
18.2. LE MORPION 169

/* teste si un joueur a gagné */

void fin_de_partie(char damier[][3]) {


int gagne; /* vaudra 1 si le joueur 1 a gagné */
/* vaudra 2 si le joueur 2 a gagné */
/* test en colonne */
...

/* test en ligne */
...

/* test des diagonales */


...
if (gagne==1) {
printf("le joueur 1 a gagné !!!");
exit(1);
}
if (gagne==2) {
printf("le joueur 2 a gagné !!!");
exit(1);
}
}

main() {
int joueur=1; /* vaut 1 ou 2 suivant le joueur */
/* qui est entrain de jouer */
char damier[3][3];
system("clear"); /* clear sous UNIX: efface l’écran */
/* sytem appelle donc UNIX pour */
/* lancer la commande "clear" */
initialiser_damier(damier);
jouer(damier);
}

18.2.2 Améliorations possibles

Libre à vous d’ajouter ce que vous voulez. Voici quelques suggestions, au choix :

– pouvoir sauvegarder/recharger une partie en cours


– gérer les meilleurs scores au moyen d’un fichier
– dessiner un joli morpion, mettre un peu de couleur (voir à ce propos sur le campus numérique de l’ESEO
le mini-projet de vos collègues de I1, partie "Comment mettre de la couleur ?")
– autres idées... si vous en avez d’autres... appel à l’imagination...
170 CHAPITRE 18. PARTIE OPTIONNELLE : EXERCICES SUPPLÉMENTAIRES...

18.3 Le pendu

18.3.1 Le pendu de base

" Vous jouez contre l’ordinateur. " Ce dernier vous propose un mot à deviner en affichant la première et
la dernière lettre du mot à deviner (exemple pour le mot "bonjour", il afficherait : b - - - - - r). " Vous
proposez une lettre, si cette dernière se trouve dans le mot, l’ordinateur affiche le mot avec l’emplacement
où se trouve votre lettre. Exemple : vous proposez la lettre ’o’, l’ordinateur affiche alors : b o - - o - r. Si
vous proposez ensuite la lettre ’u’, l’ordinateur affiche : b o - - o u r ... " Si la lettre ne figure pas dans le
mot, l’ordinateur vous donne un point d’erreur. Au bout de 7 erreurs, vous avez perdu.

18.3.2 Améliorations possibles

Libre à vous d’ajouter ce que vous voulez. Voici quelques suggestions, au choix :

– pouvoir sauvegarder/recharger une partie en cours


– gérer les meilleurs scores au moyen d’un fichier
– dessiner une potence, mettre un peu de couleur (voir à ce propos sur internet comment colorier des
caractères ou mettre du gras) ; vous verrez que ça reste cependant assez succinct...
– autres idées... si vous en avez d’autres... appel à l’imagination...

Vous aimerez peut-être aussi