149
Service d’Informatique Introduction à la Programmation en C ++ Mohammed Benjelloun 1 ère Bachelier Année académique 2010-2011 FACULT E POLYTECHNIQUE DE MONS

Service d’Informatique - Université de Mons · Principes fondamentaux des structures 84 8.2. Pointeurs vers les structures 86 8.3. Structures et fonctions 87 Ce qu’il ... support

Embed Size (px)

Citation preview

Service d’Informatique

Introduction à la Programmation en C++

Mohammed Benjelloun

1ère Bachelier Année académique 2010-2011

FACULTE POLYTECHNIQUE DE MONS

M.BENJELLOUN Info I S. Informatique

Service d’Informatique

Table des matières

Avant-propos 1

Chapitre 1 : Introduction 2

1.1. Historique 2

1.2. Intérêts du langage 2

Chapitre 2 : Eléments du langage 5

2.1. Structure d'un programme C++ 5

2.2. Commentaires en C ++ 6

2.3. Identificateurs 7

2.4. Directives du préprocesseur 7

2.5. Points-virgules 8

2.6. Type de données 8

2.7. Les variables 9

2.8. Mots réservés 10

2.9. Opérateurs et expressions 11

2.9.1. Opérateurs arithmétiques 11

2.9.2. Conversions de type forcées (casting) 12

2.9.3. Opérateurs logiques et opérateurs de comparaison 12

2.9.4. Opérateurs d'affectation 13

2.9.5. Classes de priorités 14

FACULTE POLYTECHNIQUE DE MONS

M.BENJELLOUN Info I S. Informatique

2.10. Nos premiers programmes 15

2.10.1. Le programme C++ le plus court 15

2.10.2. Le premier programme 15

2.11. Les entrées/sorties 15

2.11.1. Flux de sortie cout 16

2.11.2. Flux d’entrée cin 19

Ce qu’il faut au moins retenir 20

Exercices 20

Chapitre 3 : Les structures de contrôle 21

3.1. La structure alternative 21

3.2. La structure de sélection (switch) 24

3.3. Les instructions répétitives 26

3.3.1. Instruction for 26

3.3.2. Instruction while 29

3.3.3. Instruction do ... while 32

3.4. Instructions de branchement 33

3.4.1. Instruction break 34

3.4.2. Instruction continue 34

3.4.3. Instruction goto 35

Ce qu’il faut au moins retenir 35

Exercices 36

Chapitre 4: Les tableaux et les chaînes de caractères 38

4.1. Les tableaux 38

4.1.1. Tableaux à une dimension 38

4.1.2. Tableaux à deux dimensions 39

4.1.3. Initialisation et réservation automatique 39

4.2. Les chaînes de caractères 41

4.2.1. Déclaration de chaînes de caractères 41

4.2.2. Initialisation de chaînes de caractères 41

4.2.3. Précédence alphabétique et lexicographique 42

Ce qu’il faut au moins retenir 46

Exercices 47

Chapitre 5 : Les fonctions 48

5.1. Introduction 48

5.2. Définition de fonctions 48

5.3. Instruction return 50

5.4. Appel et déclaration de fonctions 51

5.5. Passage de paramètres 52

5.5.1. Passage de paramètres par valeur 53

5.5.2. Tableaux comme paramètres 55

M.BENJELLOUN Info I S. Informatique

5.6. Visibilité des variables 56

5.7. Récursivité 59

5.8. Surcharge des fonctions 59

5.9. Paramètres par défaut 60

Ce qu’il faut au moins retenir 61

Exercices 62

Chapitre 6: Les pointeurs et les références 63

6.1. Un pointeur: qu'est-ce que c'est ? 63

6.1.1. Déclaration de pointeurs 64

6.1.2. Valeurs pointées et adresses 64

6.2. Une référence: qu'est-ce que c'est ? 66

Déclaration de références 66

6.3. Pointeurs, références et arguments de fonctions 66

6.4. Pointeurs et tableaux 69

6.5. Allocation dynamique de la mémoire 71

Ce qu’il faut au moins retenir 73

Exercices 74

Chapitre 7: Les fichiers 75

7.1. Généralité 75

7.2. Accès aux fichiers 75

7.2.1. Ouverture d’un fichier 76

7.2.2. Fermeture d’un fichier 77

7.3. Lecture et écriture 78

Ce qu’il faut au moins retenir 82

Exercices 83

Chapitre 8: Les structures 84

8.1. Principes fondamentaux des structures 84

8.2. Pointeurs vers les structures 86

8.3. Structures et fonctions 87

Ce qu’il faut au moins retenir 91

Exercices 92

Chapitre 9: Quelques erreurs à éviter 98

Annexes 102

Annexe A 103

Ordre de priorité des opérateurs 103

Table des codes ASCII des caractères 104

Annexe B : Support des présentations 106

Avant-propos Ces notes permettent de se familiariser avec le langage de programmation C++. Elles constituent le support des travaux pratiques et des exercices dans le cadre de l'enseignement du cours d'Informatique I de la première année Bachelier à la Faculté Polytechnique de l’Umons. Il représente aussi une partie du cours Programmation et Algorithmique dispensé aux étudiants d’IG Charleroi. Ce support a pour objectif d'aider l'étudiant dans l'assimilation des principales notions et de certains concepts d'algorithmique vus au cours théorique. Afin d’illustrer par la programmation ces concepts, ce syllabus est agrémenté de nombreux exemples et exercices permettant au débutant de mettre immédiatement en application la théorie qu'il vient de lire.

Ces notes ne sont donc nullement un manuel de référence du langage C++, mais simplement une aide à l'apprentissage des bases et de la structure du langage. Lors des séances d'exercices, où la présence est obligatoire, un complément de matière sera abordé. Donc le présent document ne peut être considéré comme complet. Notre ambition est d’éveiller chez l’étudiant le souhait de découvrir ce langage, d’utiliser les possibilités offertes par C++ afin de pouvoir écrire ses propres programmes. Certaines constructions syntaxiques sont volontairement omises, par souci de clarté. D'autres concepts, comme l’aspect objet (deuxième année bachelier), sont volontairement absents ou ne sont présentés que superficiellement, afin de ne pas accabler le programmeur néophyte. Il ne faut donc pas considérer ce syllabus comme auto-suffisant, mais bien, pour ce qu'il doit être, c'est-à-dire une introduction à la programmation des concepts de base de C++ relative au «plus» par rapport au C, et un support d'exercices et de travaux.

Néanmoins, nous espérons qu'il vous permettra de mieux comprendre ce langage et qu'il vous en rendra l'apprentissage plus agréable. Bien entendu, nous ne pouvons que chaudement renvoyer le lecteur désireux d'en savoir plus à l'un des très nombreux ouvrages sur le C++ disponibles dans toutes les bonnes librairies, et notamment à:

Bjarne Stroustrup. Le langage C++. Édition revue et corrigée. Pearson Education, 2003. La référence de base sur C++, par l'auteur du langage. Complet mais beaucoup trop riche pour débuter. homepage de Bjarne Stroustrup http://www.research.att.com/~bs/ Stanley B. Lippman and Josée Lajoie. C++ Primer. Addison-Wesley, third edition, 1998. Très complet (1200 pages), un des meilleurs livres pour apprendre C++. Bruce Eckel. Thinking in C++ Volumes 1 et 2. Téléchargeable gratuitement sur Internet http://mindview.net/Books/TICPP/ThinkingInCPP2e.html. Excellent ouvrage sur C++, Kris Jamsa et Lars Klander. C/C++ La bible du programmeur, 1500 astuces pour toutes les situations. Edition Eyrolles, 1998. De nombreux exemples en C et C++. 1010 pages

Sur le Web : http://cpp.developpez.com/

Cours, tutoriels, livres électroniques et Docs sur C++ : En particulier : le méga-cours de Christian Casteyde (2008)

Je voudrais remercier ici les collègues du Service d'Informatique qui ont pris soin de relire ces notes et de suggérer corrections et améliorations. Je suis bien conscient qu'il reste des erreurs et/ou des imprécisions. Merci au lecteur assidu de les signaler! Si vous notez la moindre erreur ou si vous souhaitez me proposer vos suggestions, n'hésitez pas à le faire à l’adresse E-mail suivante :

[email protected]

Chapitre 1 : Introduction

1.1. Historique Le langage de programmation C++ est un langage parmi d'autres (BASIC, FORTRAN, C, Java, ...). Tous ne sont pas équivalents et peuvent avoir des utilisations différentes selon les besoins de l’application ou du projet. Il est l'un des langages de programmation les plus utilisés actuellement. Le C++ dont le concepteur est Bjarne Stroustrup (1982), est fortement inspiré de deux autres langages de programmation :

• Le C : c’est un des plus puissants langages évolués. Il trouve ses sources en 1972 dans les 'Bell Laboratories'. Il fut inventé par deux ingénieurs, Dennis Ritchie et Brian Kernighan, pour concevoir un système d'exploitation portable : UNIX. Plus de 90% du noyau UNIX est écrit en C. Ce langage de programmation structuré, d'assez bas niveau, est très proche de la structure matérielle des ordinateurs (mots, adresses, registres, octets, bits, ...). C’est un langage structuré, modulaire, performant, polyvalent et relativement portable. Il permet de développer des applications à la fois complexes, efficaces et rapides grâce aux structures de contrôle de haut niveau.

• Le Simula 67 : mis au point en 1967 pour décrire, modéliser (et programmer) et simuler des systèmes complexes comportant des activités parallèles. Il est considéré comme le précurseur de la programmation orienté-objet, basé sur le concept de Class décrivant les attributs et méthodes d'un ensemble d'objets.

On peut considérer que le C++ est un langage apparenté au standard ANSI-C à qui on a greffé un système orienté objet. Il reprend d'ailleurs l'ensemble des règles syntaxiques du C, mais il corrige certains points faibles en apportant des améliorations, extensions, nouveautés dont les principales sont liées à la programmation orientée objet inspirée surtout de SIMULA 67 mais aussi de ADA, ALGOL 68...

Pour en terminer avec l’historique voici quelques dates :

• 1983 La première version du C++ est utilisée en interne chez ATT • 1985 Première implémentation commerciale du C++ • 1989 Premiers travaux de normalisation du C++ • 1997 Approbation du nouveau standard (Ansi C++) par le comité de normalisation

1.2. Intérêts du langage Nous allons tout au long de ce support étudier le langage C++ qui a su devenir un des langages les plus utilisés en développement. Ce langage est utilisé aussi bien sous UNIX que MacOS ou Windows. Entre autres, la plupart des applications graphiques comportent une partie en C++, ce qui inclut les traitements de textes, utilitaires, jeux,...

Chap.1 : Introduction

M.BENJELLOUN Info I S. Informatique

3

Le langage permet de développer des applications à la fois complexes, efficaces et rapides. Il permet aussi d’écrire un code extrêmement concis. Cependant cet atout peut se révéler être un inconvénient car parfois on perd en lisibilité. Les principaux avantages du C++ qui peut être considéré comme un « super C » sont : Structuré : conçu pour traiter les tâches d'un programme en les mettant dans des blocs. Efficace : possédant les mêmes possibilités de contrôle de la machine que l'assembleur, donc

générant un code compact et rapide. Il offre aussi un grand nombre de fonctionnalités. Portable : portabilité des fichiers sources. Modulaire : permettant de séparer une application en plusieurs sous-modules s'occupant chacun

d'un sous-domaine du problème initial. Objets : facilité d'utilisation du langage objet permettant une meilleure qualité de

programmation. Il est à noté que ce syllabus, ne dresse qu’une description des concepts de base de C++. En effet, nous n’aborderons pas ici l’aspect objet (classe, héritage, polymorphisme, ...) qui ne rentre pas dans le cadre de l’enseignement de cette année mais bien de la deuxième année de bachelier. Ce syllabus est organisé en neuf chapitres, chacun d'entre eux nous permettant de nous focaliser sur chacun des points du langage (les types, les expressions, les instructions, les fonctions, manipulation de pointeurs et références ...). Aucune connaissance d'un langage de programmation n'est supposée, mais bien des notions d'algorithmique vue au cours. Les notes, du chapitre 2 au chapitre 8, sont accompagnées de nombreux exemples et programmes d'illustration car un petit exemple valant mieux qu'un long discours !!. Nous appelons un programme un ensemble d’instructions contenant tout ce qu’il faut afin d’être compilé et exécuté. Ces programmes sont représentés comme suit :

….

….

Programme Chap.Num

Nous recommandons à nos lecteurs de suivre les styles de programmation suggérés. Ces mêmes chapitres se terminent par une rubrique ‘’ Ce qu’il faut au moins retenir ‘’ permettant de donner un résumé du chapitre et mettre parfois l’accent sur des erreurs ou des pièges dans lesquels tombent souvent les étudiants. Cette rubrique se présente comme suit :

Ce qu’il faut au moins retenir :

….

….

Chap.1 : Introduction

M.BENJELLOUN Info I S. Informatique

4

Quelques exercices suivent chaque chapitre. Nous invitons le lecteur à les résoudre afin de maîtriser le langage. Les solutions ne se trouvent pas dans ce syllabus : certaines seront proposées en séances de travaux et/ou d’exercices ; les étudiants de la faculté désireux d’en savoir plus sur les exercices non résolus peuvent me contacter. Le dernier chapitre décrit quelques erreurs observées lors des séances de travaux ou à l’examen. La plupart sont des erreurs d'inattention ou proviennent de la syntaxe du langage combinée à l'inattention du programmeur. Le but de ce chapitre est d’attirer l’attention des étudiants sur ce genre d’erreurs afin de les éviter. Enfin, on trouvera en annexes la table de précédence des opérateurs qui fixe l'ordre de priorité ainsi que la table des codes ASCII des caractères. D’autres documents comme celui des séances de travaux, le code source des programmes "Programme Chap.Num" d'illustration de ce syllabus, se trouvent sur le site Web https://applications.umons.ac.be/moodle/

Chapitre 2 : Eléments du langage C++

2.1. Structure d'un programme C++

Un programme écrit en C++ doit obéir à certaines règles bien définies. Il est composé des directives du préprocesseur (une ligne de programme commençant par le caractère dièse (#) ) et d’une ou plusieurs fonctions stockées dans un ou plusieurs fichiers. Une fonction particulière, dont la présence

dans le programme est obligatoire, est la fonction programme principal ou main(). Le programme

commencera toujours son exécution au début de cette fonction. Une fonction est un bloc de code d'une ou plusieurs instructions qui peut renvoyer une valeur à la fonction appelante. La forme générale d'une fonction est :

Classe [Type] Ident( [liste_de_parametres_formels] ) { Corps_de_la_fonction

}

Les éléments entre [] dans cette syntaxe sont facultatifs, car une valeur par défaut existe. Nous verrons plus tard au chapitre 5 de nombreux exemples de fonctions. La syntaxe précédente appelée déclaration de la fonction contient : Classe : la classe de mémorisation de la fonction qui peut prendre par exemple la valeur static ou

extern. Classe peut ne pas exister. Type : le type de la valeur renvoyée par la fonction. Type étant un des types reconnus par le langage

C++ comme :void, int, float… (voir 2.6). Ident : l’identificateur représentant le nom de la fonction (voir 2.3). liste_de_parametres_formels : la liste de paramètres nécessaires pour la bonne exécution de la

fonction. Chacun des paramètres ayant un nom et un type. Corps_de_la_fonction : est un corps commençant par une accolade ouvrante ‘’{‘’ et terminant par une

accolade fermante ‘’}‘’, comportant des déclarations de variables, des instructions devant être exécutées par la fonction.

La figure 2.1 nous présente une structure simple d’un programme C++. Nous verrons tout au long de ce syllabus, plus en détail et d’une manière plus précise, le rôle de chaque bloc. Remarquez cependant les parenthèses obligatoires après les noms des fonctions et les accolades d’ouverture et de fermeture délimitant le début et la fin de chaque fonction. La fonction fonc1() retourne une valeur à la fonction appelante main(). Les fonctions fonc2() et main() ne retournent rien. En effet le mot-clé void (vide en anglais) devant ces fonctions, indique qu’elles ne retournent aucune valeur. Le (void ) après main indique que cette fonction ne prend aucun argument. Le (int) après fonc1 et fonc2 indique que ces fonctions prennent un argument de type entier.

Il faut noter aussi que toutes les instructions doivent se trouver dans main() ou être appelées depuis main() à l’aide d’une ou de plusieurs fonctions. Tout ce qui est en dehors de main() et qui n’est pas appelé dans main() n’est pas exécuté. Il ne faut donc pas oublier la fonction main() !

Chap. 2 : Eléments du langage C++

M.BENJELLOUN Info I S. Informatique

6

Figure 2.1 : Une structure simple d’un programme C++

2.2. Commentaires en C ++

Dans un programme C++, il est possible (et d’ailleurs préférable) d’ajouter des commentaires facilitant ainsi la lecture et la compréhension du programme. En effet, comme nous avons la possibilité d'utiliser des expressions compactes, ceci peut parfois rendre le programme moins lisible et sans commentaires ou explications, ces programmes peuvent devenir difficilement compréhensibles. Toute suite de caractères encadrée par les symboles ‘/*’ et ‘*/’ correspond à un commentaire, et ne joue évidemment aucun rôle lors de l’exécution du programme. Il faut noter aussi le symbole ‘//’ pour un commentaire sur une seule ligne. Ces commentaires sont donc ignorés par le compilateur et ne sont indispensables que sur le plan de la documentation du programme. Notons que les commentaires imbriqués sont interdits.

Exemple 1 syntaxiquement corrects :

/* ceci est un commentaire écrit en deux lignes */

// un commentaire sur une seule ligne /* un autre commentaire sur une seule ligne */

syntaxiquement incorrects : /* imbriquer /* ce n’est pas autorisé */ donc à éviter */

#include <iostream>

using namespace std;

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

int fonc1(int x);

void fonc2(int y);

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

void main(void)

{ /* début du bloc de la fonction main*/ int i, j=5, k; // définition des variables locales

i = 0 ;

j=fonc1(i) ;

fonc2(i) ;

} // fin du bloc de la fonction main - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

int fonc1( int var){

….. ;

….. ;

return var ;

}

void fonc2( int t)

{ /* Corps de la fonction fonc2 */

….. ;

….. ;

}

Directives du préprocesseur : accès avant la compilation

Déclaration des fonctions

Programme

principal

Définitions des

fonctions

Chap. 2 : Eléments du langage C++

M.BENJELLOUN Info I S. Informatique

7

2.3. Identificateurs

Un identificateur est un nom donné à un objet du programme (variable, constante, fonction), composé de lettres et de chiffres commençant par une lettre. Le caractère _ (souligné) est considéré comme une lettre.

• En C/C++, les lettres minuscules sont différenciées des majuscules ainsi TAILLE, Taille et taille sont trois identificateurs différents et peuvent être utilisés dans le même programme.

• Les mots réservés par le compilateur, dans la syntaxe du C++, ne peuvent être utilisés comme identificateurs à cause des confusions que cela pourrait entraîner.

Exemple 2 Identificateurs valides:

Xx, y1, fonc1, _position, DEBUT, fin_de_fichier, GSM, FPMs, VeCteuR

Identificateurs invalides : 3eme commence par un chiffre

x#y caractère non autorisé (#) no-commande caractère non autorisé taux de change caractère non autorisé (espace) main mot réservé (voir 2.8.)

2.4. Directives du préprocesseur

Les directives du préprocesseur permettent d'effectuer un prétraitement du programme source avant qu'il soit compilé. On y retrouve ce dont le compilateur a besoin pour compiler correctement un programme. Ces directives sont identifiées par le caractère dièse (# qui constitue un signal au préprocesseur) comme premier caractère de la ligne. On les utilise pour l’inclusion de fichiers, compilation conditionnelle, …

Inclusion de fichiers

La directive #include insère, au moment de la compilation, le contenu d'un fichier, généralement réservé à l'inclusion de fichiers appelés fichiers en-tête contenant des déclarations de fonctions précompilées, de définition de types... Il sert à inclure des librairies qui regroupent des ensembles de fonctions qui permettent de faciliter la tâche du programmeur. Ces fichiers sont traditionnellement suffixés par .h (header file), mais en C++, il n'est pas toujours nécessaire de spécifier l'extension .h surtout si on ajoute la déclaration using namespace std;

Syntaxe #include <nom-de-fichier> // répertoire standard #include "nom-de-fichier" // répertoire courant : où se trouve le programme

Ainsi l’instruction suivante : #include <iostream.h> // Entrées-sorties standard Ou #include <iostream>

using namespace std;

invoque le fichier qui comporte des routines auxiliaires concernant les opérations d’entrée/sortie (par exemple saisie au clavier et affichage à l’écran).

Chap. 2 : Eléments du langage C++

M.BENJELLOUN Info I S. Informatique

8

Le C++ fait appel à un ensemble de librairies standards, fournies avec le compilateur, contenant des fonctions prédéfinies permettant de contrôler des aspects tels que le traitement de chaînes de caractères <string>, la manipulation des fonctions mathématiques <math> ou <cmath>, etc.

Il faut savoir que la plupart des déclarations de la bibliothèque standard C++ ont été regroupées dans un namespace, appelé std. Ce dernier est un espace de noms auquel appartiennent les routines des

flux standard d'entrée/sortie. Ainsi, un programme utilisant des fonctionnalités de la bibliothèque standard comportera idéalement la directive

using namespace std; ce qui permettra d’inclure des fichiers sans ajouter l’extension .h.

Remarque: Le compilateur Visual C++ 6 par exemple accepte les deux notations, à savoir :

#include <iostream.h> et #include <iostream> using namespace std;

alors que le compilateur DotNet n’accepte que la seconde notation

2.5. Points-virgules

En C++, le point virgule est un terminateur d’instruction, on en met donc après chaque instruction. On met également un point virgule après chaque déclaration.

2.6. Type de données

Les types de base du C++ sont : • les mêmes que les types du C ; • le type bool ; • la notion de référence ; • le type class qui permet de définir les objets.

Lors de la programmation, on a besoin de manipuler et de stocker les données. Le langage C++ comme le C est déclaratif et typé. Cela signifie que chaque entité (variables, fonctions, …) manipulée dans un programme doit être déclarée et son type est vérifié à la compilation. Il permet la représentation des données sous différents formats. Les différents types de données de base à partir desquels on peut construire tous les autres types, dits types dérivés (tableaux, structures, unions ...) sont les suivants:

Type Représentation Signification Intervalle Taille en

octets

Exemple

caractères signed char ou char

unsigned char

Un caractère unique -128 à 127

0 à 255

1 'a' 'A' 'Z' '\n'

'\t'

entiers signed int ou int

unsigned int

short ou long

entier signé (par défaut)

entier positif

spécifie la taille de l'emplacement mémoire utilisé

-231 à 231 –1

0 à 232 -1

-215

à 215

–1

2 ou 4 0 1 -1 4589

32000

réels float

double

long double

Réel simple précision

Réel double précision

Réel précision étendue

+-10 E-37 à +-10 E+38

+-10 E-307 à

+-10 E+308

4

8

0.0 1.0E–10

1.0 -

1.34567896

La plage de valeur des types peut être représentée comme ceci : char ≤ short int ≤ int ≤ long int float ≤ double ≤ long double

Chap. 2 : Eléments du langage C++

M.BENJELLOUN Info I S. Informatique

9

où l'opérateur « ≤ » signifie ici « a une plage de valeur plus petite ou égale que ».

Le codage en mémoire va déterminer les valeurs limites (minimales, maximales, précision numérique) pour les nombres entiers et réels. On peut définir des types non signés (unsigned) qui correspondent à des valeurs toujours positives. Les caractères sont eux représentés en machine par un entier variant de –128 à 127, correspondant pour les valeurs positives au code ASCII (code standard associant un ensemble de caractères standards à des entiers, voir Annexe A). A noter que les caractères accentués ou spéciaux du français (é, à, ç, ô, …) ne font pas partie du code ASCII. Nous les éviterons donc.

Pour obtenir la taille des informations manipulées par C++, on utilisera l’opérateur sizeof. Ce dernier retourne la taille en octets d’un type ou d’une expression passée en paramètre, auquel cas cette expression n’est en fait pas évaluée. Par exemple, la taille d’un entier ‘i‘ est donnée par sizeof(i). Pour illustration voir Programme 2.4.

C++ introduit le type bool (booléen) qui s'accompagne des mots-clés true et false. Là où une condition est attendue on doit mettre une expression de type booléen et il est déconseillé de prendre les entiers pour des booléens et réciproquement. Par exemple le résultat d'une opération logique (&&, ||, etc.) est de type booléen. Le type bool est bien pratique pour tout ce qui est variables ou fonctions "drapeau".

Il existe aussi le type void : il s'agit d'un "pseudo-type" utilisé pour spécifier le fait qu'il n'y a pas de type. Ce type sera surtout utilisé au moment de déclaration des fonctions (fonctions ne renvoyant rien). D’autres types comme les structures, les pointeurs ou les références seront détaillés plus loin dans ce syllabus. Par contre le type class ne sera pas abordé dans ce cours.

2.7. Les variables

Les variables, qui doivent être déclarées avant leur utilisation, sont des données dont la valeur peut être modifiée durant l'exécution du programme. Chaque variable possède un type de données, une adresse mémoire et un nom qui est son identificateur. Pendant l’exécution du programme, elles occupent de la place mémoire.

Définir une variable, c’est donc associer un type et un identificateur. La syntaxe de cette opération est :

Type Ident [= valeur] {, Ident [= valeur]}; Dans cette notation, les éléments entre crochets [ ] sont optionnels, les éléments entre accolades {} peuvent apparaître 0, 1 ou plusieurs fois.

Type est un des types reconnus par le langage C++. Ident est l’identificateur représentant le nom par lequel on désigne la variable. valeur est la valeur initiale qu'on souhaite attribuer à la variable. Si, dans la déclaration, la variable n'est pas initialisée (on ne lui attribue pas de valeur initiale), cette dernière peut alors contenir n'importe quoi.

Chap. 2 : Eléments du langage C++

M.BENJELLOUN Info I S. Informatique

10

Exemple 3 int i, j=5, k; // i, j, k sont des variables de type entier et j est initialisée à 5

float moyenne; // moyenne est une variable de type virgule flottante, char une_lettre; // une_lettre est une variable de type caractère int Compteur = 0;

float X = 5.03e-3;

Le compilateur réserve la mémoire nécessaire pour stocker ces variables. Leur initialisation n’est pas nécessaire lors de la déclaration, mais il est possible de les initialiser par affectation.

En utilisant le mot clé réservé const, nous pouvons indiquer que la valeur de cette variable ne change pas (constante) au cours d'un programme comme dans: const int MAX = 1000; const char NEWLINE = '\n'; L’initialisation de telles variables est obligatoire lors de leur déclaration.

En ce qui concerne le type caractère, il existe des caractères spéciaux (constantes) représentant une séquence d’échappement qui seront toujours précédés de l’anti-oblique (backslash : \) et sont interprétés comme suit :

Caractères Signification CODE ASCII (hexadécimal)

\n Génère une nouvelle ligne (newline) 0x0A

\t Tabulation horizontale 0x09

\v Tabulation verticale 0x0B

\b Retour, supprime le caractère précédent (backspace) 0x08

\r Retour chariot (return) 0x0D

\f Saut de page (form feed) 0x0C \a Emet un bip sonore (alarm) 0x07

\\ Affiche une barre oblique inverse (backslash) 0x5C

\' Affiche une apostrophe (single quote) 0x2C

Remarques : • Les variables peuvent être définies quasiment n'importe où dans le programme. Cela permet

de ne définir une variable temporaire que là où l'on en a besoin. Cependant, par souci de lisibilité nous préférons (à ce niveau) des déclarations au début des fonctions.

• La définition d'une variable ne suffit pas, en général, à l'initialiser. Les variables non initialisées contenant des valeurs aléatoires, il faut éviter de les utiliser avant une initialisation correcte. Les variables « constantes » que l'on déclare avec le mot clé const, doivent être initialisées lors de la déclaration.

2.8. Mots réservés

L’attribution des noms à des identificateurs est soumise à une limitation due au groupe des mots dits réservés ou mots-clés. Les mots réservés du langage sont des noms prédéfinis qui ont une signification particulière, et ne peuvent pas servir de noms de variables personnelles. La signification des mots réservés les plus utilisés sera donnée au fur et à mesure du cours. Nous ne verrons cependant pas ici la signification de tous ces mots réservés.

Chap. 2 : Eléments du langage C++

M.BENJELLOUN Info I S. Informatique

11

asm auto bool break case

catch char class const continue

default delete do double else

goto if inline int long

mutable namespace new operator private

protected public register return short

signed sizeof static struct switch

template this typedef union unsigned

using virtual void volatile while

2.9. Opérateurs et expressions

2.9.1. Opérateurs arithmétiques

+ addition

- soustraction

* multiplication

/ division (entière et rationnelle!)

% modulo (reste d'une div. entière)

7%2 vaut 1, alors que 7/2 vaut 3. Si l’on désire obtenir une division réelle, il faut que le numérateur et/ou le dénominateur soi(en)t réel(s). Ainsi 7.0/2, 7/2.0 et 7.0/2.0 donnent la même valeur, soit 3.5.

Le compilateur considère le type des opérandes pour savoir comment effectuer les opérations. Si, par exemple, on a : int i = 5, j = 4, k; double f = 5.0, g = 4.0, h;

k = i / j; // k = 5/4 = 1 h = f / g; // h = 5.0/4.0 = 1.25

h = i / j; // h = 5/4 = 1.0000

Les opérateurs + et – sont également utilisés comme opérateurs de signes unaires (opérateurs placés devant un entier ou un réel pour indiquer son signe).

La priorité dans les évaluations d’expression est donnée aux opérateurs unaires + et -, puis aux opérateurs *, / et %, et enfin aux opérateurs binaires + et – (voir Annexe A). Par exemple,

5 + 3 * -7 = 5 + (3*(-7)) = -16 5 – 2 / -1 + 8 = 5 – (2/(-1)) + 8 = 15

On peut toujours utiliser des parenthèses en cas de doute de priorité, ce qui peut d’ailleurs faciliter la lisibilité.

(5 + 3) * (-7) = -56 (5 + 10) / (-1 + 8) = 15/7 = 2 (en arithmétique entière)

Les opérateurs arithmétiques binaires sont :

Chap. 2 : Eléments du langage C++

M.BENJELLOUN Info I S. Informatique

12

Les expressions sont formées de combinaisons d’opérandes (constantes caractères ou numériques, identificateurs de variables ou de fonctions) et d’opérateurs, avec éventuellement des parenthèses.

Ainsi, (3*x*x + 5*x*y + 7)/(x + 1) 2*cos(PI*x) exp((0.5)*log(x)) // racine carrée de x

où x, y sont des variables réelles, PI une constante réelle, cos(), exp() et log() des fonctions mathématique, sont des expressions correctes.

2.9.2. Conversions de type forcées (casting) Il est possible de convertir explicitement une valeur d’un certain type vers un autre, quelconque, en forçant la transformation à l'aide de la syntaxe:

(Type) <Expression>

Exemple 4

Nous divisons deux variables de type entier. Pour avoir plus de précision, nous voulons avoir un résultat de type rationnel. Pour ce faire, nous convertissons l'une des deux opérandes en float. Automatiquement le compilateur convertira l'autre opérande en float et effectuera une division rationnelle:

int I=3, J=4; float K; K = (float)I/J;

La valeur de I est explicitement convertie en float. La valeur de J est automatiquement convertie en float. Le résultat de la division (type rationnel, valeur 0.75) est affecté à K.

Attention ! Les contenus de I et de J restent inchangés; seulement les valeurs utilisées dans les calculs sont converties.

Exemple 5 float F=65.49, FI; int I=98, IF; char C, CF, CI; IF = (int) F; // IF = 65 CF = (char) F; // CF= A FI = (float) I; // FI= 98.0000 CI = (char)I; // CI= b

2.9.3. Opérateurs logiques et opérateurs de comparaison Les opérateurs logiques permettent de calculer des expressions logiques, au résultat vrai, ou faux.

Opérateurs logiques

&& et logique (and)

|| ou logique (or)

! négation logique (not)

Opérateurs de comparaison

== égal à

!= différent de

<, <=, >, >= plus petit que, ...

Chap. 2 : Eléments du langage C++

M.BENJELLOUN Info I S. Informatique

13

Les résultats de ces opérateurs sont du type booléen. Le résultat d'une expression logique vaut true si elle est vraie et false sinon. Réciproquement, toute valeur non nulle est considérée comme vraie et la valeur nulle comme fausse.

Exemple 6 x = = y // vrai si x égal à y (true), faux sinon (false) (val > 0) || (val == -10) // vrai si val est positive ou égale à -10 (Car >= 'b') && (Car < 'e') // vrai si le caractère Car vaut 'b', 'c', ou 'd' int i = 7; float f = 5.5; f > 5 ====> true //retourne la valeur true (i + f) <= 1 ====> false

Pour les expressions logiques formées de && ou de ||, le programme évalue tout d’abord l’expression de gauche, et ensuite celle de droite, mais seulement si le résultat n’est pas encore déductible. C'est ce qu'on appelle une évaluation paresseuse.

!expr1 est vrai si expr1 est faux et faux si expr1 est vrai ; expr1&&expr2 est vrai si les deux expressions expr1 et expr2 sont vraies et faux sinon.

L'expression expr2 n'est évaluée que dans le cas où l'expression expr1 est vraie ; expr1 || expr2 = true si expr1= true ou expr2= true et false sinon. L'expression expr2 n'est évaluée que dans le cas où l'expression expr1 est fausse.

2.9.4. Opérateurs d'affectation

Le symbole d’affectation = est un opérateur à part entière qui peut être utilisé au sein d’expressions. Son rôle consiste à évaluer l’expression du membre de droite et à transférer ce résultat comme valeur de l’objet au membre de gauche.

L’instruction d’affectation la plus simple est donc : variable = expression ; Une autre instruction tout aussi correcte est variable1 = variable2 = expression ; En pratique, nous retrouvons souvent des affectations comme: i = i + 5 ; /* On prend la valeur contenue dans la variable i, on lui ajoute 5 et on met le

résultat dans la variable i. */ En C/C++, il est possible d’utiliser une formulation plus compacte: i += 5 ; // réalise la même opération que l’instruction précédente

L'opérateur += est un opérateur d'affectation. Pour la plupart, les expressions ont la forme:

expr1 = (expr1) op (expr2) La formulation équivalente utilisant un opérateur d'affectation est :

expr1 op= expr2

Opérateurs d'affectation

+= ajouter à

-= diminuer de

*= multiplier par

/= diviser par

%= modulo

Ainsi ces expressions sont équivalentes :

X= X + 32 ; � X += 32 ;

F= F/1.5 ; � F /= 1.5 ;

I= I *(J+10); � I *= J+10;

Tab[n*i+j]= Tab[n*i+j]*3; Tab[n*i+j] *= 3;

Chap. 2 : Eléments du langage C++

M.BENJELLOUN Info I S. Informatique

14

Le C/C++ fournit aussi d’autres opérateurs contractés pour des opérations usuelles. Ainsi l'opérateur d'incrémentation ++ ajoute 1 à son opérande entière. De même l'opérateur de décrémentation -- retranche 1 à son opérande. On peut les utiliser soit devant une variable (préfixé : mise à jour de la variable avant son utilisation dans l'expression courante), soit derrière la variable (postfixé : mise à jour après son utilisation dans l'expression courante).

Exemple 7 int i=4, j=3, k;

i++; // équivalent à i= i+1, donc i vaut maintenant 5

--j; // équivalent à j= j-1, donc j vaut maintenant 2

k = ++i; // i = i+1 puis k = i, donc i=k= 6 k = j--; // k = j puis j=j-1, donc k vaut 2 et j vaut 1

Ce dernier exemple nous incite à faire attention lors de l’utilisation des opérateurs contractés. Il faut savoir quand utiliser une variable préfixée ou postfixée. La confusion entre les deux peut induire des erreurs dans le programme. Il est à noter que les formulations compactes ne sont pas obligatoires et n’ont pour rôle que de réduire la quantité de code du programme. Voici une explication sur l’utilisation de la forme compacte pour incrémenter/décrémenter une variable et en même temps affecter sa valeur à une autre variable :

X = I++ passe d'abord la valeur de I à X et incrémente I après postfixe

X = I-- passe d'abord la valeur de I à X et décrémente I après

X = ++I incrémente d'abord I et passe la valeur incrémentée à X préfixe

X = --I décrémente d'abord I et passe la valeur décrémentée à X

2.9.5. Classes de priorités

Parmi les opérateurs que nous connaissons jusqu'ici, nous pouvons distinguer les classes de priorités suivantes:

Priorité 1 (la plus forte): ( )

Priorité 2: ! ++ --

Priorité 3: * / %

Priorité 4: + -

Priorité 5: < <= > >=

Priorité 6: == !=

Priorité 7: &&

Priorité 8: ||

Priorité 9 (la plus faible): = += -= *= /= %=

Evaluation d'opérateurs de la même classe :

� Dans chaque classe de priorité, les opérateurs ont la même priorité. Si nous avons une suite d'opérateurs binaires de la même classe, l'évaluation se fait en passant de la gauche vers la droite dans l'expression.

Pour les opérateurs unaires (!,++,--) et pour les opérateurs d'affectation (=,+=,-=,*=,/=,%=), l'évaluation se fait de droite à gauche dans l'expression.

Chap. 2 : Eléments du langage C++

M.BENJELLOUN Info I S. Informatique

15

2.10. Nos premiers programmes

Un fichier source C++ correspond à un fichier texte contenant des lignes de code. Ce fichier prend généralement pour extension .cpp. Lorsqu'un programme est chargé, son exécution commence par l'appel d'une fonction spéciale du programme. Cette fonction doit impérativement s'appeler main() (principal en anglais) pour que le compilateur puisse savoir que c'est cette fonction qui marque le début du programme.

2.10.1. Le programme C++ le plus court

Notre premier programme est le programme C++ le plus court à écrire. Et pour cause, c’est un programme qui ne fait rien. Il est constitué uniquement de la fonction main(), obligatoire pour que le programme soit exécutable.

void main (void) { }

Cette fonction ne retourne aucune valeur (premier void) et ne prend aucun argument (deuxième void). Les parenthèses après main sont obligatoires, elles spécifient que l’on est en train de décrire une fonction. Les accolades { } encadrent un bloc d'instructions (ensemble de déclarations et instructions exécutables), constituant le Corps_de_la_fonction. Dans notre exemple, ce corps est vide, donc le programme ne fera rien.

2.10.2. Le premier programme

A titre d’exemple, notre premier programme devient le programme 2.1 qui calcule la circonférence et la surface d’un disque.

void main(void) /* Ce programme calcule la circonférence et

la surface d’un disque de rayon 2 */

{

const float PI = 3.141596; // initialisation de la constante PI

int rayon = 2; // initialisation de la variable rayon

float circonference, surface, temp; // déclaration des variables réelles

temp = PI*rayon; // affectation d’une variable temporaire

circonference = 2*temp; // calcul de la circonférence

surface = temp*rayon ; // calcul de la surface

}

Programme 2.1 L’exécution de ce programme n’affichera aucun résultat à l’utilisateur. En effet, aucune fonction d’affichage n’a été invoquée !

2.11. Les entrées/sorties

En C++, la saisie/affichage ou entrée/sortie (I/O) est gérée par des librairies, c’est-à-dire des ensembles de fonctions/objets standard pré-programmées, livrées avec les compilateurs. Un programme qui utilise les flux standard d'entrée/sortie doit comporter la directive #include <iostream.h> ou #include <iostream> + using namespace std

Chap. 2 : Eléments du langage C++

M.BENJELLOUN Info I S. Informatique

16

Les flux d'entrée/sortie sont représentés dans les programmes par les trois variables (objet flot, stream), pré-déclarées et pré-initialisées, suivantes :

• cout, le flux standard de sortie, habituellement associé à l'écran, • cin, le flux standard d’entrée, habituellement associé au clavier, • cerr, le flux standard pour la sortie des messages d’erreur associé à l’écran.

Les écritures et lectures se font à l'aide des opérateurs <<, appelé opérateur d'injection (injection de données dans un flux de sortie), et >>, appelé opérateur d'extraction (extraction de données d'un flux d'entrée).

Le namespace, appelé std est un espace de noms auquel appartient cout et cin. La directive using namespace std;

permettra d’inclure des fichiers sans ajouter l’extension .h et évitera d'avoir à écrire explicitement std::cout,par exemple.

2.11.1. Flux de sortie cout

Le flux de sortie cout permet d’afficher tous les types de valeurs à l’écran.

Syntaxe : cout << expression << expression << expression … ; Description : Permet l'écriture (à l'écran par défaut).

Pour utiliser cout dans un programme, il faut inclure la librairie standard I/O <iostream>.

#include <iostream.> // pour standard I/O

using namespace std ; // utilisation de l’espace de nommage std

void main(void){

int i=5 , j= 10 ; // déclaration et initialisation de deux entiers i et j

cout << i << " " << i+1 << endl; // Affiche la valeur de i et de i+1

cout << i << " " << j << endl; // Affiche la valeur de i et de j

cout << " 100+20 =" << 100+20 << endl; // Affiche : 100+20 = 120

cout << "Salut a tous " << endl; // Affiche le texte entre " "

}

Programme 2.2

Remarquez que nous évitons d'utiliser des caractères spéciaux (à,é,à,è,ù,ô,…) dans les affichages. Leur traitement dépend en effet des compilateurs et sort du cadre de la norme ANSI. endl signifie le saut de ligne, il représente la même chose que ‘\n’. Ainsi, les deux écritures sont équivalentes

int n=2;

float x= 3.4;

char C= 'A' ;

cout << n << endl ;

cout << x << endl;

cout << C << endl;

int n=2;

float x= 3.4;

char C= 'A' ;

cout << ("%d" , n) << '\n' ;

cout << ("%f" ,x) << '\n';

cout << ("%c" ,C) << '\n';

Pour indiquer sous quel format afficher ces variables, on utilise un descripteur d'affichage de type %d pour les entiers, %f pour les réels, %c pour les caractères et %s pour les chaînes de caractères. Voici quelques descripteurs des paramètres passés en écriture.

"%c" : un caractère. "%d" ou "%i" : entier signé. "%u" : entier non signé. "%e" : réel avec un exposant. "%f" : réel sans exposant. "%s" : chaîne de caractère.

Chap. 2 : Eléments du langage C++

M.BENJELLOUN Info I S. Informatique

17

"%p" : pointeur. "%x" ou "%X" : entier base 16 avec respect majuscule/minuscule.

Pour pouvoir traiter correctement les arguments du type long, il faut utiliser les spécificateurs %ld, %li, %lu, %lo, %lx.

Et si l’on désire afficher les résultats du programme 2.1

#include <iostream> // pour standard I/O ;

using namespace std; // utilisation de l’espace de nommage std

void main(void) // Ce programme calcule et affiche la circonférence et la surface d'un disque de rayon 2

{

const float PI = 3.141596; // initialisation de la constante PI

int rayon = 2; // initialisation de la variable rayon

float circonference, surface, temp; // déclaration des variables réelles

temp = PI*rayon; // affectation d'une variable temporaire

circonference = 2*temp; // calcul de la circonférence

surface = temp*rayon ; // calcul de la surface

cout << "La surface d'un disque de rayon " << rayon << " vaut " << surface << endl; // (1)

cout << "La circonference vaut " << circonference << endl;

}

Programme 2.3

cout permet de placer des valeurs de types différents dans la même instruction comme le montre (1). Voici ce que affiche l’exécution du programme 2.3 :

La surface d'un disque de rayon 2 vaut 12.5664 La circonference vaut 12.5664

Maintenant que nous savons comment afficher des informations à l’écran, voici un exemple qui permet d’obtenir la taille des informations manipulées par C++ à l’aide de l’opérateur sizeof vu auparavant.

#include <iostream>

using namespace std;

void main(void) {

char c;

unsigned char cu;

int i;

unsigned int iu;

short int is;

unsigned short int isu;

long int il;

unsigned long int ilu;

float f;

double d;

long double ld;

cout << "\n char= " << sizeof(c) << "\n unsigned char = " << sizeof(cu)

<< "\n int = " << sizeof(i) << "\n unsigned int = " << sizeof(iu)

<< "\n short = " << sizeof(is) << "\n unsigned short = " << sizeof(isu)

<< "\n long = " << sizeof(il) << "\n unsigned long = " << sizeof(ilu)

<< "\n float = " << sizeof(f)

<< "\n double = " << sizeof(d) << "\n long double = " << sizeof(ld)

<< endl;

}

Solution : char= 1 unsigned char = 1 int = 4 unsigned int = 4 short = 2 unsigned short = 2 long = 4 unsigned long = 4 float = 4 double = 8 long double = 8

Programme 2.4

Chap. 2 : Eléments du langage C++

M.BENJELLOUN Info I S. Informatique

18

Extension de l’utilisation de cout

Le flux de sortie cout offre plusieurs possibilités (fonctions membres) pour améliorer l’affichage de données. Parmi celles-ci nous trouvons :

a) Précision : Si l’on désire spécifier la précision d’une valeur lors de l’affichage, voici un exemple qui illustre le fonctionnement :

#include <iostream> // pour standard I/O

using namespace std; // utilisation de l’espace de nommage std pour importer les symboles de la bibliothèque standard C++

void main(void) {

const double V=5.1234567;

cout << "V vaut : " << V << endl;

cout.precision(2); /*manipulateur (fonction membre) de cout : tous les flottants QUI SUIVENT

seront affichés avec une précision de 2 chiffres */

cout << "avec precision de 2 -- V vaut : " << V << endl;

cout.precision(4);

cout << "avec precision de 4 -- V vaut : " << V << endl;

cout.precision(6);

cout << "avec precision de 6 -- V vaut : " << V << endl;

cout.precision(8);

cout << "avec precision de 8 -- V vaut : " << V << endl;

}

Programme 2.5

et dont le résultat à l’affichage est le suivant : V vaut : 5.12346 avec precision de 2 -- V vaut : 5.1 avec precision de 4 -- V vaut : 5.123 avec precision de 6 -- V vaut : 5.12346 avec precision de 8 -- V vaut : 5.1234567

b) Largeur des champs :

La fonction width indique le nombre minimal de caractères du champ de sortie.

#include <iostream>

using namespace std;

void main(void) {

int i = 0;

cout.width(5);

cout << i << '\n';

i = 1;

cout.width(5);

cout << i << '\n';

}

#include <iostream>

using namespace std;

void main(void) {

int i = 0;

cout.width(i+3);

cout << i << '\n';

i = 1;

cout.width(i+3);

cout << i << '\n';

}

#include <iostream>

using namespace std;

void main(void) {

int i = 0;

cout.fill('.'); // voir résultats

cout.width(i+3);

cout << i << '\n';

i = 1;

cout.fill('.'); // voir résultats

cout.width(i+3);

cout << i << '\n';

}

Programme 2.6 Solutions :

0

1

0

1

..0

...1

Chap. 2 : Eléments du langage C++

M.BENJELLOUN Info I S. Informatique

19

2.11.2. Flux d’entrée cin

Si pour l’affichage on utilise le flux de sortie cout qui repose sur l’opérateur <<, pour la saisie on utilise son dual, le flux d’entrée cin qui repose sur l’opérateur >>. cin est aussi définie dans la librairie <iostream>.

Syntaxe : cin >> valeur >> valeur >> valeur … ; Description : Permet la saisie (du clavier en principe).

#include <iostream>

using namespace std;

void main () {

int n=2;

float x= 3.4;

char C= 'A' ;

cout << " Entier n = "<< n << " ; Entrez un nouveau = " ;

cin >> n ;

cout << " Float x = "<< x << " ; Entrez un nouveau = " ;

cin >> x ;

cout << " Char C = "<< C << " ; Entrez un nouveau = " ;

cin >> C ;

cout << "\n L'entier vaut maintenant : " << n << endl;

cout << " Le flottant vaut maintenant :" << x << endl;

cout << " Le caractere vaut maintenant :" << C << endl ;

}

L’exécution nous donne : Entier n = 2 ; Entrez un nouveau = 3 Float x = 3.4 ; Entrez un nouveau = 5.67 Char C = A ; Entrez un nouveau = y

L'entier vaut maintenant : 3 Le flottant vaut maintenant :5.67 Le caractere vaut maintenant :y

Nous pouvons aussi améliorer notre programme 2.3. Le rayon peut alors prendre n’importe quelle valeur réelle puisque sa valeur est demandée à l’utilisateur.

#include <iostream>

using namespace std;

void main(void) {

const float PI = 3.141596; // initialisation de la constante PI

float circonference, surface, temp, rayon; // déclaration des variables réelles

cout << "Quel est la valeur de ton Rayon : " ;

cin >> rayon;

temp = PI*rayon; // affectation d'une variable temporaire

circonference = 2*temp; // calcul de la circonférence

surface = temp*rayon ; // calcul de la surface

cout << "La surface d'un disque de rayon " << rayon << " vaut " << surface << endl;

cout << "La circonference vaut " << circonference << endl;

}

Programme 2.8 La solution du programme 2.8 est :

Quel est la valeur de ton Rayon : 5

La surface d'un disque de rayon 5 vaut 78.5399

La circonference vaut 31.416

Chap. 2 : Eléments du langage C++

M.BENJELLOUN Info I S. Informatique

20

Exercices

2.1. Citer parmi les identificateurs suivants ceux qui sont valides et ceux qui ne le sont pas. a) record b)long c) %interet d) étudiant e) MaVariable3 f) ma_var_4 g) 2x h) Long i) double j) ma-var-5 2.2. Les déclarations suivantes sont-elles correctes ? Les corriger si nécessaire.

a) int i = 3, j , k = 5; b) float x = y = z = 1.0; c) char c = "bonjour"; d) char d = '\n';

2.3. Les appels suivants d'affichage vont-ils correctement s'exécuter ? A défaut, comment les corriger ? int i = 7, j = 8; char c = 'a'; float x = 3.5;

cout << i= , j= ,c << (i,j,c) ; cout << " i= , j= ,c "<< (i,j,c) ; cout >> " i= " >> i ; cout << " i= " << i ; cin << " i= " << i ; cout >> " i= " >> x ;

2.4. Corriger le programme suivant pour qu’il s’exécute correctement. Main() float k; { int i, j=k ;

cout << " i= " << i << "alors entrer la valeur de j " << j ;

cin << " j= " << j ; }

Ce qu’il faut au moins retenir :

• Avant d’utiliser cin pour la saisie et cout pour l’affichage, il faut d’abord

inclure la librairie standard I/O :

OU

#include <iostream.h> #include <iostream>

using namespace std;

cout << "Quel est la valeur de ton Rayon : " ;

cin >> rayon;

• Une constante est une donnée dont la valeur ne peut pas être modifiée durant

toute sa durée de vie. Il est indispensable d'initialiser la constante au moment

de sa définition avec le mot réservé const :

const int DEBUT = 10 ; // ou const DEBUT = 10 ;

• Avant de manipuler une variable I (I++ , I-- , etc.) ou de l’afficher, il faut la

déclarer et l’initialiser.

Chapitre 3 : Les structures de contrôle

Les structures de contrôle définissent la suite dans laquelle les instructions sont effectuées et la façon de contrôler le déroulement du programme. Dans ce chapitre, nous allons voir ce que met à notre disposition le langage C++ comme structures dites de contrôle de flux. Constatons déjà que la particularité la plus importante des instructions de contrôle en C++ est le fait que les 'conditions ' peuvent être des expressions quelconques qui fournissent une valeur logique vraie (true) ou fausse (false).

3.1. La structure alternative

La structure alternative (ou instruction conditionnelle) permet de ne pas exécuter systématiquement certaines instructions, mais seulement dans certains cas bien prévus par le programmeur. En langage algorithmique une structure alternative peut s’écrire de la manière suivante : si (<expression logique>) // Si l'<expression logique> a la valeur logique vrai, alors // alors <bloc d'instructions 1> // ce bloc est exécuté sinon // Si l'<expression logique> a la valeur logique faux, <bloc d'instructions 2> // ce bloc est exécuté fsi // fin de si Son organigramme se présente comme suit :

La partie sinon est facultative. On peut donc utiliser si sans la partie sinon.

Cette structure alternative se programme en C++ comme suit (if-else):

if ( <expression logique > ) <bloc d'instructions 1> else <bloc d'instructions 2>

if ( <expression logique > ) <bloc d'instructions >

oui non

Suite du programme

Condition

vraie

Bloc

d’instructions 2 Bloc

d’instructions 1

oui

Suite du programme

Condition

vraie

Bloc

d’instructions

non

Chap. 3 : Les structures de contrôle

M.BENJELLOUN Info I S. Informatique

22

La partie <expression logique > peut désigner : • une variable d'un type numérique, • une expression retournant un résultat numérique.

La partie <bloc d'instructions> peut désigner : • un bloc d'instructions compris entre accolades, • une seule instruction terminée par un point-virgule.

Si <expression logique > est vrai <bloc d'instructions 1> est exécuté. Dans le cas contraire c’est <bloc d'instructions 2> qui est exécuté.

Exemple 1 if (i < 10) i++; //La variable i ne sera incrémentée que si elle a une valeur inférieure à 10.

Exemple 2 if (a > b)

max = a; else

max = b;

Le langage possède un opérateur “ternaire” qui peut être utilisé comme alternative à if-else et qui a l'avantage de pouvoir être intégré dans une expression:

<expr1> ? <expr2> : <expr3>

L’exemple 2, en utilisant l’opérateur “ternaire” peut être écrit comme: max = (a > b) ? a : b; // max contiendra a si a est plus grand que b, et b sinon

Exemple 3 if (A-B) cout << "A est different de B " << "\n"; else cout << " A est egal a B " << endl;

Exemple 4 if ( (!j) && (i < 10) && (n!=0) ){

i++; moy = som/n;

cout << " la valeur de i = " << i << " et moy= " <<moy ; } else { cout << "erreur "; i = i +2 ; }

Exemple 5 if ( (i%2) ==1 ) { /* Attention de ne pas confondre == (opérateur logique d'égalité)

et = (opérateur d'affectation) */

cout <<"Introduisez un entier j = "; cin >> j ; cout <<"division par 2 = " << j/2.0 << endl; } else{ cout << "Introduisez un caractere C : " ; cin >> k ; cout << " i = " << i << endl << " C = " << k << endl;

}

Chap. 3 : Les structures de contrôle

M.BENJELLOUN Info I S. Informatique

23

Remarque importante :

Rappelez-vous que, x = 0 est une expression valide en C++ qui affecte à x la valeur zéro et qui retourne la valeur affectée, c'est-à-dire zéro. Il est donc parfaitement légal d'écrire :

if (x = 0) { /* traitement */ ...

}

Malheureusement, if(x=0) a pour effet : quelque soit la valeur de x, elle deviendra zéro.

Il faut donc écrire : if (x = =0) {

/* traitement */ ...

}

Parfois, on a besoin de traiter des décisions emboîtées: si ceci est vrai, faire cela; sinon, si ceci est vrai, faire cela,… On imbrique alors plusieurs instructions if-else l'une dans l'autre. En combinant plusieurs structures if-else en une expression nous obtenons une structure qui est très courante pour prendre des décisions entre plusieurs alternatives:

Les structures de décision imbriquées s'écrivent par la construction abrégée:

if ( <expr1> ) <bloc1> else if (<expr2>) <bloc2> else if (<expr3>) <bloc3> else if (<exprN>) <blocN> else <blocN+1>

Les expressions <expr1> ... <exprN> sont évaluées du haut vers le bas jusqu'à ce que l'une d'elles soit différente de zéro. Le bloc d'instructions lié à cette dernière est alors exécuté et le traitement de la commande est terminé. Remarque :

Comme dans tout langage de programmation, il y a lieu d'être très soigneux dans l'indentation (structuration et alignement des instructions et des blocs). Dans le cas des structures if-else cela permet de bien cerner à quelle instruction if chaque else se rattache. En C++, par convention, une partie else est toujours liée au dernier if qui ne possède pas de partie else.

Pour éviter des confusions et pour forcer une certaine interprétation d'une expression, il est recommandé d'utiliser des accolades { }.

Chap. 3 : Les structures de contrôle

M.BENJELLOUN Info I S. Informatique

24

Exemple 6

#include <iostream>

using namespace std;

void main()

{

char C;

cout << "Continuer (O)ui / (N)on ?" ;

cin >> C ;

if (C=='O'){

cout << " OKKK " << endl;

……

}

else if (C=='N'){

cout << "Au revoir ...";

}

else{

cout << "\a " << " Il faut choisir entre O et N !" << endl;

}

}

3.2. La structure de sélection (switch)

L'instruction switch est une sorte d'aiguillage, elle est commode pour les "menus". Elle permet de remplacer plusieurs instructions if-else imbriquées. Cette instruction permet de gagner en lisibilité quand le nombre de if-else imbriqués augmente. La variable de contrôle est comparée à la valeur des constantes de chaque cas (case). Si la comparaison réussit, les instructions du case sont exécutées jusqu'à la première instruction break rencontrée.

switch (variable_controle) // au cas où la variable vaut:

{

case valeur1 : ensemble_instruction1; /* cette valeur1(étiquette): exécuter ce bloc d'instructions.*/

break; // sortie du case

case valeur2 : ensemble_instruction2;

break;

case valeur3 : ensemble_instruction3;

break;

..

..

default : ensemble_instructionN; /* cas par défaut si aucune des valeurs précédentes:

exécuter ce bloc d'instructions. Facultatif mais recommandé */

}

variable_controle doit être de type int, short, char ou long. break fait sortir du sélecteur. En l'absence de break, l'instruction suivante est exécutée; on peut ainsi tester plusieurs cas différents et leur attribuer la même instruction. Le bloc "default" n'est pas obligatoire. valeur1, valeur2, …. doivent être des expressions constantes. L’instruction switch correspond à une cascade d’instructions if ...else.

Chap. 3 : Les structures de contrôle

M.BENJELLOUN Info I S. Informatique

25

Exemple 7

char choix;

cout<<"Tapez un entier entre 1 et 3 :";

cin>> choix;

switch(choix)

{

case '1' : cout<<"Vous avez tape 1 "<<endl;

break;

case '2' : cout<<"Vous avez tape 2 "<<endl;

break;

case '3' : cout<<"Vous avez tape 3"<<endl;

break;

default: cout << "Mauvais choix…, Tapez 1 ou 2 ou 3 \n";

}

Notez la présence d’un “:” (deux points) après la valeur des étiquettes case. l’étiquette “ case '1':” intercepte le cas où choix est égal à la valeur '1', le bloc d’instruction simple : cout << "ensemble_instruction1 : case 1" << endl; est alors exécuté, puis l’instruction break nous fait sortir du bloc switch. Cette instruction break (voir 3.4.1.) est indispensable, sans elle l’exécution continuerait linéairement avec le bloc 2 :

cout << "ensemble_instruction2 : case 2" << endl; …

l’étiquette “ case '2':” atteinte si choix est égal à '2'. l’étiquette “ case '3':” atteinte si choix est égal à '3'. l’étiquette “ default : ” atteinte si choix est différent de '1', '2' ou '3'.

Dans le cas où la variable ‘’choix ‘’ est un entier, l’exemple 7 devient:

int choix;

cout<<"Tapez un entier entre 1 et 3 :";

cin>> choix;

switch(choix)

{

case 1: cout<<"Vous avez tape 1 "<<endl;

break;

case 2: cout<<"Vous avez tape 2 "<<endl;

break;

case 3: cout<<"Vous avez tape 3"<<endl;

break;

default: cout << "Mauvais choix…, Tapez 1 ou 2 ou 3 \n";

}

Remarques :

• Une étiquette ne peut pas définir un intervalle de valeur. Par exemple “case 1..5:” ou “case 1&&5:” sont interdits; on écrira à la place plusieurs “case”.

• Les valeurs des étiquettes n’exigent ni d’être ordonnées ni de former une série discrète (1, 2, 3…). • La valeur d’une étiquette doit pouvoir être évaluée au moment de la compilation et être soit une

valeur entière soit un caractère. Ainsi “case 5*j ” ou “case 6.0” ne sont pas correctes. • L’étiquette “default” est optionnelle mais recommandée. • Le break dans l’étiquette “default” n’est pas nécessaire car cette branche est la dernière des

étiquettes case concernée par les instructions.

Chap. 3 : Les structures de contrôle

M.BENJELLOUN Info I S. Informatique

26

3.3. Les instructions répétitives

En C++, nous disposons de trois structures de contrôle d'itérations, appelées aussi structures répétitives ou encore boucles, qui nous permettent d’exécuter plusieurs fois certaines phases de programmes. Les boucles conditionnelles sont:

1) la structure : for (pour…) 2) la structure : while (tant que ...), 3) la structure : do - while (faire… tant que)

Ces boucles ont toutes en commun le fait que l’exécution des instructions à répéter dépend, comme avec les instructions alternatives, d’une condition (<expression logique >).

3.3.1. Instruction for

La syntaxe de l’instruction for est : for (initialisation ; <expression logique > ; itération)

{ liste d'instructions …

}

avec liste d'instructions qui est une instruction simple ou composée.

Exemple 8 int compt ; for (compt = 0 ; compt<=5; compt++) { cout << compt << " " ; }

� résultat de l'exécution 0 1 2 3 4 5

Cette boucle définit un compteur de nom compt, qui évolue de 0 à 5 en s’incrémentant à chaque passage. Ce programme pourrait se traduire par “Initialiser un compteur compt à 0. Tant que le compteur est inférieur ou égal à 5, afficher la valeur de compt, incrémenter le compteur de 1”. Le compteur, une variable entière, prend successivement les valeurs 0, 1, .., 5.

for (compt = 0 ; …..) {

Correspond à la première instruction "initialisation" qui sert à initialiser les variables. Il est recommandé que ces variables de contrôle de l’instruction for soient des variables locales. C’est pour cette raison que l’on peut déclarer une variable de boucle directement dans l'instruction for. Ceci permet de n'utiliser cette variable que dans le bloc de la boucle.

for (int compt = 0 ; compt<=5; compt++){ …

L’expression logique ou test "compt<=5 ", est la condition d'arrêt de la boucle. Elle est évaluée avant d'entrer dans la boucle. Si le test est vrai (true), le bloc est exécuté et donc la boucle se poursuit.

Si par exemple le test était le suivant :

for (compt = 0 ; compt<0; …..) {

Chap. 3 : Les structures de contrôle

M.BENJELLOUN Info I S. Informatique

27

le corps de la boucle ne serait jamais exécuté car compt (initialisé à zéro) ne vérifie pas la condition test (zéro n’est pas strictement inférieur à zéro).

La condition " itération " " compt++" est l'opération à effectuer en fin de boucle sur la variable de contrôle compt. Dans ce cas-ci, c’est une incrémentation. Les trois expressions "initialisation", "<expression logique > " et " itération " peuvent comporter plusieurs instructions pouvant être de types différents. Il suffit pour cela de séparer les différentes instructions par des virgules.

Dans le programme suivant :

#include <iostream>

using namespace std;

void main() {

int i,j, k=9;

float F, r;

for ( i=0, j=5 , r=0.0; (i<j) && (k!=0) ; i++ , j++, k--, r+= 0.2) {

F=(float)i/k ;

F= F + r;

cout << " i= " << i <<" j= " << j << " k= " << k << " F= " << F << endl;

}

}

Programme 3.1

l’expression initialisation contient trois initialisations : i=0, j=5 et r=0.0. Remarquons que ces variables sont de types différents. L’expression <expression logique > combine deux vérifications “i inférieur à j” et “k différent de zéro” grâce à l’opérateur “et logique” noté “&&”. L’expression " itération " modifie quatre variables : i, j, k et r.

#include <iostream>

using namespace std;

void main() {

int I, nombre=1, pairs, impairs, Val_Max;

pairs = 0;

impairs = 0;

cout << "Val_Max ? = ";

cin >> Val_Max ; // Saisie de Val_Max nombres maximum avec décompte des nombres pairs et impairs

for (I = 0; I < Val_Max && nombre != 0; I++ , ((nombre % 2) == 0) ? pairs++ : impairs++ ) {

cout << "\nTapez votre " << I+1 << ( (I== 0) ? "er" : "eme" ) << " nombre (0 pour sortir): ";

cin >> nombre;

cout << "Votre nombre est " << nombre << endl;

}

cout << "\n\nVous avez tape " << pairs << " nombre(s) pair(s)";

cout << " et " << impairs << " nombre(s) impair(s)\n";

}

Programme 3.2

Chap. 3 : Les structures de contrôle

M.BENJELLOUN Info I S. Informatique

28

Dans ce dernier programme, le contenu de la boucle est répété tant que le compteur I est inférieur à Val_Max et que l’utilisateur ne rentre pas la valeur zéro.

Cet exemple montre que l’on peut éventuellement utiliser des tests dans l’expression "itération ". En effet, on incrémente compteur ("I++") et, selon le résultat du test “nombre % 2” qui retourne 0 si nombre est pair et 1 s’il est impair, on incrémente “pairs” ou “impairs”.

Les trois expressions "initialisation", "<expression logique > " et " itération " sont facultatives, ainsi le bloc :

for ( ; ; ) { }

est un bloc d’instruction valide, vide et répété à l’infini comme le montre le programme suivant :

#include <iostream>

using namespace std;

void main() {

char c ;

for ( ; ; ) { // Saisie et affichage d'un caractère tant qu'il est différent de S

cout << "\nTapez votre caractere (S pour sortir): " ;

cin >> c;

if (c =='S') break;

else cout << "Votre caractere est "<< c ;

}

cout << "\n\n Fin du programme \n" ;

}

Programme 3.3

L’exécution de ce programme peut présenter des problèmes liés à l’acquisition des caractères. Elle peut se passer de la manière suivante :

Tapez votre caractere (S pour sortir): q Votre caractere est q Tapez votre caractere (S pour sortir): s Votre caractere est s Tapez votre caractere (S pour sortir): S Fin du programme

Chap. 3 : Les structures de contrôle

M.BENJELLOUN Info I S. Informatique

29

3.3.2. Instruction while

Il s'agit de l'instruction: tant que (<expression logique >) // Condition vraie faire{

BLOC D'INSTRUCTIONS

}

Organigramme:

La syntaxe de l’instruction while est :

while (<expression logique > ) instruction ;

L’instruction de boucle while permet de réaliser un traitement tant qu’une <expression logique > est vraie.

Remarques : L'expression <expression logique > est toujours placée entre parenthèses et les {} ne sont pas nécessaires lorsque le bloc ne comporte qu'une seule instruction. On peut rencontrer la construction suivante:

while (<expression logique >);

terminée par un ‘’ ; ‘’ et sans la présence du bloc d'instructions. Cette construction signifie: "tant que l'expression est vraie, attendre".

Comme toujours, si le bloc instruction est composé de plusieurs instructions, on les placera entre les symboles de début et de fin de bloc: “{” et “}” soit :

while (<expression logique > ) { instruction 1; instruction 2; ... }

Le test se fait d'abord, le bloc d'instructions n'est exécuté que si <expression logique > est vraie. A chaque exécution du bloc, la condition est réévaluée, et ainsi de suite. Dès que l'expression est fausse, on sort de la boucle. Ainsi dans l’exemple suivant, le corps de la boucle sera exécuté 3 fois (valeurs i=3,4,5). A la sortie de la boucle, i vaudra 6.

int i=3; while ( i < 6 ) { i++; }

Condition vraie

oui non

Suite du programme Bloc

d’instruction

Chap. 3 : Les structures de contrôle

M.BENJELLOUN Info I S. Informatique

30

Il est fréquent de voir une partie du travail ou la totalité reportée dans l'expression comme le montre ces codes : char C; cout << "Donner un ou plusieurs caractere(s) : ";

for ( ; ; ) {

cin >> C;

if(C=='&') break;

else cout << C ;

}

for ( ; ; )

if ((C = cin.get()) == '&') break;

else cout << C ; // ou cout.put(C)

while ((C = cin.get()) != '&')

cout.put(C);

cin.get() : permet de saisir un caractère et cout.put(C) permet d’afficher le caractère C.

Exemple 9 L’exemple 8 devient en utilisant la boucle while : int compt=0 ; while (compt<=5 ) { cout << compt << " " ; compt++; }

Cette boucle est équivalente aussi à : int compt=0; for ( ; compt<=5; ) { cout << compt << " " ;

compt++; }

En fait, la boucle while est une boucle for avec une initialisation avant la boucle et l’incrémentation à l’intérieur du bloc d’instructions.

Voici un exemple qui illustre le fonctionnement de la boucle while :

#include <iostream>

using namespace std;

void main()

{

bool sortir = false; // le booléen pour le cas d'arrêt de la boucle

char rep; // un caractère pour stocker la réponse

cout<<"Avant la boucle"<<endl;

while(!sortir) // !sortir équivaut à si sortir == false

{ // TantQue (sortir est false = FAUX) exécuter le corps de la boucle.

cout<<"Dans la boucle"<<endl;

cout<<"Voulez vous quitter (O/N)?"<<endl;

cin>>rep;

if(rep=='O')

sortir=true; // Si sortir deviens true (VRAI) alors on sort de la boucle sans exécuter le corps.

}

cout<<"Apres la boucle"<<endl;

}

Programme 3.4

Chap. 3 : Les structures de contrôle

M.BENJELLOUN Info I S. Informatique

31

Le programme 3.2 devient en utilisant la boucle while :

#include <iostream>

using namespace std;

void main() {

int I, nombre=1, pairs, impairs, Val_Max;

pairs = impairs = I =0;

cout << "Val_Max ? = ";

cin >> Val_Max ;

while (I < Val_Max && nombre != 0) { cout << "\nTapez votre " << I+1 << ( (I== 0) ? "er" : "eme" ) << " nombre (0 pour sortir): ";

cin >> nombre;

cout << "Votre nombre est " << nombre << endl;

I++ ;

((nombre % 2) == 0) ? pairs++ : impairs++ ;

}

cout << "\n\nVous avez tape " << pairs << " nombre(s) pair(s)";

cout << " et " << impairs << " nombre(s) impair(s)\n";

}

Programme 3.5

Programme 3.6 : Saisir une suite de caractères, compter et afficher le nombre total de caractères, de lettres a et b.

#include <iostream>

using namespace std;

void main(){

char c;

int compt_a= 0,compt_b= 0, compt_tot= 0;

cout << "ENTREZ UNE PHRASE: "; // l'utilisateur saisit la totalité de sa phrase

while((c=cin.get())!='\n') { /*lors du 1er passage, cin.get() ne prend en compte que le

1er caractère, les autres sont rangés dans le tampon */

if(c=='a') compt_a++; // et récupérés par cin.get() lors des autres passages

if(c=='b') compt_b++;

compt_tot++ ;

}

cout <<"NOMBRE DE a: "<< compt_a << " de b : " << compt_b << " total : " << compt_tot;

}

Programme 3.6

Voici un exemple d’exécution :

ENTREZ UNE PHRASE: abracadabra magie NOMBRE DE a: 6, de b :2 et le total = 17

Chap. 3 : Les structures de contrôle

M.BENJELLOUN Info I S. Informatique

32

#include <iostream>

using namespace std;

void main() {

const int NMAX = 20;

int NE;

cout << "Entrez une valeur entiere positive inferieure ou egale a " << NMAX << " = ";

cin >> NE;

while((NE<=0)||(NE>NMAX)) {

if (NE<=0)

cout << "j'ai demande une valeur positive, redonnez la valeur : " ;

else

cout << "j'ai demande une valeur inferieure a " << NMAX

<< " redonnez la valeur : ";

cin >> NE;

}

cout << "La valeur de NE= " << NE << " est OK ";

}

Programme 3.7 : un exemple qui interdit d’introduire des valeurs

extérieures à un intervalle prédéfini.

3.3.3. Instruction do ... while

Il s'agit de l'instruction: faire{

BLOC D'INSTRUCTIONS

}

tant que (<expression logique >) ; // Condition vraie

Il est parfois utile de faire le test non pas avant l'entrée de la boucle, mais à la sortie de celle-ci. Dans ce cas, il y aura toujours au moins une itération qui sera effectuée. Le C permet cette option par la construction do-while ( faire…tant que…).

Organigramme:

La syntaxe de l’instruction est :

do { instruction1; instruction2; ….. } while (<expression logique >);

La condition est évaluée après le passage dans la boucle. Il faut noter l’existence du ‘’ ;’’ après while(<expression logique >). Le bloc d'instruction(s) est d'abord exécuté. L'expression de test <expression logique > (entre parenthèses après while) est ensuite évaluée. Si elle est fausse (valeur 0), on sort de la boucle. Si elle est vraie, on itère, en ré-exécutant le bloc d'instruction(s), et ainsi de suite, tant que le test de boucle est vrai.

De même, une boucle sans fin pourrait s'écrire comme: do { instruction(s) ; }while (true);

Programme

oui

Suite du programme

Condition vraie

Bloc d’instructions

Chap. 3 : Les structures de contrôle

M.BENJELLOUN Info I S. Informatique

33

Il est évident qu'on peut écrire avec un while (test avant) ou for, ce qu'on écrit avec un do-while (test après). Avoir les trois constructions facilite cependant la tâche du programmeur et l'écriture de programmes clairs et concis.

Exemple 10 : Nous pouvons réécrire les instructions de l’Exemple 9 comme suit: int compt=0; do { cout << compt << " " ;

compt++; }while (compt<=N );

Exemple 11 bool flag=true; .... do { .... if (....) flag=false; .... } while (flag==true); ....

Programme 3.8 : L’application de l’instruction do ... while au programme 3.5 nous donne :

#include <iostream>

using namespace std;

void main(){

char c ;

int compt_a= 0,compt_b= 0, compt_tot= 0;

cout << "ENTREZ UNE PHRASE: ";

do {

c=cin.get() ;

if(c=='a') compt_a++;

if(c=='b') compt_b++;

compt_tot++ ;

} while(c!='\n');

cout <<"NOMBRE DE a: "<< compt_a << " de b : " << compt_b << " total : " << compt_tot;

}

L’exécution de ce programme donne cette fois-ci :

ENTREZ UNE PHRASE: abracadabra magie NOMBRE DE a: 6, de b :2 et le total = 18

Il faut remarquer qu’ici total = 18, et pas à 17 comme dans l’exemple 3.6. En effet, après la phrase abracadabra magie, l’utilisateur termine par ‘’Enter’’ (‘\n’) afin que la boucle s’arrête. Et comme le bloc d’instructions s‘exécute avant d’arriver à la condition d’arrêt ‘’while(c!='\n');‘’, la variable compt_tot a été incrémentée par l’introduction du caractère (‘\n’).

3.4. Instructions de branchement

Les instructions de branchement transfèrent le contrôle du programme d’une instruction à une autre. Nous examinons ici les instructions (mots réservés):

Break ; continue ; goto ; return

La dernière instruction ‘’ return‘’, sera traitée, pour des raisons pédagogiques, au chapitre des fonctions.

Chap. 3 : Les structures de contrôle

M.BENJELLOUN Info I S. Informatique

34

3.4.1. Instruction break

Elle ne peut s’utiliser qu’à l’intérieur d’une structure for, while, do…while ou switch. Elle provoque l’arrêt avant terme de ces instructions. Placée par exemple dans une boucle, l’instruction break provoque l’interruption immédiate de celle-ci et le contrôle est rendu à l’instruction suivante. Pour illustrer notre propos, considérons par exemple les instructions suivantes contenant deux boucles :

int i=0, j ;

while (i<3){

for ( j=0 ; j<10 ; j++){

if (j== 2){ cout << endl; break;

}

else cout << "i= " << i << " j= " << j <<" ; ";

}

i++;

}

L’exécution de ce code nous donne le résultat suivant : i= 0 j= 0 ; i= 0 j= 1 ; i= 1 j= 0 ; i= 1 j= 1 ; i= 2 j= 0 ; i= 2 j= 1 ;

En effet, chaque fois que j vaut 2, le break provoque l’arrêt de l’instruction for et rend le contrôle pour le i suivant du while.

3.4.2. Instruction continue

Elle ne peut s’utiliser qu’à l’intérieur d’une structure for, while, do…while. Alors que l’instruction break stoppe complètement une boucle simple, l’instruction continue permet de sauter un passage dans la boucle. L’exécution reprend alors au prochain passage dans la boucle. Les instructions suivantes permettent de comprendre l’effet de l’instruction continue :

int i=0, j ;

while (i<3){

for ( j=0; j<4; j++){

if (j== 2){ cout << endl;

continue;

}

else cout << "i= " << i << " j= " << j <<" ; ";

}

i++;

}

L’exécution de ce code nous donne le résultat suivant : i= 0 j= 0 ; i= 0 j= 1 ; i= 0 j= 3 ; i= 1 j= 0 ; i= 1 j= 1 ; i= 1 j= 3 ; i= 2 j= 0 ; i= 2 j= 1 ; i= 2 j= 3 ;

En effet, chaque fois que j vaut 2, continue provoque un saut et passe à j=3 dans la boucle for

Chap. 3 : Les structures de contrôle

M.BENJELLOUN Info I S. Informatique

35

3.4.3. Instruction goto

L’instruction goto provoque un saut à un endroit du programme repéré par une étiquette (label). Le programme continue alors à l’instruction qui se trouve à cet endroit-là. Cette instruction peut parfois être utile, notamment dans les traitements d'erreurs. Mais il est fortement déconseillé de l'utiliser car son emploi abusif amène facilement à des programmes peu structurés et souvent illisibles. Sa syntaxe est la suivante : goto étiquette; où étiquette est une étiquette marquant la ligne destination dans la fonction. Les étiquettes sont simplement déclarées avec la syntaxe suivante : étiquette: Voici un ensemble d’instructions qui illustre le fonctionnement de l’instruction goto:

int i=0, j ;

while (i<3){

for ( j=-2; j<4; j++){

if (j== 0) goto erreur;

else cout << "i= " << i << " j= " << j <<" i/j " << (float)i/j << endl;

}

i++;

}

erreur : cout << "Erreur car j =0 ";

L’exécution de ce code nous donne le résultat suivant :

i=0 j=-2 i/j=0.00 i=0 j=-1 i/j=0.00

Erreur car j = 0

Ce qu’il faut au moins retenir :

1) Il est recommandé, afin de faciliter la lecture et le "débogage", de ne mettre qu'une

seule instruction par ligne dans la source du programme.

Grâce à l'indentation des lignes, on fera ressortir la structure syntaxique du

programme. Il est aussi recommandé de commenter les programmes et d’éviter les

commentaires triviaux.

2) if (i= =j){ // pas i=j car c’est un test et pas une affectation

3)

int i;

cin >> i ;

switch (i) {

case valeur1 :

ensemble_instruction1;

break;

char i;

cin >> i ;

switch (i) {

case 'valeur1' :

ensemble_instruction1;

break;

4) Les trois structures de contrôle d'itérations :

for (initialis. ; ( ?test_vrai); itération)

{

instruction 1;

instruction 2;

}

while ( ?test_vrai) {

instruction 1;

instruction 2; itération ; }

do { instruction 1;

instruction 2; itération ; }while( ?test_vrai);

Chap. 3 : Les structures de contrôle

M.BENJELLOUN Info I S. Informatique

36

Exercices 3.0. Qu’affiche ce code à l’écran ?

int i=1; while(i<5) cout << "Dans while " << i << endl; i++;

3.1. Le test suivant : if( choix == ‘o’ || ‘O’) peut-il remplacer if( choix == ‘o’ || choix== ‘O’) et pourquoi ?

3.2. Que se passe-t-il si on retire tous les "break" de l’exemple 7 et que la valeur de la variable

choix est égale à 1 ? 3.3. Ecrire un programme dont le rôle est de saisir 3 nombres réels au clavier et les afficher par

ordre croissant. 3.4. Ecrire un programme qui calcule les racines d'un polynôme du second degré à coefficients réels.

Examinez toutes les possibilités (racines réelles, complexes, doubles, infinité ou inexistence de solutions).

3.5. Ecrire un programme qui calcule les 100 premiers nombres premiers. 3.6. Ecrire un programme qui calcule le plus grand commun diviseur (PGCD) de deux nombres entiers

positifs par l'algorithme d'Euclide. 3.7. Ecrire un programme qui calcule et affiche la factorielle d’un nombre entre 1 et 10.

Tant que le nombre introduit est <=0 ou > 10, redemandez l’introduction de l’entier.

3.8. Ecrire un programme qui lit N nombres entiers au clavier et qui affiche leur somme, leur produit et leur moyenne. Choisissez un type approprié pour les valeurs à afficher. Le nombre N est à entrer au clavier. Résolvez ce problème en utilisant,

a) while, b) do - while, c) for.

Laquelle des trois variantes est la plus naturelle pour ce problème? 3.9. Modifiez l’exercice 3.7.en ne demandant plus N, mais que la suite de chiffres non nuls entrés

au clavier soit terminée par zéro. 3.10. Ecrire un programme qui calcule et affiche la Table de multiplication par 2, par 5 et par 9. Exemple de solution : Table de 2 : 2 * 0 = 0 2 * 1 = 2 2 * 2 = 4 2 * 3 = 6 2 * 4 = 8

2 * 5 = 10 2 * 6 = 12 2 * 7 = 14 2 * 8 = 16 2 * 9 = 18

2 * 10 = 20

Chap. 3 : Les structures de contrôle

M.BENJELLOUN Info I S. Informatique

37

3.11. Programmez un jeu où l'ordinateur choisira un nombre aléatoire entre 0 et 100 que vous devez

deviner. Pour vous aider, l'ordinateur vous dira si vous êtes trop haut ou trop bas. Dès que vous l'aurez trouvé, il vous dira combien de coups vous avez dû jouer pour arriver à la bonne solution.

3.12. Calculez le Nième terme UN de la suite de FIBONACCI qui est donnée par la relation de

récurrence: U1=1 U2=1 UN=UN-1 + UN-2 (pour N>2) Déterminez le rang N et la valeur UN du terme maximal que l'on peut calculer si on utilise pour UN le type:

- int - long - double - long double

3.13. Ecrire un programme qui calcule la série

X * 3! X2 * 5! X3 * 7! X4 * 9! S = 1 - ______ + ______ - ______ + ______ ….. 1 * 2 2 * 4 3 * 8 4 * 16

L’utilisateur introduira la valeur de X et le nombre de termes à prendre en compte. Le calcul du Nième terme se fera à partir du N-1, …Il n’est pas demandé de chercher le terme général.

Chapitre 4: Les tableaux et les chaînes de caractères

4.1. Les tableaux Les tableaux sont certainement les variables structurées les plus populaires. Un tableau est une collection de variables de même type, appelées éléments ou composantes du tableau. Le C++ alloue une zone contiguë de la mémoire lors de la déclaration d’un tableau. En effet, la dimension maximale du tableau doit être connue avant son utilisation. Cette dimension est une CONSTANTE placée entre [ ] et une fois déclarée, on ne peut redimensionner un tableau. 4.1.1. Tableaux à une dimension Déclaration: Type Nom_Tableau[dim];

Nom_Tableau[0] Nom_Tableau[1] …… …. Nom_Tableau[dim-1]

Pour définir un tableau on utilise un type : le type des composantes du tableau, un identificateur : le nom du tableau, entre crochets [] : la dimension maximale du tableau.

Remarque : Nom_Tableau est un identificateur qui doit correspondre aux restrictions définies

auparavant.

Exemple1 int Tab1[10]; // dim=10 float Tab2[20];

char Tab3[2*15]; // dim=2*15

Cette déclaration signifie que le compilateur réserve 10 places en mémoire de type entier pour ranger les éléments du tableau Tab1, 20 réels pour Tab2 et 30 caractères pour Tab3. dim, dimension du tableau, est nécessairement une valeur numérique constante (10, 20, 2*15=30) et ne peut être en aucun cas une combinaison des variables du programme.

Utilisation: Un élément du tableau est repéré par son indice. En C++, les tableaux commencent à

l'indice 0. L'indice maximum est donc dim-1. Appel: Nom_Tableau[indice]

Exemple2 Tab1[0] = 3; Tab2[3] = 1.015; Tab3[6] = ‘C’;

Le nom d'un tableau (Tab1, Tab2, Tab3) correspond à l'adresse mémoire du premier élément du tableau (Tab1 est équivalent à &Tab1[0], …). Les adresses des autres composantes sont calculées (automatiquement) relativement à cette adresse. Nous avons défini Tab1 comme un tableau avec dix composantes, auxquelles on peut accéder par:

Tab1[0], Tab1[1], …., Tab1[9].

Chap. 4 : Les tableaux et les chaînes de caractères

M.BENJELLOUN Info I S. Informatique

39

Tous les éléments d'un tableau ne sont pas forcément définis. D'une façon générale, les tableaux consomment beaucoup de place en mémoire. On a donc intérêt à les dimensionner au plus juste. 4.1.2. Tableaux à deux dimensions Les tableaux à deux dimensions ou matrices, sont rangés ligne par ligne et considérés comme des vecteurs de lignes. L’accès aux composantes est effectué par des doubles crochets. Déclaration: Type Nom_Tableau [dim1][dim2];

Nom_Tableau [0][0] Nom_Tableau [0][1] Nom_Tableau [0][dim2-1] Nom_Tableau [1][0] Nom_Tableau [dim1-1][0] Nom_Tableau [dim1-1][dim2-1]

Exemple3: int Tab12[4][5]; float Tab34[2][10]; char Tab_Char[5][3]; //déclare un tableau de 5*3=15 caractères

Utilisation : Un élément du tableau est repéré par ses indices. En C/C++ les tableaux commencent aux indices 0. Les indices maxima sont donc dim1-1, dim2-1.

Appel : Nom_Tableau [indice1][indice2]

Exemple4: Tab12 [2][4] = 25; Tab34 [0][5] = 2.79;

4.1.3. Initialisation et réservation automatique Lors de la déclaration d'un tableau, on peut initialiser ses composantes en indiquant la liste des valeurs respectives entre accolades.

Exemple5 int Tab_A[5] = {10, 20, 30, 40, 50}; float Tab_B[5] = { 10.1, 70.3, 30.5, 20.0, 50.4 }; int C_Tab[10] = {1, 1, 0, 1, 1, 1, 0, 1, 0, 1}; int D_Tab[] = {1, 5, 3, 21}; float E_Tab[2] = {1.1, 5.0, 1.2, 1.9}; // erreur int Deux_Dim[2][3] = {{1,5,7},{8,4,3}}; //2 lignes et 3 colonnes

Tab_A définit un tableau du type int de dimension 5. Les 5 composantes sont initialisées par les valeurs respectives 10, 20, 30, 40, et 50. On peut accéder à la première composante du tableau par Tab_A[0], à la deuxième composante par Tab_A[1], . . . , à la dernière composante par Tab_A[4]. Il faut évidemment veiller à ce que le nombre de valeurs dans la liste corresponde à la dimension du tableau. Si la liste ne contient pas assez de valeurs pour toutes les composantes, les composantes restantes sont initialisées à zéro. Mais si les valeurs dans la liste dépassent la dimension du tableau (E_Tab), une erreur se produit lors de l’accès.

Chap. 4 : Les tableaux et les chaînes de caractères

M.BENJELLOUN Info I S. Informatique

40

Si la dimension n'est pas indiquée explicitement lors de l'initialisation, alors le compilateur réserve automatiquement le nombre d'octets nécessaires. Ainsi pour D_Tab, le compilateur lui réserve une dimension de quatre entiers car l'initialisation est faite à la déclaration. Programme 4.1 : Ecrire un programme qui lit la dimension N (<= NE_Max=10) d'un tableau T du type

float, remplir le tableau par des valeurs entrées au clavier. Calculer et afficher ensuite la somme des éléments du tableau.

#include <iostream>

using namespace std;

void main()

{

const int NE_Max =10 ;

float T[NE_Max],Somme = 0.0;

int N;

cout << "Nombre d’elements a saisir inferieur a " << NE_Max<<endl;

cin >>N;

// saisie des nombres

cout << "SAISIR " << N << " NOMBRES SEPARES PAR RETURN: \n";

for(int i=0;i<N;i++) {

cout << "T["<< i+1 <<"] = ";

cin >> T[i];

Somme += T[i] ;

}

cout << "\nLa Somme = " << Somme << endl;

}

Programme 4.2 : Ecrire un programme qui affiche les éléments du tableau à deux dimensions

suivant : int A[3][5] = {{ 0, 1, 2, 3, 4}, {10,11,12,13,14}, { 20,21,22,23,24} };

#include <iostream>

using namespace std;

void main() {

int A[3][5] = { { 0, 1, 2, 3, 4}, {10,11,12,13,14},{ 20,21,22,23,24} };

for(int i=0;i<3;i++){

for(int j=0;j<5;j++){

cout << "A[" << i+1 <<"][" << j+1 << "]= " << A[i][j] << "; ";

}

cout << endl ;

}

}

Important !

L'opérateur d'affectation ne peut affecter un tableau à un autre tableau : int t1[10], t2[10]; t1 = t2; // le compilateur rejettera cette instruction Une telle affectation ne peut se réaliser qu'à l'aide d'une procédure qui réalisera l'affectation élément par élément. En effet, un identificateur ayant le type tableau (t1 et t2) est converti en une valeur constante, on ne peut donc rien lui affecter.

Programme 4.2

Programme 4.1

Chap. 4 : Les tableaux et les chaînes de caractères

M.BENJELLOUN Info I S. Informatique

41

4.2. Les chaînes de caractères

4.2.1. Déclaration de chaînes de caractères En C++ une chaîne de caractères peut être traitée et manipulée à travers un tableau de caractères à une dimension (vecteur de caractères), avec des déclarations dont la syntaxe est de type :

char Ident[dim]; //Ident : Nom de la chaîne de caractères Mais il existe aussi un type spécial string (le mot anglais pour une chaîne de caractères), dont la syntaxe est de type :

string Ident; // Il faut inclure <string> 4.2.2. Initialisation de chaînes de caractères

a) char Ident[dim];

char s[10] = "bonjour !";

'b' 'o' 'n' 'j' 'o' 'u' 'r' ' ' '!' '\0' s[0] s[1] s[2] s[3] s[4] s[5] s[6] s[7] s[8] s[9]

Le caractère nul '\0' (null terminator), de code ASCII 0, est un caractère de contrôle qu’il ne faut pas confondre avec le caractère affichable '0', lequel est codé par le nombre ASCII 48. Il doit être placé obligatoirement en fin de chaîne. Sans lui, la chaîne n'a pas de fin. Une lecture de la chaîne pourrait donc continuer jusqu'au prochain caractère nul en mémoire, et donc bien après la dimension maximale. Dans l’exemple, '\0' est ajouté automatiquement par le compilateur, car le nombre de caractères dans "bonjour !" est inférieur à la dimension du tableau s.

char S[] = {'H','e','l','l','o','\0'};

'H' 'e' 'l' 'l' 'o' '\0' S[0] S[1] S[2] S[3] S[4] S[5]

Le tableau S est initialisé par le programmeur et '\0' est ajouté par lui même. char S[6] = "Hello";

'H' 'e' 'l' 'l' 'o' '\0' S[0] S[1] S[2] S[3] S[4] S[5]

'\0' est ajouté automatiquement par le compilateur, car le nombre de caractères (5) dans "Hello" est inférieur à la dimension du tableau S (6).

char S[8] = "Hello"; 'H' 'e' 'l' 'l' 'o' '\0' ' ' ' '

S[0] S[1] S[2] S[3] S[4] S[5] S[6] S[7] '\0' est ajouté automatiquement par le compilateur à la fin de "Hello";

Chap. 4 : Les tableaux et les chaînes de caractères

M.BENJELLOUN Info I S. Informatique

42

Par exemple le programme suivant affiche Hello:

#include <iostream> using namespace std;

void main() { char S[8]= "Hello"; for(int i=0; i<8; i++){ cout << S[i] << endl; } }

Si par contre la dimension de S est inférieure à la taille réelle de "Hello", c’est-à-dire inférieure à 6, (5+1 pour '\0'), selon le compilateur, une erreur se produit pendant la compilation ou pendant l’exécution.

b) string Ident; // Il faut inclure <string>

Cette construction est équivalente à string Ident = ""; ce qui revient à dire que par défaut une chaîne est créée vide. Il existe toutefois de nombreuses autres façons d’initialiser un string

string s1 = "Bonjour a vous"; string s2 = s1; // s2 contient " Bonjour a vous " string s3(4, 'x'); // équivaut à string s3 = "xxxx" string s4(s1, 4, 8); // s4 contient "our a vo"

Exemple 6: #include <string > … string ST = "Salut";

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

cout << ST[i] << " "; // affichera : S a l u t

}

Exemple 7 : #include <string > … string Phrase;

while ( cin >> Phrase )

cout << "Dans Phrase il y a: " << Phrase << '\n';

cout << "ok: Plus rien : bye!\n";

Si l’utilisateur introduit : Le monde est un livre Le résultat sera : Dans Phrase il y a: Le

Dans Phrase il y a: monde

Dans Phrase il y a: est

Dans Phrase il y a: un

Dans Phrase il y a: livre

� Pouvez-vous nous dire quand le programme affichera " ok: Plus rien : bye! " ? Expliquez.

Programme 4.3

Chap. 4 : Les tableaux et les chaînes de caractères

M.BENJELLOUN Info I S. Informatique

43

4.2.3. Précédence alphabétique et lexicographique

La précédence des caractères dans l'alphabet d'une machine est dépendante du code de caractères utilisé. Pour le code ASCII, nous pouvons constater l'ordre suivant:

. . . ,0,1,2, ... ,9, . . . ,A,B,C, ... ,Z, . . . ,a,b,c, ... ,z, . . .

Les symboles spéciaux (' ,+ ,- ,/ ,{ ,] , ...) et les lettres accentuées (é ,è ,à ,û , ...) se trouvent répartis autour des trois grands groupes de caractères (chiffres, majuscules, minuscules). Leur précédence ne correspond à aucune règle d'ordre spécifique.

Relation de précédence

De la précédence alphabétique des caractères, on peut déduire : '0' est inférieur à 'Z'

et noter '0' < 'Z'

car dans l'alphabet de la machine, le code du caractère '0' (ASCII: 48) est inférieur au code du caractère 'Z' (ASCII: 90).

Précédence lexicographique des chaînes de caractères

En nous basant sur cette relation de précédence alphabétique des caractères, nous pouvons définir une précédence lexicographique pour les chaînes de caractères. Cette relation de précédence suit l'ordre du dictionnaire et est définie de façon récurrente:

a) la chaîne vide "" précède lexicographiquement toutes les autres chaînes ; b) la chaîne A = "a1a2 ... ap" (p caractères) précède lexicographiquement la chaîne B = "b1b2 ... bm" (m caractères) si l'une des deux conditions suivantes est remplie:

1) 'a1' < 'b1' 2) 'a1' = 'b1' et "a2a3 ... ap" précède lexicographiquement "b2b3 ... bm"

Exemple 8

"ABC" < "BCA" car 'A'<'B' "ABC" < "B" car 'A'<'B' "Abc" < "abc" car 'A'<'a' "ab" < "abcdZ" car " " < "cd" " ab" < "ab" car ' '<'a' (le code ASCII de ' ' est 32, et le code ASCII de 'a' est 97)

La classe string fournit les opérateurs suivants : Égal à == ; Non égal à != ; Affectation = ; Addition et affectation += Concaténation des chaînes de caractères ou addition +

Dans le programme 4.4 qui suit, si on déclare les chaînes comme des tableaux de caractères :

char ST1[4] = "ABC"; char ST2[4] = "DEF";

il n’est pas correct d’affecter ou de concaténer comme le montre le programme (ST1 = ST2, ST12 = ST1+ST2 ). En effet, comme les chaînes sont déclarées comme des tableaux, il n'est pas permis de

Chap. 4 : Les tableaux et les chaînes de caractères

M.BENJELLOUN Info I S. Informatique

44

faire des opérations d'affectations ou d’additions sur celles-ci. Elles peuvent être initialisées avec “=” mais pas affectées avec “=”. Ces opérations peuvent se faire caractère par caractère.

#include <iostream>

#include <string> // traitement de string

using namespace std; /* utilisation de l’espace de nommage std pour importer

les symboles de la bibliothèque standard C++ */

void main( ) {

string ST1 = "ABC", ST2 = "DEF", ST12, ST3, ST4;

cout<< "ST1 = " << ST1 << "\t Et ST1[0] = " << ST1[0] << endl;

cout<< "ST2 = " << ST2 << "\t Et ST2[1] = " << ST2[1] << endl;

cout << "\nLe plus grand est : ";

if (ST1>ST2) cout<< ST1;

else cout << ST2;

ST12 = ST1+ST2; // concaténation = fusion des deux chaînes

cout<< "\n\nST12 : ST1+ ST2= " << ST12 << endl;

cout << "\nPosition de la premiere occurrence de DEF dans ST12 = ";

cout << ST12.find("DEF");

ST12 += " PLUS";

cout<< "\n\nST12 : ST12+ PLUS= " << ST12 << endl;

ST1=ST2;

cout<< "\nCopie ST1=ST2 " << endl;

cout<< "ST1 = " << ST1 << " et ST2 = " << ST2 << endl;

cout <<"\nAffecte ST12[1], ,ST12[4] a ST3 : \t\t\t ST3= " ;

ST3.assign(&ST12[1], &ST12[5]);

cout << ST3 << endl;

cout <<"Affecte les deux premiers caracteres de ST3 a ST4 : \t ST4= " ;

ST4.assign(ST3, 0, 2);

cout << ST4 << endl;

cout << "Reinitialise ST12 avec des 'B' : \t\t\t ST12= ";

ST12.assign(4, 'B');

cout << ST12 << endl;

ST12.append("AA");

cout << "ST12.append(AA) :...... \t\t\t ...... ST12= ";

cout << ST12 << endl;

ST12.insert(1, "bcd");

cout << "ST12.insert(1, bcd):...... \t\t\t ...... ST12= ";

cout << ST12 << endl;

ST12.insert(5, "CC").insert(7, "DD").insert(10, "FF");

cout << "ST12.insert(5, CC).insert(7, DD).insert(10, FF) ....... ST12= ";

cout << ST12 << endl;

ST12 += "_FIN";

cout << "ST12 += _FIN ........... \t ...... ST12 = ";

cout << ST12 << endl;

}

Programme 4.4

Chap. 4 : Les tableaux et les chaînes de caractères

M.BENJELLOUN Info I S. Informatique

45

Le résultat du programme 4.4 est le suivant :

ST1 = ABC Et ST1[0] = A ST2 = DEF Et ST2[1] = E Le plus grand est : DEF ST12 : ST1+ ST2= ABCDEF Position de la premiere occurrence de DEF dans ST12 = 3 ST12 : ST12+ PLUS= ABCDEF PLUS Copie ST1=ST2 ST1 = DEF et ST2 = DEF Affecte ST12[1],..,ST12[4] a ST3 : ST3= BCDE Affecte les deux premiers caracteres de ST3 a ST4 : ST4= BC Reinitialise ST12 avec des 'B' : ST12= BBBB ST12.append(AA) :...... ...... ST12= BBBBAA ST12.insert(1, bcd):...... ...... ST12= BbcdBBBAA ST12.insert(5, CC).insert(7, DD).insert(10, FF) ....... ST12= BbcdBCCDDBFFBAA ST12 += _FIN ........... ...... ST12 = BbcdBCCDDBFFBAA_FIN

find est une méthode (fonction) qui permet de rechercher la sous-chaîne correspondant au motif passé en paramètre. Elle retourne l'indice de la première occurrence de ce motif dans la chaîne de caractères, ou une valeur quelconque si le motif n'y apparaît pas. Assign est une autre méthode qui permet d’affecter un certain nombre de caractères d’une chaîne à une autre. append et insert sont d’autres méthodes de manipulation sur les string. Il faut noter que le nombre de méthodes est conséquent, comme par exemple des recherches dans les chaînes de caractères grâce aux méthodes find, find_first_of, find_last_of, et qu’il nous est impossible de toutes les détailler dans ce syllabus.

Programme 4.5 : Ecrire un programme qui vérifie que le nom du fichier introduit par l’utilisateur possède l'extension .dat, et tant qu’il n’est pas conforme, on redemande d’introduire le nom

#include <iostream>

#include <string> // Chaîne de caractères

using namespace std;

void main( ) {

string nom;

int len, size;

bool rep = false;

do{ cout << "\nNom du fichier .dat : " ;

cin >> nom;

len = nom.length(); // nombre de caractères dans nom

size = nom.size(); // nombre de caractères dans nom

cout << "size = " << size << " len = " << len;

if ( nom[len-4]=='.' && nom[len-3]=='d' && nom[len-2]=='a' && nom[len-1]=='t'){

cout << "\nextension OK";

rep = true;

}

else cout << "\nextension Not OK";

cout << "\nVoici votre nom : " << nom << endl;

}while(rep==false);

}

Programme 4.5

Chap. 4 : Les tableaux et les chaînes de caractères

M.BENJELLOUN Info I S. Informatique

46

Exercices

4.1. Les déclarations des tableaux suivants sont-elles correctes ? Pourquoi ? Corrigez les déclarations fausses.

int TabA[5] = {0, 2, 3, 4, 5}; char tab[5] = 'ABCD '; char ST[7] = "Bonjour"; char a[11]= "0123456789\0";

char b[] = "un\ndeux\ntrois\n"; char f[] = "Cette " "phrase" "est coupée";

4.2. Les affectations suivantes sont-elles correctes ? Pourquoi ? char T ,S = 'K', A[10] = "Salut" , B[10]; string C, D="Toto" ; a) T = S; b) T = &S; c) B=A; d ) D= C; e) C= D; f) C= A; g) B= D;

4.3. Écrire un programme qui range au maximum (taille Nmax ) 20 nombres entiers saisis au clavier dans un tableau et qui calcule sa moyenne.

4.4. Écrire un programme qui supprime le premier et le dernier élément d’un vecteur. vecteur initial : Tab � 0 1 2 3 4 5 6 sup. bords � 1 2 3 4 5 sup. bords � 2 3 4

4.5. Écrire un programme qui supprime le milieu du tableau et ajoute son double au début. vecteur initial : Tab � 1 2 3 4 5 sup. Aj � 6 1 2 4 5 sup. Aj � 4 6 1 4 5 4.6. Codez un programme de tri à bulles d'un vecteur de strings.

4.7. Codez un programme de recherche dichotomique d'un nombre dans un vecteur de strings rangés par ordre croissant.

Ce qu’il faut au moins retenir :

Un tableau est une collection de variables de même type et qui commence à l'indice 0.

L'indice maximum est donc dim-1 si on déclare un tableau Tab[dim].

L’affectation d’un tableau à un autre est interdite de la manière suivante :

int TabA[]= {1,2,3} , TabB[]={5,6,7};

TabA = TabB;

&Tab[0] est équivalent à Tab

Une chaîne de caractères se comporte différemment selon le type de déclaration :

string TabA="ABC";

string TabB ="def " ;

TabA=TabB; // Correcte

char TabA[4]= "ABC";

char TabB[4] ="def" ;

TabA=TabB; // Non correcte

Pour cela, il faut inclure <string>

Chap. 4 : Les tableaux et les chaînes de caractères

M.BENJELLOUN Info I S. Informatique

47

4.8. On dispose de deux tableaux A et B, de dimensions respectives N et M, triés par ordre croissant. Fusionnez les éléments de A et B dans un troisième tableau C trié par ordre croissant. Entrelacez A et B dans un quatrième tableau D.

4.9. Ecrire un programme qui construit et affiche une matrice carrée unitaire U de dimension N. Une matrice unitaire est une matrice, telle que:

Uij = 1 si i=j et 0 sinon

4.10. Écrire un programme qui permet de supprimer toutes les valeurs V>7 du tableau T: T

����

4.11. Complétez le code pour que L1 et/ou L2 puissent contenir du texte (des caractères et des espaces). (ex: "bonjour à tous").

string L1, L0; // L1: un ou + Espaces char L2[100]; // L2: un ou + Espaces cout << " L0: " ; cin >> L0; cout << "Première ligne L1: " ; … cout << "\n Deuxième ligne L2: " ; …

4.12. On désire réaliser la gestion d’un réseau routier par l’intermédiaire de tableaux en reliant N villes principales de Wallonie dont on connaît les distances qui séparent les villes entre elles.

Quelques idées de distances sur autoroute entre les villes principales de Wallonie :

Bruxelles – Namur : 63 km Bruxelles – Liège : 97 km Bruxelles – Charleroi : 62 km Bruxelles – Mons : 68 km

Namur – Charleroi : 37 km Namur – Liège : 65 km Namur – Mons : 74 km

Charleroi – Liège : 94 km Charleroi – Mons : 50 km

Liège – Mons : 130 km

On définira Villes un tableau de chaîne de caractères qui contiendra toutes les villes de notre réseau. On définira ensuite un tableau à deux dimensions Distances[ville1,ville2] qui contiendra les distances entre deux villes du tableau Villes. On supposera que les distances sont toutes distinctes entre elles et ont des valeurs entières. 1. écrire le code d’initialisation des tableaux Villes et Distances 2. écrire le code qui permet d’afficher la distance entre deux villes du tableau 3. écrire le code qui permet de trouver les deux villes les plus proches

8 2 7 9 1 9

2 7 1

Chapitre 5 : Les fonctions

5.1. Introduction Jusqu'ici, nous n’avons utilisé que la fonction principale main(). Pour une programmation plus conséquente, nous obtenons ainsi de longues listes d'instructions, peu structurées et par conséquent

peu compréhensibles. En plus, il faut souvent répéter les mêmes suites de commandes dans le texte du programme, ce qui réduit la lisibilité de ce dernier.

Dans la plupart des langages de programmation le principe de modularité est fondamental. Il permet de subdiviser les programmes en sous-programmes, fonctions ou procédures plus simples et plus compactes. A l'aide de ces structures, nous pouvons modulariser nos programmes pour obtenir des solutions plus élégantes et plus efficientes.

La structuration de programmes en sous-programmes ou en modules se fait, entre autre, à l'aide de fonctions. Quand on a un problème, on commence par le décomposer en un ensemble de petits problèmes distincts ou modules, plus simples à traiter, jusqu'à obtenir des tâches élémentaires et

faciles à spécifier et à mettre en œuvre.

Dans ce contexte, une fonction est un module qui désigne une entité de données et d'instructions qui fournissent une solution à une partie bien définie d'un problème. Une fonction peut faire appel à d'autres fonctions, leur transmettre des données et recevoir des données en retour. L'ensemble des modules ainsi reliés doit alors être capable de résoudre le problème global.

Dans ce syllabus, nous insistons sur le fait qu’un programme est constitué d'un ensemble de fonctions, l'une d'entre elles étant la fonction main() ou programme principal. Chaque fonction représente un morceau de programme logiquement cohérent dans lequel on décrit un ensemble d’actions à effectuer.

Nous allons, au cours de ce chapitre, découvrir comment nous pouvons définir et utiliser nos propres fonctions et ce que cette notion nous apporte comme réel confort au niveau de la programmation, de la lisibilité, … Voici quelques avantages d’utilisation des fonctions :

• meilleure lisibilité ; • diminution du risque d'erreurs ;

• réutilisation des modules ; • amélioration du débogage et de la maintenance ; • favorisation du travail en équipe.

5.2. Définition de fonctions En général, le nom d'une fonction apparaît à trois endroits dans un programme:

1) lors de la définition 2) lors de la déclaration 3) lors de l'appel

Comme le langage C++ est déclaratif, normalement tout objet C++ doit être déclaré avant d'être utilisé. Ceci s’applique à toute fonction qui pour pouvoir être utilisée, doit être définie voire

Chap. 5 : Les fonctions

M.BENJELLOUN Info I S. Informatique

49

déclarée. Elle porte un nom, qui est un identificateur semblable à celui des variables. Elle peut avoir ou ne pas avoir de paramètres qui ont chacun un type spécifique selon les besoins. Elle renvoie zéro (mot-clé void) ou une valeur de retour, dont le type doit être également spécifié.

La structure d'une fonction est de la forme suivante :

Type_retourné nom_fonction(liste_paramètres_typés)

{ ... // Corps de la fonction.

return(valeur); // On peut aussi écrire : return valeur;

}

Si Type_retourné vaut void (qui signifie "vide" en anglais), alors la fonction ne renvoie pas de valeur. Dans ce cas, soit on:

• n'utilise pas d'instruction return(valeur) comme dans l'exemple de la fonction Affiche ci-dessous.

• utilise une telle instruction, mais sans (valeur) : ���� return ;

Si Type_retourné est omis lors de la définition de la fonction, le compilateur suppose que la fonction retourne une valeur de type int.

Considérons une nouvelle fonction ‘’Affiche‘’, qui a pour rôle d’afficher les paramètres passés en argument de la fonction. Sa définition peut se faire comme suit:

void Affiche( int x, float y) { //En-tête de la fonction cout << "\nLes valeurs des parametres sont x= " << x << " et y = " << y << endl; }

Type_retourné : void, nom_fonction : Affiche,

liste_paramètres_typés : int x, int y, return : pas de return car Type_retourné : void.

La première ligne de la définition (en-tête de la fonction) contient : • ‘’Affiche‘’ : un identificateur correspondant au nom de la fonction. • ‘’void‘’ : le type de valeur renvoyée, cela indique que la fonction, après avoir effectué sa

tâche, ne renvoie rien à toute fonction qui a appelé ‘’Affiche‘’. • ‘’ (int x, float y)‘’ : liste de paramètres, chacun a un type et un nom. Les parenthèses sont

obligatoires même s’il n y’a pas de paramètres.

Le corps de la fonction dans, cet exemple, ne contient qu’une seule instruction.

L'imbrication de fonctions n'est pas autorisée: une définition de fonction ne doit pas contenir

d’autres définitions de fonctions. L’exemple suivant n’est pas autorisé :

void fonction1() { cout << " fonction1 "; void fonction2( ) { cout << " fonction2 "; } }

Chap. 5 : Les fonctions

M.BENJELLOUN Info I S. Informatique

50

5.3. Instruction return

Considérons maintenant une nouvelle fonction ‘’Somme ‘’, qui additionne deux entiers passés comme

paramètres de la fonction, et retourne la somme. La définition de cette fonction est:

int Somme(int x, int y) //En-tête de la fonction { return (x + y) ; }

Type_retourné : int, nom_fonction : Somme,

liste_paramètres_typés : int x, int y, return : x+y .

Le corps de la fonction dans, cet exemple, ne contient que le mot-clé return. La valeur retournée est une expression ou une valeur (ici : x+y). On remarquera que cette fonction ne modifie pas la valeur des paramètres entrés x et y. Ce n'est pas toujours le cas, il y a des fonctions qui modifient certains de leurs paramètres (voir plus loin).

Quand un paramètre d'entrée n'est pas modifié par la fonction, il est intéressant de faire précéder son type par le mot-clé const. Cette convention permet une vérification par le compilateur de la non-modification de ce paramètre dans le code de la fonction et, évite ainsi des sources potentielles

d'erreur. La fonction Somme peut donc s’écrire aussi comme suit :

int Somme(const int x, const int y) { return (x+y) ; // retourne le résultat de ‘’Somme ‘’ à la fonction appelante }

Le mot-clé return met fin à l’exécution des instructions d’une fonction et rend le contrôle du programme à l’instance appelante.

Considérons la fonction ‘’Sortie ‘’,

int Sortie (const int x, const int y) { if (x>y) return x;

else return y; cout << "x= " << x << " y = " << y << " et somme = " << x+y ; }

Cette fonction possède deux points de sortie. Soit en retournant x si (x>y), soit en retournant y dans le cas contraire. Notons que l’instruction pour afficher x,y,x+y ne sera jamais exécutée. En effet,

return met fin à l’exécution des instructions d’une fonction et rend le contrôle à la fonction appelante.

Chap. 5 : Les fonctions

M.BENJELLOUN Info I S. Informatique

51

5.4. Appel et déclaration de fonctions La déclaration d’une fonction n’est pas obligatoire si sa définition se trouve plus haut que son appel dans le programme. Mais si nous désirons définir les fonctions n’importe où dans le programme sans nous soucier de l’ordre de définitions et appels, il faut déclarer les fonctions. Une déclaration de fonction, qui se termine par un point-virgule, fournit des indications sur le type, le nom et sur d’éventuels paramètres. Mais contrairement à une définition, une déclaration de fonction ne fait que donner au compilateur des renseignements sur l’existence d’une entité et n’en crée pas de nouvelle.

Les fonctions, une fois définies, peuvent être appelées dans le corps d'autres fonctions, ou de la fonction elle-même (récursivité). La définition ou la déclaration d’une fonction doit se faire avant l’appel de celle-ci. Pour cela, dans une expression, on placera le nom de la fonction ainsi que les valeurs des paramètres réels (lors de l'appel) de cette fonction. Ces paramètres peuvent bien sûr (mais ne doivent pas obligatoirement) avoir un nom différent de celui employé lors de la définition de la fonction. Seul le nombre, l’ordre et le type des paramètres doivent correspondre à celui de la définition.

Par exemple, un programme principal pourra faire appel à la fonction Somme, définie dans la section précédente, comme suit:

#include <iostream>

using namespace std;

int Somme (const int x, const int y); //Déclaration de la fonction

void main( ) //Programme de calcul de la somme des deux entiers

{

int S, i; // Variables locales à main

S = Somme (5,6); //Appel de Somme et stockage du résultat dans S.

cout << "La Somme de 5 et 6 : " << S;

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

cout << "\n 2 x " << i << " = " << Somme(i,i);

}

Dans ce programme, nous appelons plusieurs fois la fonction Somme et notamment au cœur d'une boucle for. Ceci est tout à fait légal en C++. La troisième ligne du programme signale au compilateur que nous allons utiliser une fonction Somme, à deux paramètres constants et entiers, fonction qui renvoie un entier: c'est ce qu'on appelle un prototype ou déclaration de fonction, indispensable au compilateur.

Un appel à une fonction peut donc être utilisé dans une expression: c'est une force du C++, car cela facilite la lisibilité du programme (si on n'en abuse pas!).

Les deux programmes (Programme 5.1) sont équivalents et montrent quand il faut utiliser la déclaration des fonctions.

Chap. 5 : Les fonctions

M.BENJELLOUN Info I S. Informatique

52

// Version1

#include <iostream>

using namespace std;

void Affiche (int x, int y);

int Somme (const int x, const int y);

void moyenne(const int x, const int y) ;

void main(void) {

int i;

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

Affiche (i, Somme(i,i)) ;

moyenne( i, i*i) ;

}

}

void Affiche (int x, int y){

cout << "\n 2 * " << x << "=" << y ;

}

int Somme(const int x, const int y) {

return (x+y);

}

void moyenne(const int x, const int y) {

float Moy ;

Moy = (x+y)/2.0 ;

cout << "la moyenne est : " << Moy <<endl ;

}

// Version2

#include < iostream>

using namespace std;

int Somme(const int x, const int y){

return (x+y);

}

void Affiche (int x, int y){

cout << "\n 2 * " << x << "=" << y ;

}

void moyenne(const int x, const int y) {

float Moy ;

Moy = (x+y)/2.0 ;

cout << "la moyenne est : " << Moy <<endl ;

}

void main(void) {

int i;

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

Affiche (i, Somme(i,i)) ;

moyenne( i, i*i) ;

}

}

Programme 5.1

Il faut remarquer que la fonction Somme est passée comme paramètre de la fonction Affiche lors de l’appel de celle-ci. La différence entre les deux versions est, que dans la Version2, la définition des fonctions est avant leur appel, et donc leurs déclarations ne sont pas obligatoires, contrairement à la Version1. L’exécution des deux programmes affiche le même résultat :

2 * 0 = 0 la moyenne est : 0 2 * 1 = 2 la moyenne est : 1 2 * 2 = 4 la moyenne est : 3 2 * 3 = 6 la moyenne est : 6 2 * 4 = 8 la moyenne est : 10 2 * 5 = 10 la moyenne est : 15 2 * 6 = 12 la moyenne est : 21 2 * 7 = 14 la moyenne est : 28 2 * 8 = 16 la moyenne est : 36 2 * 9 = 18 la moyenne est : 45 2 * 10= 20 la moyenne est : 55

5.5. Passage de paramètres Avec la liste des paramètres d’une part et l’instruction return d’autre part, une fonction dispose de deux moyens pour communiquer ou pour échanger des données avec d’autres fonctions. L’instruction return a déjà été traitée; dans cette section, nous allons approfondir l’étude des paramètres des fonctions.

Chap. 5 : Les fonctions

M.BENJELLOUN Info I S. Informatique

53

Les paramètres d'une fonction acceptent les données de l'extérieur et déterminent les actions et le résultat de la fonction. Les valeurs de ces paramètres doivent être connues pour enclencher la fonction. Nous n'admettons donc pas des variables cachées non passées en paramètres (variables globales). En effet, c'est contraire à la lisibilité et à la modularité des programmes. Il ne faut pas non plus mettre des paramètres inutiles, qui pourraient être définis comme variables locales dans une fonction.

Quand on veut définir une fonction, on réfléchit donc sérieusement à son interface nécessaire et suffisant:

• quels sont les paramètres d'entrée (non modifiables) ? • quel type a la valeur de retour ?

• y a-t-il d'autres paramètres nécessaires ? Soit des paramètres d'entrée à modifier, soit des paramètres de retour.

Pour ce dernier point (paramètres de retour, c'est-à-dire modifiés par la fonction), nous aurons besoin, d'un passage différent des paramètres: le passage par référence (voir chapitre 6). En effet, le langage C++ propose deux modes de passages de paramètres:

- le passage par référence, souvent caractérisé par le marqueur syntaxique & ; - le passage par valeur, dans tous les autres cas.

5.5.1. Passage de paramètres par valeur Le passage de paramètres par valeur est en C/C++ le mode standard de transmission des paramètres effectifs à une fonction. Si, par exemple, une variable (tableaux, pointeurs et références exceptés), apparaît en tant que paramètre réel d’une fonction, alors la fonction appelée reçoit plus précisément

une copie de la valeur de la variable passée comme paramètre. La fonction travaille donc sur un duplicata et non sur l’original de la valeur transmise. Autrement dit, les fonctions n'obtiennent que les valeurs de leurs paramètres et n'ont pas d'accès aux variables elles-mêmes.

Donc, les paramètres d'une fonction sont à considérer comme des variables locales qui sont initialisées automatiquement par les valeurs indiquées lors d'un appel. A l'intérieur de la fonction, nous pouvons donc changer les valeurs des paramètres sans influencer les valeurs originales dans les fonctions appelantes.

#include <iostream>

using namespace std;

void Modifier(int v){

v = v *100; // Modifie la copie

cout << "Modifier: v = "<< v; /* Affiche la valeur de v dans la fonction Modifier*/

}

void main(){

int v = 5; // Variable locale à main()

Modifier(v); // Appel de la fonction Modifier

cout << "\n main: v = " << v; /* Affiche la valeur de v après passage dans la fonction Modifier*/

}

Programme 5.2

Chap. 5 : Les fonctions

M.BENJELLOUN Info I S. Informatique

54

L’exécution de ce programme s’effectue de la manière suivante : 1. le programme commence son exécution, comme toujours, par la fonction main(), 2. v est initialisé à 5 puis injecté dans la fonction ‘Modifier’ que l’on enclenche, 3. la copie de v est modifiée dans la fonction, et est multipliée par 100, 4. à l’intérieur de ‘Modifier’ on affiche : Modifier: v = 500, 5. on retourne à la fonction main() juste après l’exécution de la fonction ‘Modifier’, 6. dans main() on affiche : main: var = 5

Ceci montre que le changement effectué sur la variable à l’intérieur de la fonction ‘Modifier’ (500) est resté local à cette fonction. Lorsqu’on retourne dans la fonction main(), la variable a récupéré sa valeur originale (5). Le changement dans ‘Modifier’ n’a aucune influence sur elle. Cela montre bien que la fonction n’a manipulé qu’une copie de la variable et non l’originale.

Un autre exemple classique est donné par le programme 5.3 et dont nous laissons au lecteur le soin d’analyser les résultats.

#include <iostream>

using namespace std;

void affiche (int, int);

void echange (int, int);

void main () {

int i= 1, j=2;

cout << " Dans main(): avant Appel de la fonction echange" << endl;

affiche (i, j);

cout << endl;

cout << " Dans la fonction echange " << endl;

echange (i, j);

cout << endl;

cout << " Dans main(), apres Appel de la fonction echange" << endl;

affiche (i, j);

}

void affiche (int a, int b) {

cout << "\t\t Valeurs i = " << a << " j = " << b << endl;

}

void echange (int a, int b) {

int tmp;

cout << "\tAvant transformation" << endl;

affiche (a, b);

tmp = b;

b = a;

a = tmp;

cout << "\tApres transformation" << endl;

affiche (a, b);

}

Programme 5.3

Chap. 5 : Les fonctions

M.BENJELLOUN Info I S. Informatique

55

L’exécution nous donne :

1. Dans main(): avant Appel de la fonction echange Valeurs i = 1 j = 2

2. Dans la fonction echange Avant transformation Valeurs i = 1 j = 2 Apres transformation Valeurs i = 2 j = 1

3. Dans main(), apres Appel de la fonction echange Valeurs i = 1 j = 2

5.5.2. Tableaux comme paramètres

Les tableaux peuvent être passés comme paramètres de fonctions. Ils ne peuvent cependant pas être retournés comme résultat d'une fonction (par le mot-clé return). Pour déclarer un tableau comme paramètre d'une fonction, il suffit de l'exprimer dans la liste des paramètres. Ici, la longueur n'est pas indispensable, car elle sera définie lors de l'appel à la fonction.

Il est souvent demandé de manipuler des tableaux et de garder le changement effectué. Tel est le cas, par exemple, lorsqu’il faut trier un tableau.

Supposons un tableau défini dans la fonction principale main() : int tab[10] = {5, 8, 3, 1, 9, 0, 4, 6, 7, 2} ;

Nous souhaitons trier ce tableau afin d’obtenir le résultat suivant : {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

La fonction ‘TriaBulles’, utilise l’algorithme du tri à bulles vu au cours. Pour que cette fonction trie convenablement le tableau, il faut lui communiquer, en plus du tableau, son nombre d’éléments ou bien le nombre d’éléments à trier.

Déclaration : void TriaBulles(int tab[], int nbr_elem) ; Définition :

void TriaBulles(int x[], int N) // X ≅ tab et N ≅ nbr_elem { int i, perm=1, tmp; // variables locales à TriaBulles while (perm==1) { perm =0; for (i=0;i<N-1;i++) {

if(x[i] > x[i+1]) { tmp = x[i]; x[i] = x[i+1]; x[i+1] = tmp; perm = 1; } } }

}

void TriaBulles(int x[], int N) { int i, tmp; bool perm=true; while (perm) {

perm =false; for (i=0;i<N-1;i++) { if(x[i]>x[i+1]) { tmp = x[i]; x[i] = x[i+1]; x[i+1] = tmp; perm = true; } }

} }

OU

Chap. 5 : Les fonctions

M.BENJELLOUN Info I S. Informatique

56

L’appel de la fonction dans main() :

#include ... void main( ) { int i, K = 10, tab[10] = {5, 8, 3, 1, 9, 0, 4, 6, 7, 2} ; TriaBulles (tab, K); cout << "Vecteur trie :" << endl; for(i=0;i< Ki++) cout << tab[i] << " , "; cout << endl; }

Le résultat de l’exécution, comme nous l’attendons évidemment, est : Vecteur trie : 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,

Ce qu’il faut remarquer : • les noms des paramètres ne doivent pas forcément être les mêmes à la déclaration, à la

définition et à l’appel, mais correspondent par la position au niveau de leurs types ; • les crochets du tableau lors de la déclaration sont obligatoires ; • le passage du tableau lors de l’appel se fait sans crochets.

Mais ce qu’il faut surtout noter :

Un tableau passé comme paramètre d’une fonction, garde les modifications effectuées

dans la fonction et les transmet à la fonction appelante.

En fait, les tableaux en C/C++ sont toujours transmis à une fonction par adresse ou référence et non par valeur, contrairement aux variables standards. La fonction appelée reçoit toujours, ce faisant, l’adresse du début du tableau, c’est-à-dire l’adresse de son premier élément d’indice zéro. L’appel ‘ TriaBulles (tab, K); ’ est équivalent à ‘ TriaBulles (&tab[0], 10); ’. Donc dans ce cas, on ne travaille pas sur une copie mais sur la variable elle-même puisqu’on accède à son adresse. Plus de détails seront donnés au chapitre 6.

Pour que cet exemple fonctionne pour un tableau de dimension quelconque initialisé par l’utilisateur, la fonction main() deviendra :

void main( ) {

int const NMAX= 20; int tab[NMAX], i, N;

cout << "Donner un nombre (entre 1 et " << NMAX << ") !! ";

cin >> N; for(i=0;i<N;i++) { cout << "nombre " << i+1; cin >> tab[i]; }

TriaBulles (tab, N); cout << "Vecteur trie :" << endl;

for(i=0;i<N;i++) cout << tab[i] << " , ";

cout << endl; }

Chap. 5 : Les fonctions

M.BENJELLOUN Info I S. Informatique

57

5.6. Visibilité des variables Jusqu’à présent, nous n’avons vu que des variables locales. Ces variables, déclarées au début d'une fonction ou de main(), ne sont connues et visibles qu’à l'intérieur de la fonction où elles sont déclarées. Elles ne sont pas automatiquement initialisées (sauf si on le fait dans le programme) et elles perdent leur valeur à chaque appel à la fonction. Nous pouvons définir des variables globales qui sont déclarées au début du programme. Elles sont connues de toutes les fonctions du programme. Ces variables sont initialisées à 0 au début de

l'exécution, sauf si on les initialise à une autre valeur. En général, les variables globales sont déclarées immédiatement derrière les instructions #include au début du programme.

L’exemple suivant montre quelques erreurs dues à la confusion de l’utilisation de variables locales et globales :

#include < iostream>

using namespace std; int globale; // variable globale void fonc(int x); void main(void) {

int i = 5, j; //locales à main float f = 2.8, g;

d = 3.7; // (0) cout << " valeur de j= " << j ; // (1)

cout << "\nglobale = " << globale ; // (2)

fonc (i); } void fonc(int v) { double d, f ; //locales à fonc (3) i++; // (4) globale --; // (5)

f = 0.0; }

Explications :

(0) : ‘d ’ est une variable locale à la fonction ‘fonc’ et ne peut être initialisée dans main(). (1) : ‘ j ’ variable locale à main() n’est pas initialisée, donc le ‘cout’ affiche n’importe quelle

valeur. (2) : ici ce n’est pas une erreur car ‘globale’ est une variable globale et est initialisée

automatiquement. (3) : ‘ f ’ est une variable locale à ‘fonc’ différente de la variable locale à main() ‘ f ’. (4) : ‘ i ’ ne peut être incrémenté ici car c’est une variable locale à main(). (5) : ‘globale’ peut être décrémenté car c’est une variable globale connue dans toutes les

fonctions du programme.

Chap. 5 : Les fonctions

M.BENJELLOUN Info I S. Informatique

58

Voici un programme qui trace la valeur des variables locales et celle d’une variable globale.

#include <iostream>

using namespace std;

int g; // variable globale

void affichage(int un, int deux) {

cout << un << " " << deux << " " << g << endl;

}

void fonc(int un, int deux){

affichage(un, deux);

un += 2;

deux += 2;

g += 2;

affichage(un, deux);

}

void main(void) {

int i = 5, j; //locales à main

affichage(i, j);

j=10;

fonc(i, j);

affichage(i, j);

}

Ce programme produit le résultat suivant :

5 -858993460 0 5 10 0 7 12 2 5 10 2

Conseils :

• Il faut faire attention à ne pas masquer involontairement des variables globales par des variables locales du même nom.

• Les variables globales sont à utiliser avec précaution car la modularité d'un programme peut en souffrir et la lisibilité peut en être diminuée.

L'utilisation de variables globales devient inévitable, si • plusieurs fonctions qui ne s'appellent pas ont besoin des mêmes variables ; • plusieurs fonctions (voire toutes) d'un programme ont besoin du même ensemble de variables.

Ce serait alors trop encombrant de passer toutes les variables comme paramètres d'une fonction à l'autre.

En langage C/C++, nous pouvons allonger la durée de vie d'une variable locale en la déclarant static. Lors d'un nouvel appel à la fonction, la variable garde la valeur obtenue à la fin de l'exécution précédente. Une variable static est initialisée à 0 lors du premier appel à la fonction. Le programme suivant montre l’effet de déclarer une variable comme static.

Programme 5.4

Chap. 5 : Les fonctions

M.BENJELLOUN Info I S. Informatique

59

#include <iostream>

using namespace std;

void fonc_Static();

int globale; // variable globale

void main(void){

int i; // variable locale

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

globale--;

fonc_Static();

}

}

void fonc_Static(){

static int i; // variable static cout <<"\n globale= " << globale << " et static : "<< i;

i +=2;

}

L’exécution du programme affiche :

globale= -1 et static : i=0 globale= -2 et static : i=2 globale= -3 et static : i=4 globale= -4 et static : i=6 globale= -5 et static : i=8

5.7. Récursivité Le langage C++ permet qu'une fonction s'appelle elle-même de manière récursive. Les exemples classiques sont la tour de Hanoï et le calcul de la factorielle d’un nombre, qui peut être défini comme:

int fact (int n ) { if ( n < 0) return –1; //code d'erreur else if ( n == 0 ) return 1; // 0! = 1

else return n*fact( n-1 ); // n! = n*(n-1)! }

Nous n'insisterons pas ici sur les avantages et inconvénients de la récursivité. Disons simplement, qu'en matière d'écriture de programme, la récursivité est un outil puissant et de grande souplesse, indispensable d'ailleurs dans le traitement de structures de données dynamiques.

5.8. Surcharge des fonctions La surcharge de fonction consiste à définir des fonctions ayant le même nom, mais un type et/ou un

nombre d'arguments différents. Cette technique permet donc de définir des fonctions faisant ou non le même traitement dans des contextes différents. Par exemple on peut définir int Somme(int, int) et float Somme(float,float), deux fonctions de même nom ayant une implémentation différente suivant le type de données, c'est le compilateur qui détermine la fonction à appeler d’après le nombre et le type des arguments d’appel de la fonction. Cependant, on ne peut pas surcharger deux fonctions ayant exactement les mêmes types d'arguments mais retournant un type différent.

Programme 5.5

Chap. 5 : Les fonctions

M.BENJELLOUN Info I S. Informatique

60

#include <iostream>

using namespace std;

int Somme(int x, int y){

cout << "Dans la fonction int " <<(x+y) << endl;

return (x+y);

}

float Somme(float x, float y){

cout << "Dans la fonction float "<< (x+y) << endl;

return (x+y);

}

void Somme(const int x, const int y, const int z){

cout << "Dans la fonction void "<< (x+y+z) << endl;

}

void main( ) {

int i=2, j=3;

float k=2.3, z=2.2;

cout <<"Retour de la fonction int " << Somme(i,j) << endl;

cout <<"Retour de la fonction float "<< Somme(k,z)<< endl;

Somme(1,2,3);

}

5.9. Paramètres par défaut

A chaque paramètre déclaré dans un prototype ou dans une définition d’une fonction correspond une

valeur que le programme doit lui passer. Les prototypes qui déclarent une valeur par défaut pour le paramètre font exception. Une valeur par défaut est une valeur utilisée lorsque aucune autre n'est fournie.

Lorsqu'un paramètre est fréquemment appelé avec une valeur donnée, il serait intéressant de pouvoir l'omettre et simplifier ainsi l'appel à la fonction. C++ procure cette facilité en nous autorisant à fournir une valeur par défaut aux paramètres. Cette valeur doit être précisée dans la déclaration de la fonction. Elle ne doit pas apparaître en revanche lors de la définition. Une valeur par défaut est une valeur utilisée lorsque aucune autre n'est fournie.

Les déclarations suivantes sont équivalentes : void fonct(int) ; void fonct(int x) ; void fonct(int x=5) ; void fonct(int =5) ;

Le programme suivant illustre l’effet des paramètres par défaut :

#include <iostream> using namespace std;

void affiche(int un = 1, int deux = 2, int trois = 3) { cout << un << ' ' << deux << ' ' << trois << '\n'; }

void main () { affiche(1, 2, 3); affiche(100, 200); affiche(1000); affiche(); }

L’exécution nous donne : Dans la fonction int 5

Retour de la fonction int 5

Dans la fonction float 4.5 Retour de la fonction float 4.5

Dans la fonction void 6

Programme 5.7

Le résultat du programme

5.7 est le suivant :

1 2 3 100 200 3 1000 2 3

1 2 3

Programme 5.6

Chap. 5 : Les fonctions

M.BENJELLOUN Info I S. Informatique

61

Pour fixer les idées sur le fonctionnement des paramètres par défaut dans les fonctions, voici un autre programme.

#include <iostream>

using namespace std;

int Volume(int longueur, int largeur = 3, int hauteur = 1);

void main(){

int longueur = 10;

int largeur = 5;

int hauteur = 2;

int volume;

volume = Volume(longueur, largeur, hauteur);

cout << "Le premier volume est egal a : " << volume << "\n";

volume = Volume(longueur, largeur);

cout << "Le deuxieme volume est egal a : " << volume << "\n";

volume = Volume(longueur);

cout << "Le troisieme volume est egal a : " << volume << "\n";

}

Volume(int longueur, int largeur, int hauteur){

return (longueur * largeur * hauteur);

}

Ce programme produit le résultat suivant : Le premier volume est egal a : 100 Le deuxieme volume est egal a : 50 Le troisieme volume est egal a : 30

Ce qu’il faut au moins retenir :

Un programme C++ ne doit pas nécessairement commencer par la fonction main(), et ce sera

d’ailleurs rarement le cas. La seule chose qui compte pour qu'un programme ait un point d'entrée

et soit donc exécutable, est qu’il y ait une fonction main() quelque part dans ce programme.

return met fin à l’exécution des instructions d’une fonction et rend le contrôle à la fonction

appelante.

Une fonction ne peut pas modifier la valeur d’une variable, locale à main() ou à une autre fonction,

de type standard (int , char, …) passée en paramètre par valeur. Par contre, elle peut, si nous le

désirons, modifier les éléments d'un tableau passé en paramètre, car c’est un passage par adresse.

Les tableaux peuvent être passés comme paramètres de fonctions. Ils ne peuvent cependant pas être

retournés comme résultat d'une fonction (par le mot-clé return).

Une variable globale est une variable connue de tout le programme, contrairement à une variable

locale qui est connue uniquement dans la fonction où elle est déclarée.

On n’utilise des variables globales que lorsque c’est nécessaire. En effet, c'est contraire à la lisibilité

et à la modularité des programmes.

Le langage C++ permet d’utiliser la surcharge des fonctions. Ces dernières portent le même nom

mais pas forcément le même nombre et/ou type des arguments. Le but est qu’elles exécutent des

opérations différentes.

Programme 5.8

Chap. 5 : Les fonctions

M.BENJELLOUN Info I S. Informatique

62

Exercices

5.1. Transformer l’exercice 3.6. en utilisant une fonction dont le seul rôle est de calculer le PGCD de deux nombres passés comme paramètres de la fonction.

5.2. Transformer l’exercice 3.10. en utilisant une fonction " multiplication" .

5.3. Ecrire une fonction MIN et une fonction MAX qui déterminent le minimum et le maximum de deux nombres réels. Ecrire un programme se servant des fonctions MIN et MAX pour déterminer le minimum et le maximum de cinq nombres réels entrés au clavier.

5.4. Ecrire un programme qui calcule le produit scalaire de deux vecteurs d'entiers U et V de même dimension. Utiliser une fonction de saisie pour remplir les deux vecteurs. Exemple:

( 5 2 -4 ) * ( 1 -3 6 ) = 5*1 + 2*(-3) + (-4)*6 = -25

5.5. Coder un programme qui range au maximum (taille Nmax ) 20 nombres entiers saisis au clavier dans un tableau. Il doit gérer en boucle le menu de choix suivant :

A- Saisie et affichage B- Moyenne C- Suppression du Max et affichage D- Suppression du Min et affichage E- Ajout d’un entier à une position donnée F- Supprimer les doublons Q- Quitter

Le point A sera traité par deux fonctions : Saisie : la saisie au clavier des entiers et leur rangement dans le tableau. Dans cette fonction on demandera le nombre d’éléments (NE<= Nmax) à saisir.

Affichage : affichage des données.

Le point B sera traité par une fonction :

Moyenne : fonction de calcul de la moyenne du tableau (avec affichage du résultat).

Le point C sera traité par trois fonctions : Max_elem : une fonction qui retourne la position du maximum des valeurs du tableau. Supprimer : supprime le Max du tableau. Affichage : affichage des données.

Le point D sera traité par trois fonctions : Min_elem : une fonction qui retourne la position du minimum des valeurs du tableau. Supprimer : supprime le Min du tableau. Affichage : affichage des données.

Le point E sera traité par deux fonctions : Ajout : c’est une fonction où on demande à l’utilisateur d’introduire l’entier et la position. Puis vous insérez l’entier dans le tableau à la position indiquée. Affichage : affichage des données.

Le point F sera traité par une fonction : Doublons : fonction qui supprime les doublons du tableau (avec affichage du résultat).

Chapitre 6: Les pointeurs et les références

Les pointeurs sont des variables qui jouent un rôle primordial en C et en C++. Ils nous permettent

d'écrire, avec une certaine discipline, des programmes clairs, compacts, efficients et fournissent

souvent des solutions raisonnables à un problème. Cependant, il faut être très attentif en utilisant cet

outil puissant, car les pointeurs ne doivent pas être employés négligemment. En effet, il est assez

facile de créer des pointeurs qui pointent ‘n'importe où’ et malheureusement parfois sur des zones

mémoires utilisées par le système.

Les références sont des identificateurs synonymes d'autres identificateurs, qui permettent de

manipuler certaines notions introduites avec les pointeurs plus souplement. Elles n'existent qu'en C++.

6.1. Un pointeur: qu'est-ce que c'est ?

La mémoire est découpée en octets. Chaque octet est repéré par son numéro d'ordre, ou adresse

(emplacement en mémoire). Un pointeur ‘pointe’ vers un octet en indiquant son adresse (une valeur). Ici, pointer signifie « faire référence à ». En pratique, un pointeur est une variable qui contient une valeur de type ‘adresse’ et pas la valeur d'un des types vus précédemment. C’est donc une variable dont le contenu est l'adresse mémoire d'une autre variable (objet), c'est-à-

dire la position en mémoire de cette autre variable (objet). Un pointeur permet donc de retrouver la

valeur d'une variable (par son adresse) et d'y accéder. On dit aussi que le pointeur renvoie ou ‘pointe’

vers la variable concernée, cela via son contenu consistant en l’adresse de cette variable. La variable

(objet) pointée peut être référencée via le pointeur.

Exemple 1

Pointeur p: valeur 5A0F3 (adresse hexadécimale de la case mémoire où se trouve la valeur de i)

Adresse 5A0F3: valeur 35 (correspondant à la valeur d'un entier i)

Si un pointeur ‘’p‘’ contient l'adresse d'une variable ‘’i ‘’, on dit que 'p pointe sur i'.

Remarque Les pointeurs et les noms de variables ont le même rôle: Ils donnent accès à un emplacement

dans la mémoire interne de l'ordinateur. Il faut quand même bien faire la différence:

• un pointeur est une variable qui peut ‘pointer’ sur différentes adresses ; • le nom d'une variable reste toujours lié à la même adresse.

Il faut signaler que l’on parle parfois de pointeur dont la valeur est constante (adresse constante).

Par exemple, les noms des tableaux sont des pointeurs constants équivalents à l’adresse de la

première composante du tableau concerné.

35

Mémoire

Adresses mémoires 60C19 5A0F3

i p

5A0F3

Noms des variables

Valeurs des variables

Chap. 6: Les pointeurs et les références

M.BENJELLOUN Info I S. Informatique

64

6.1.1. Déclaration de pointeurs

Les pointeurs se déclarent, comme les variables, en donnant le type de l'objet vers lequel ils devront

pointer. Cependant, pour différencier une variable quelconque d’une variable « pointeur », le type de

base (char, float, int, …) est toujours suivi d’une étoile ‘ * ’. Cela se fait par l'instruction:

type* nom_du_pointeur;

Exemple 2 : on peut déclarer les pointeurs suivants : int *p, *q, r ; // 2 pointeurs p et q vers des entiers et 1 entier r

char *s; // 1 pointeur s vers un caractère

float *pr; // 1 pointeur pr pointant sur un float

int **tab; // 1 pointeur tab pointant sur un pointeur qui pointe sur un int

L'opérateur * désigne en fait le contenu de l'adresse, c’est l’opérateur de déréférencement.

6.1.2. Valeurs pointées et adresses

La manipulation des pointeurs nécessite une bonne maîtrise des deux opérateurs & et *.

Lors de l’utilisation des pointeurs, nous avons besoin :

- d'un opérateur 'adresse de': & pour obtenir l'adresse d'une variable, - d'un opérateur 'contenu de': * pour accéder au contenu d'une adresse, - d'une syntaxe de déclaration pour pouvoir déclarer un pointeur.

Pour fixer les idées, prenons l’exemple 3:

int i = 35;

int *p; // déclaration : p est un pointeur pointant sur un entier

p = &i; /* initialisation du pointeur p, affecte l'adresse de la variable i.

p pointe maintenant vers la zone mémoire où est stockée la variable i*/

cout << *p; //affiche la valeur pointée par p, donc 35

p contient l’adresse de la variable i (p = &i ) alors que *p contient le contenu de la variable, donc 35. On peut donc accéder à la valeur de la variable en passant par son adresse et en utilisant l’opérateur

‘’contenu de * ‘’.

Attention : int *p;

*p = 100 ; // provoque une erreur à l’exécution. Pourquoi ? Remarque : Comme pour les autres variables, la valeur d’une variable pointeur est indéfinie lors de la

déclaration et le reste tant qu’on ne lui affecte pas explicitement une valeur.

Il est même possible de modifier les valeurs pointées comme le montre les instructions de l’exemple 4.

Chap. 6: Les pointeurs et les références

M.BENJELLOUN Info I S. Informatique

65

Exemple 4 :

int i = 35, j;

int *p, *q;

p = &i; // manipulation de i via p; p=&i = 0x0012ff7c � *p = 35

j = *p; // j = 35

*p = 10; // *p=i=10

i= 20; // i=*p=20

(*p)++; //équivalent à i++, donc i=*p= 21. Notez l’importance des parenthèses !!!

q = p; // p et q pointent maintenant ensemble vers la même adresse 0x0012ff7c

i=50 ; // i=*p=*q= 50

*(q+1)= 29; // on range 29, 4 cases mémoire plus loin *p=*q=i reste à 50

- p pointe sur i,

- le contenu de i (référencé par *p) est affecté à j, et

- le contenu de i (référencé par *p) est mis à 10,

- le contenu de p est mis à 20,

- le contenu de p est mis à 21,

- le contenu de q est mis à 21.

- p et q pointent vers la même adresse et leur contenu est égal à 21.

- le contenu de p et de q est mis à 50.

- le contenu de p et de q n’est pas changé par *(q+1)= 29; .

Les opérateurs * et & ont la même priorité que les autres opérateurs unaires (la négation !,

l'incrémentation ++, la décrémentation --). Dans une même expression, les opérateurs unaires *, &, !,

++, -- sont évalués de droite à gauche.

Nous conseillons au programmeur de faire attention lors de l’utilisation des opérateurs unaires ++ et -

-, notamment avec les pointeurs. Ainsi *p++ est différent de (*p)++.

Si au lieu de :

i= 20; // i=*p=20

(*p)++; // est équivalent à i++, donc i=*p= 21

on avait écrit :

i= 20; // i=*p=20

*p++; // est équivalent à *p puis p++ : i=20 *p= ? le nouveau p ne pointe pas vers i // mais bien sur la case mémoire située une adresse plus loin que i.

Comme les opérateurs unaires * et ++ sont évalués de droite à gauche, sans les parenthèses, le pointeur p serait incrémenté, donc ne pointerait plus sur i, mais sur une valeur peut-être inconnue de l’utilisateur.

Chap. 6: Les pointeurs et les références

M.BENJELLOUN Info I S. Informatique

66

6.2. Une référence: qu'est-ce que c'est ?

Un type référence définit une variable dont la valeur est l’adresse d’une zone mémoire typée. Une

référence vers un objet permet de définir un nouveau nom, un alias, un synonyme, pour désigner

l’emplacement mémoire de l’objet référencé. En tant qu'alias, la référence doit impérativement être

initialisée, avec l'objet référencé.

Une fois initialisée pour référencer un objet donné, une référence ne peut plus être changée pour

référencer un autre objet (variable), puisqu’il s’agit d’un synonyme pour le premier.

Une référence s’exprime en préfixant le nom de la variable avec l’opérateur « & » et toutes les

occurrences de la référence sont implicitement déréférencées pour accéder à la variable référence.

Déclaration de références

Les références se déclarent de la manière suivante :

type &nom_référence = identificateur;

La référence doit être initialisée à la déclaration. Après cette déclaration, nom_référence peut être

utilisé partout où identificateur peut l'être. Ce sont des synonymes.

Exemple5 :

int i = 35, j;

int &ref = i ; /* ref une référence sur la variable i. permet au programme de manipuler i sous un autre nom que celui de sa déclaration. i et ref deux identificateurs qui représentent la même variable */

int &ErRef; // INTERDIT : une référence doit être initialisée

j = ref; // j = la valeur de l'objet référencé par ref� (j = i = 35) ref = 100 ; // ref=i= 100 ; j reste à 35

i= j ; // ref=i=j = 35

6.3. Pointeurs, références et arguments de fonctions

En C, les pointeurs sont fort utiles lors du passage de paramètres modifiables dans des fonctions.

Ceci a pour conséquence de produire un code peu lisible et constitue une source fréquente d'erreurs,

surtout pour les personnes peu habituées à cette gymnastique. Le langage C++ pallie à cette lacune en

introduisant le passage de paramètres par références.

Rappelons ici que le passage des paramètres par valeur implique qu'à l'entrée de la fonction, une

copie de la valeur de ces paramètres est réalisée. La fonction travaille elle-même sur cette copie, et

donc, en aucun cas, ne modifie la valeur originale.

Nous avons vu que si nous voulons sortir les modifications d’une variable vers la fonction appelante,

l’instruction return est à notre disposition. Mais dans le cas où il s’agit de retourner plusieurs

variables, il faut trouver une autre solution.

Chap. 6: Les pointeurs et les références

M.BENJELLOUN Info I S. Informatique

67

Afin d’illustrer l’effet et le fonctionnement des pointeurs et des références, nous reprenons

l’exemple du Programme 5.3.

#include <iostream>

using namespace std;

void affiche (int a, int b) {

cout<<"\t Valeurs i = " << a << " j = " << b << endl;

}

void echange (int, int);

void main () {

int i= 1, j=2;

affiche (i, j);

echange (i, j);

affiche (i, j);

}

void echange (int a, int b)

{

int tmp;

tmp = b;

b = a;

a = tmp;

}

void echange (int*, int*);

void main () {

int i= 1, j=2;

affiche (i, j);

echange (&i, &j);

affiche (i, j);

}

void echange (int *a, int *b)

{

int tmp;

tmp = *b;

*b = *a;

*a = tmp;

}

void echange (int&, int&);

void main () {

int i= 1, j=2;

affiche (i, j);

echange (i, j);

affiche (i, j);

}

void echange (int &a, int &b)

{

int tmp;

tmp = b;

b = a;

a = tmp;

}

Résultats :

Valeurs i = 1 j = 2

Valeurs i = 1 j = 2

Valeurs i = 1 j = 2

Valeurs i = 2 j = 1

Valeurs i = 1 j = 2

Valeurs i = 2 j = 1

Programme 6.1

Partie commune aux trois programmes

Passage des paramètres par valeur

Passage des paramètres par référence de pointeur

Passage des paramètres par référence

En la fonction echange est syntaxiquement correcte pour intervertir les valeurs des variables entières a et b. Malheureusement, codée comme telle, elle n'aura aucun effet! En effet, ce sont des

copies (en variables temporaires locales) des variables a et b qui seront interverties, mais pas les

originaux!

Utiliser l’instruction return peut éventuellement nous sortir une valeur, mais pas les deux.

Contrairement au passage par valeur, le passage par référence ou par adresse, n'effectue pas de

copie du paramètre: il passe une référence sur celui-ci. En conséquence, une fonction passant des

paramètres par référence ne pourra jamais prendre (en paramètre) une constante, mais uniquement

une variable. Ce mode de passage peut donc être intéressant, surtout si un paramètre est important

2 3 4

1

2

3

4

2

Chap. 6: Les pointeurs et les références

M.BENJELLOUN Info I S. Informatique

68

en terme de taille (un tableau, une structure, ...). C’est le mode qu’il faut utiliser si nous voulons

modifier les paramètres d’une fonction. Ceci se fait de manière explicite, en utilisant soit les

pointeurs soit les références.

Les références et les pointeurs sont étroitement liés. Utiliser une référence pour manipuler un objet

revient exactement au même que de manipuler un pointeur contenant l'adresse de cet objet. Les

références permettent simplement d'obtenir le même résultat que les pointeurs, mais avec une plus

grande facilité d'écriture. En effet, comme le montre le programme, le code est nettement plus clair.

Il est aussi beaucoup plus sûr puisqu'on ne doit plus référencer à l'appel (&a) ni déréférencer dans la

fonction (*a). Le risque d'erreur dans la conception et l'utilisation des fonctions est donc

singulièrement réduit.

C’est pour ces raisons que nous préférons plutôt utiliser les références que les pointeurs. Ce sera le

cas dans ce syllabus lors du passage des paramètres par références.

Nous attirons l’attention sur la manière d’écrire l’en-tête de la définition et de l’appel de la fonction :

Définition : void echange (int &pa, int &pb)

Appel : echange ( x, y);

Programme 6.2 : Cet exemple a été traité auparavant dans le chapitre consacré aux fonctions

(Programme 5.2). Maintenant que nous connaissons les références, ce programme devient :

#include <iostream> using namespace std;

void Modifier(int &v);

void main(){

int v = 5; // Variable locale à main()

Modifier(v); // Appel de la fonction Modifier

cout << "\n main: v = " << v; /* Affiche la valeur de v après

passage dans la fonction Modifier*/

}

void Modifier(int &v){

v = v * 100;

cout << " Modifier: v = "<< v << " son adresse &v = " << &v ;

}

Programme 6.2

L’exécution de ce programme donne le résultat suivant : Modifier: v = 500 son adresse &v = 0x0012FF7C main: v = 500

Donc la modification effectuée par la fonction ‘’ Modifier ‘’ est transmise à la fonction main().

Un autre exemple illustrant la différence entre le passage des paramètres par valeur et par

référence est présenté dans le programme 6.3.

Chap. 6: Les pointeurs et les références

M.BENJELLOUN Info I S. Informatique

69

#include <iostream>

using namespace std;

void affiche(int i, float r, char c){

cout<< "\n int = " << i << " float = " << r << " char = " << c ;

}

void Saisie(int &i, float r, char &cc){

cout << "\n Donner un entier : ";

cin >> i; // 12

cout << " Donner un reel : ";

cin >> r; // 12.13

cout << " Donner un caractere : ";

cin >> cc; // Z

}

void main(){

int N=5;

float f=6.7;

char C = 'A';

affiche(N, f, C);

Saisie(N, f, C);

affiche(N, f, C);

}

Le résultat de ce programme est :

int = 5 float = 6.7 char = A Donner un entier : 12 Donner un reel : 12.13 Donner un caractere : Z int = 12 float = 6.7 char = Z

6.4. Pointeurs et tableaux

Il existe une relation très étroite entre les tableaux et les pointeurs. Cela est dû au fait que le nom

d’un tableau représente un pointeur sur le premier élément du tableau. Comme les tableaux sont

stockés de manière contiguë en mémoire, chaque opération avec des indices de tableaux peut aussi

être exprimée à l'aide de pointeurs. Ainsi, accéder à la composante suivante du tableau peut

s’effectuer en passant à l'adresse suivante.

Rappelons également que, comme paramètre de fonction, un tableau est en fait passé par l'adresse de

sa première composante. Il est donc modifiable, car passé implicitement par adresse.

Afin de pouvoir utiliser l'arithmétique des pointeurs pour manipuler les éléments des tableaux, le C++

effectue les conversions implicites suivantes lorsque nécessaire :

• tableau vers pointeur d'élément ;

• pointeur d'élément vers tableau.

Cela permet de considérer les expressions suivantes comme équivalentes :

Programme 6.3

Chap. 6: Les pointeurs et les références

M.BENJELLOUN Info I S. Informatique

70

int T[N];

int i;

T[i] est équivalent à *(T + i).

T est une constante représentant l’adresse de l’élément T[0] (T=&T[0]). L’expression *T désigne T[0].

L’accès à un élément du tableau peut se faire non seulement par le nom du tableau accompagné d’un

indice, mais aussi par un pointeur manipulé par des opérations spécifiques de pointeurs.

Si le pointeur p repère l'élément d'indice i du tableau T, p + j (de même p-j) est une valeur de type

pointeur qui repère l'élément d'indice i + j (i – j) du tableau T (en supposant qu'ils existent).

Si on a :

const int N = 100; // dim du tableau

int T[N];

int * p ;

p = &T[0]; // p repère le premier élément de T

Dans l’instruction ci-dessus, remarquez que nous rangeons dans p l’adresse du premier élément du

tableau. Il aurait été incorrect d’écrire

p = &T ;

car T est un nom du tableau et est vu par le compilateur comme une constante ; alors que l’opérateur &

s’applique à une variable pour en fournir l’adresse. En revanche, nous aurions pu écrire :

p=T ;

L'expression p + N est valide, mais p - 1 et p + N + 1 ne le sont pas.

Après les déclarations

int T[10], *p;

nous pouvons écrire

p = &T[4];

et utiliser l'opérateur d'indexation sur p, p[0] étant T[4], p[1] étant T [5], etc. p peut donc être

utilisé comme un sous-tableau de T.

Exemple 6 : manipulation de pointeurs :

const int N = 100; // dim du tableau

int T[N];

int *p,*q,*r,*s;

p = &T[0]; /* p repère le premier élément de T équivalent à p=T */

q = p + (N-1); /* q repère le dernier élément de T */

r = &T[N-1]; /* r repère le dernier élément de T */

s = r - (N-1); /* s repère le premier élément de T */

Chap. 6: Les pointeurs et les références

M.BENJELLOUN Info I S. Informatique

71

#include <iostream> using namespace std;

void main(){

int T[6] = { 2, 6, 4, 5, 10, 8 }; int *p;

p = T; // p repère le premier élément de T

cout<< *p << endl ; // affiche 2

p++; cout<< *p << endl ; // affiche 6

p += 4; cout<< *p << endl ; // affiche 8

}

Ce programme peut être représentée par le graphique suivant :

p++ p+=4

p 1000 T 2 6 4 5 10 8

Adresse mémoire 1000 1004 1008 ...

#include <iostream> using namespace std;

void main(){ const int N=6 ;

int T[N] = { 2, 4, 6, 8, 10, 12 }; int *p, i;

cout<< "T = " << T << " &T[0] = " << &T[0] << endl; p =T;

cout<< "for1 ...\n"; for (i=0; i<N ; i++){

cout<< "p+" << i << " = " << p+i << "\tp["<<i<<"]= " << p[i] << "\t *(p+"<<i<<") = "

<< *(p+i)<< endl;

} cout<< "END for1 ...\n";

*p++ = 15;

cout<< "for2 ...\n"; for (i=0; i<N ; i++){

cout<< "T["<<i<<"]="<<T[i]<<"\t p+"<<i<<" = " <<p+i << " p[" << i<<"]=" << p[i] << "\t *(p+"<<i<<") = "

<< *(p+i)<< endl;

} cout<< "END for2 ...\n";

}

Programme 6.4

Programme 6.5

Chap. 6: Les pointeurs et les références

M.BENJELLOUN Info I S. Informatique

72

L’exécution de ce programme donne le résultat suivant : T = 0x0012FF68 &T[0] = 0x0012FF68 for1 ... p+0 = 0x0012FF68 p[0]= 2 *(p+0) = 2 p+1 = 0x0012FF6C p[1]= 4 *(p+1) = 4 p+2 = 0x0012FF70 p[2]= 6 *(p+2) = 6 p+3 = 0x0012FF74 p[3]= 8 *(p+3) = 8 p+4 = 0x0012FF78 p[4]= 10 *(p+4) = 10 p+5 = 0x0012FF7C p[5]= 12 *(p+5) = 12 END for1 ... for2 ... T[0]=15 p+0 = 0x0012FF6C p[0]=4 *(p+0) = 4 T[1]=4 p+1 = 0x0012FF70 p[1]=6 *(p+1) = 6 T[2]=6 p+2 = 0x0012FF74 p[2]=8 *(p+2) = 8 T[3]=8 p+3 = 0x0012FF78 p[3]=10 *(p+3) = 10 T[4]=10 p+4 = 0x0012FF7C p[4]=12 *(p+4) = 12 T[5]=12 p+5 = 0x0012FF80 p[5]=1245120 *(p+5) = 1245120 END for2 ...

6.5. Allocation dynamique de la mémoire

Nous avons vu que l'utilisation de tableaux nous force à réserver plus de places en mémoire que

nécessaire. Si nous générons ces données, dont nous ne pouvons pas prévoir le nombre et la taille lors

de la programmation, il nous faut, en plus de l’utilisation des pointeurs, des moyens pour réserver et

libérer de la mémoire en fonction de nos besoins. Nous parlons alors de l'allocation dynamique de la mémoire.

Pour cette gestion dynamique de la mémoire, le langage C++ fournit des moyens pour allouer et restituer la mémoire. Pour cela, il dispose d'opérateurs spécifiques : new, delete, new[] et delete[].

La syntaxe de ces opérateurs est respectivement la suivante :

new type delete pointeur new type[taille] delete[] pointeur

L’opérateur new permet d'allouer de la mémoire, alors que l’opérateur delete la restitue.

La syntaxe de new est très simple, il suffit de faire suivre le mot clé new du type de la variable à

allouer, et l'opérateur renvoie directement un pointeur sur cette variable avec le bon type. Par

exemple, l'allocation d'un entier se fait comme suit :

int *pi = new int;

La syntaxe de delete est encore plus simple, puisqu'il suffit de faire suivre le mot clé delete du

pointeur sur la zone mémoire à libérer :

delete pi;

Les opérateurs new[] et delete[] sont utilisés pour allouer et restituer la mémoire pour les types

tableaux. L'emploi de l'opérateur new[] nécessite de donner la taille du tableau à allouer. Ainsi, on

pourra créer un tableau de 1000 entiers de la manière suivante :

int *Tableau=new int[1000];

et détruire ce tableau de la manière suivante :

delete[] Tableau;

Chap. 6: Les pointeurs et les références

M.BENJELLOUN Info I S. Informatique

73

Le programme suivant nous donne un aperçu sur la manière d’allouer et de libérer la mémoire:

#include <iostream> using namespace std;

void affiche(int T[], int d, char C[]){

for(int i=0; i<d; i++) cout<< "\n T["<<i<<"] = " << T[i] << " C["<<i<<"] = " <<C[i]; cout << endl;

}

void main(){ int N=6, *T, i;

char *C = new char[N];

T= new int[N]; affiche(T, N, C);

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

T[i]=i; C[i]='A'+i;

}

affiche(T, N, C);

delete[] T; affiche(T, N, C);

delete[] C;

affiche(T, N, C); }

Ce programme produit le résultat suivant :

T[0] = -842150451 C[0] = • T[1] = -842150451 C[1] = • T[2] = -842150451 C[2] = • T[3] = -842150451 C[3] = • T[4] = -842150451 C[4] = • T[5] = -842150451 C[5] = •

T[0] = 0 C[0] = A T[1] = 1 C[1] = B T[2] = 2 C[2] = C T[3] = 3 C[3] = D T[4] = 4 C[4] = E T[5] = 5 C[5] = F

T[0] = -572662307 C[0] = A T[1] = -572662307 C[1] = B T[2] = -572662307 C[2] = C T[3] = -572662307 C[3] = D T[4] = -572662307 C[4] = E T[5] = -572662307 C[5] = F T[0] = -572662307 C[0] = ¦ T[1] = -572662307 C[1] = ¦ T[2] = -572662307 C[2] = ¦ T[3] = -572662307 C[3] = ¦ T[4] = -572662307 C[4] = ¦ T[5] = -572662307 C[5] = ¦

Ce qu’il faut au moins retenir :

Quand une fonction1 appelle une autre fonction2 et que la première a besoin des modifications des variables

passées en paramètres de la seconde, il faut passer les paramètres par référence. Ceci se fait de manière

explicite, en utilisant les ‘&’.

Quand le paramètre de la fonction est une référence, la déclaration et l’appel de la fonction, en comparaison à

un paramètre ‘traditionnel’, se font comme suit :

déclaration : func(int x) � func(int &x)

appel : func( y) � func(y)

Attention aux erreurs suivantes :

int i = 35;

int &p; //Erreur

p = &i; //Erreur

int i = 35;

int &p= i; // OK

….

int i = 35;

int *p;

*p = i; //Erreur

int i = 35;

int *p;

p = &i; //OK

(*p)++ est différent de *p++

Pour réserver de la mémoire à un pointeur, on utilise l’opérateur new. Dans un programme, il est fort

souhaitable d'avoir autant d’opérateurs delete que d’opérateurs new.

Programme 6.6

Chap. 6: Les pointeurs et les références

M.BENJELLOUN Info I S. Informatique

74

Exercices

6.1. Les opérations d’affectation suivantes sont-elles correctes ?

int a,*p ;

a. a=*p ; b. *p=&a; c. *p=*(&a);

d. a=p; e. p=&a; f. a=(int)p;

6.2. Qu’affiche le programme suivant? Pourquoi ? #include <iostream> using namespace std;

void main(){ int *p, x, y;

int &r=x; p = &x;

x = 10; y = *p - 1;

cout <<"\t x= "<<x<< ";\t *p = "<< *p << ";\t r= "<< r << ";\t y=*p-1="<<y << endl;

*p += 1; cout <<" *p += 1 ==>\n\t x= "<<x<< ";\t *p = "<< *p << ";\t r= "<< r << ";\t y= "<<y << endl;

(*p)++; cout <<" (*p)++ ==>\n\t x= "<<x<< ";\t *p = "<< *p << ";\t r= "<< r << ";\t y= "<<y << endl;

r++; cout <<" r++ ==>\n\t x= "<<x<< ";\t *p = "<< *p << ";\t r= "<< r << ";\t y= "<<y << endl;

*p++; cout <<" *p++ ==>\n\t x= "<<x<< ";\t *p = "<< *p << ";\t r= "<< r << ";\t y= "<<y << endl;

}

6.3. Que faut-il ajouter à ce programme pour qu’il puisse fonctionner correctement ?

void main() { int *p ;

*p = 10; cout << " *p = " << *p ;

}

6.4. Transformer l’exercice 5.3. en remplaçant les fonctions MIN et MAX par une seule fonction

MIN_MAX qui détermine le minimum et le maximum d’un vecteur de nombres réels entrés au

clavier. Ces deux valeurs doivent être transmises au main.

6.5. Ecrire la fonction Affiche et la fonction Crypt de cryptage d'un caractère pour que le programme suivant puisse : décaler chaque lettre de Tab de 5, ainsi un a sera remplacé par un f, un b par un g, etc. On ne cryptera que les lettres majuscules et minuscules sans toucher ni à la ponctuation ni à la mise en page. On

supposera que les codes des lettres se suivent de a à z et de A à Z. void main() {

char *p, Tab[80];

cout <<"\n Une phrase ...: "<< endl;

cin >> Tab;

p=Tab;

while(*p)

Crypt(p++);

cout << "\nResultat :";

Affiche(Tab, ??);

}

Exemple : Les sanglots longs des violons de l'automne …

Deviendra : Qjx xfslqtyx qtslx ijx antqtsx ij q'fzytrsj ...

Chapitre 7: Les fichiers

7.1. Généralité

Il est parfois agréable de pouvoir traiter d’autres données que celles fournies à l’aide du clavier

pendant l’exécution du programme, surtout quand leur nombre est élevé. Il arrive aussi que l'on

veuille sauvegarder des données ou des résultats obtenus sous une forme un peu plus permanente qu’un simple affichage à l’écran, ce qui permet de les réexploiter lors d'une prochaine exécution. Ces

problèmes peuvent être résolus en utilisant des fichiers.

Un fichier est un ensemble structuré d'informations stockées sur un support externe (disque dur,

disquette, CD-ROM, DVD, …).

Ecrire ou lire sur une mémoire de masse des données non volatiles, qui restent utilisables après

l'exécution d'un programme, est évidemment fondamental. Nous allons voir dans ce chapitre,

comment nous pouvons manipuler des fichiers : les déclarer, les créer, les ouvrir, lire ou écrire voire

modifier des données s’y trouvant et les fermer.

Un fichier est une suite d'octets. Les informations contenues dans le fichier ne sont pas forcément

de même type (un char, un int, une structure ...)

Un pointeur fournit l'adresse d'une information quelconque.

Dans des fichiers séquentiels, les enregistrements sont mémorisés consécutivement dans l'ordre de leur entrée et peuvent seulement être lus dans cet ordre. Si on a besoin d'un enregistrement précis

dans un fichier séquentiel, il faut lire tous les enregistrements qui le précèdent en se déplaçant (via

un pointeur) depuis le premier.

7.2. Accès aux fichiers

Pour pouvoir manipuler un fichier de données en C++, il faut tout d'abord inclure dans le fichier .cpp

la librairie fstream qui fournit les fonctionnalités de manipulation de fichiers.

#include <fstream>

On y trouve essentiellement :

• la classe ofstream (output file stream) qui permet d’écrire les données dans le fichier ; • la classe ifstream (input file stream) qui permet de lire les données du fichier ; • la classe fstream (file stream) dérivant les fichiers en lecture/écriture.

Ces classes sont pourvues de fonctions membre qui vont nous permettre d’opérer sur les fichiers.

pointeur

Chap. 7: Les fichiers

M.BENJELLOUN Info I S. Informatique

76

7.2.1. Ouverture d’un fichier

La première chose à faire, quand on manipule un fichier, est de l'ouvrir. Pour ce faire, on déclare une

variable en lui associant le nom physique du fichier visé. Cette variable est de type ofstream s’il s’agit d’écriture, de type ifstream s’il s’agit de lecture ou de type fstream s’il s’agit de lecture/écriture.

La méthode la plus simple pour ouvrir un fichier est d’initialiser la variable qui doit lui être associée à

l’aide d’une chaîne de caractères décrivant le chemin d’accès au fichier.

ifstream input("Data.txt"); ofstream output("Res.txt");

"Data.txt" et "Res.txt" sont des chaînes de caractères donnant les noms réels des fichiers

physiques. Cependant, si les fichiers ne sont pas dans le même répertoire que le programme, il faut

indiquer le chemin d'accès. Le caractère '\' s'écrit en C++ '\\', ce qui pourrait donc donner par

exemple:

ifstream input("c:\\labojeudi\\Data.txt"); ofstream output("c:\\labojeudi\\Res.txt");

Cette façon d’ouvrir les fichiers présente plusieurs caractéristiques notables :

• Si le chemin d’accès comporte un répertoire qui n’existe pas, l’ouverture échoue.

• Si le chemin est correct, mais que le fichier n’existe pas, il est créé.

• Si le fichier existe, il est ouvert. S’il s’agit d’ouverture en mode écriture, le contenu

antérieur du fichier est perdu.

Il faut noter qu’il est possible de spécifier le mode d’ouverture. Si par exemple, on désire une

ouverture d’un fichier existant sans perdre le contenu antérieur, on peut utiliser :

ofstream output("c:\\labojeudi\\Res.txt", ios::app);

ios (input-output stream) est une classe de base fournie par le langage C++. Elle définit les

opérations fondamentales d’entrée/sortie.

La constante ios::app (append) signifie que si le fichier existe, son contenu est conservé et toutes les données insérées seront ajoutées en fin de fichier.

Remarques :

Fichier dit « texte », les informations sont codées en ASCII. Ces fichiers sont lisibles par un

éditeur de texte. Le dernier octet de ces fichiers est EOF (End Of File, caractère ASCII spécifique).

Nous n'allons dans ce chapitre, qu'évoquer les fichiers textes à accès séquentiel. Il existe, bien

sûr, d'autres fonctions pour travailler sur des fichiers binaires ou à accès direct. Nous

renvoyons les lecteurs intéressés aux nombreux ouvrages référencés au début de ce syllabus.

Chap. 7: Les fichiers

M.BENJELLOUN Info I S. Informatique

77

La fonction open()

Si l’initialisation d’une variable est une opération particulièrement simple, son inconvénient et qu’elle

ne peut avoir lieu qu’une seule fois. Il arrive pourtant qu’une même variable intervienne dans

plusieurs ouvertures (ou tentatives d’ouvertures…) successives.

Les classes ofstream et ifstream proposent, pour répondre à ce besoin, une fonction membre nommée open() qui peut accepter comme paramètre un chemin d’accès, comme le montre l’exemple suivant:

Ecriture :

ofstream output; output.open("Res.txt");

ofstream output; output.open("Res.txt", ios::app);

fstream output; output.open("Res.txt",

ios::out);

Lecture :

ifstream input ; input.open("Data.txt");

fstream input; intput.open("Res.txt", ios::in);

ios::out et ios::in indiquent respectivement le mode d’ouverture du fichier en écriture ou en lecture.

L’ouverture du fichier est une opération dont le succès ne peut être garanti. Il faut donc tester si

l’ouverture a réussi avant d’effectuer des opérations sur les données du fichier.

On détecte l’échec d’une ouverture de fichier en testant la valeur de la variable que l’on vient de

tenter d’associer au fichier. Ceci est illustré dans les deux exemples suivants:

ofstream output;

output.open("Res.txt");

if( !output) {

cout<<"Erreur d’ouverture du fichier"<<endl;

}

else{

cout<<"Le fichier est bien ouvert"<<endl;

… ;

}

ofstream output;

string nom_fichier ;

do{

cout << "\nNom du fichier : " ;

cin >> nom_fichier ;

output.open(nom_fichier );

} while(!output) ;

!output : indique si l’opération sur le fichier a réussi.

7.2.2. Fermeture d’un fichier

Après l’ouverture d’un fichier et les différents traitements qui s’y rapportent, lorsque vous avez

donc fini d’utiliser ce fichier, vos programmes doivent fermer ce flux en utilisant la fonction

membre close() comme suit : output.close() ; input.close() ;

On peut dire qu'entre les événements open() et close() le fichier est ouvert. Il est important de fermer les fichiers après les avoir utilisés, car seulement un nombre limité de

fichiers peuvent être ouverts simultanément par un programme. De plus, le traitement désiré

(lecture ou écriture) peut être différé à la fermeture du fichier. Sans fermeture de fichier, il

Chap. 7: Les fichiers

M.BENJELLOUN Info I S. Informatique

78

n'est, par exemple, pas certain que les toutes données que l'on voulait écrire aient été réellement

transférées vers le support externe.

7.3. Lecture et écriture

Il y a lieu maintenant de lire ou d’écrire des informations dans les fichiers. Pour être précis, il

importe de signaler que les données lues ou écrites le sont d'abord dans un tampon (buffer) de la

mémoire, avant d'atteindre leur destination souhaitée. Ce tampon de fichier est une zone de

mémoire RAM dans laquelle on range temporairement, avant leur transfert à destination, une

certaine quantité de données lues ou écrites dans un fichier. L’avantage est qu’ainsi on n’a pas besoin

de déclencher une opération d’entrée-sortie spécifique pour chaque donnée lue ou écrite. La gestion

du tampon est à la charge du système qui intervient lorsque le tampon est plein ou lorsqu'on lui

impose de vider le tampon, notamment avec la fonction membre close()

L'ouverture ne donnant qu'un moyen d'accès, la lecture et/ou l’écriture dans des fichiers se fait

respectivement en utilisant les opérateurs >> et/ou << .

Mieux qu’un long discours, nous donnons dans la suite des exemples que nous commentons au fur et a

mesure.

#include <fstream> // pour accès à la classe ofstream using namespace std;

void main() { ofstream ouput; // on déclare une instance de la classe ofstream

ouput.open("fichier.txt"); // on ouvre le fichier fichier.txt

if(ouput) { // if (output != NULL) on vérifie si l'ouverture se passe bien

// ou if ( ! output.fail() ) pas d’erreur cout<<"Pas d Erreur"<<endl;

char caract = 'A';

ouput << caract <<endl; int Entier = 10;

ouput << Entier <<endl; float Reel = 3.14;

ouput << Reel <<endl; double monDouble = 7.12345;

ouput << monDouble <<endl;

ouput.close(); // on ferme le fichier }

else{ // si échec à l'ouverture cout<<"Erreur"<<endl;

} }

Programme 7.1.

Les données dans le fichier "fichier.txt" sont :

A

10

3.14

7.12345

Chap. 7: Les fichiers

M.BENJELLOUN Info I S. Informatique

79

#include <fstream> // pour accès à la classe ifstream using namespace std;

void main() { ifstream input; // on déclare une instance de la classe ifstream

input.open("fichier.txt"); // on ouvre le fichier fichier.txt

if(input) { // on vérifie si l'ouverture se passe bien

cout<<"Pas d Erreur"<<endl;

char caract;

input >> caract ; int Entier ;

input >> Entier; float Reel ;

input >> Reel ; double monDouble ;

input >> monDouble ;

input.close(); // on ferme le fichier

cout << "Car = " << caract << " Entier = " << Entier << " Reel = " << Reel <<" Double = " << monDouble << endl;

} else{ // si échec à l'ouverture

cout<<"Erreur"<<endl; }

}

Programme 7.2.

Ce programme affiche à l’écran les informations suivantes :

Pas -- Erreur Car = A Entier = 10 Reel = 3.14 Double = 7.12345

Il faut remarquer que l’opérateur d’extraction >> reconnaît les passages à la ligne.

Si, par exemple, dans le Programme 7.1. la sauvegarde des données se fait sur une seule ligne,

comme le montre cette instruction :

ouput << caract <<' '<< Entier <<' '<< Reel <<' '<< monDouble <<endl;

alors les données ne sont séparées que par un espace et non par un retour à la ligne. Le fichier

"fichier.txt" aura l’allure suivante :

Ce changement sur le Programme 7.1. n’a pas d’effet sur le Programme 7.2. qui reste valide. En

effet, l’espace constitue un séparateur de données pour l’opérateur d’extraction au même titre que

le passage à la ligne. Cela a cependant des conséquences dans le cas d’une extraction d’une chaîne de

caractères contenant des espaces. En effet, supposons que l’on désire enregistrer un message

comme ceci :

ouput << "Bonjour : Debut du fichier ..."<<endl;

A 10 3.14 7.12345

Chap. 7: Les fichiers

M.BENJELLOUN Info I S. Informatique

80

son extraction se fait naturellement avec :

char TXT[50] ;

input >> TXT ;

Malheureusement TXT ne contiendra que le mot "Bonjour" car après, l’opérateur rencontre le

séparateur espace. Il est évidemment, dans ce cas, possible d’extraire le texte mot par mot, mais cela n’est guère pratique. La classe ifstream possède une fonction membre qui permet de contourner cette difficulté. Il s’agit de la fonction getline(). Donc, pour extraire toute la ligne, on utilisera :

char TXT[50] ; // un tableau

input.getline(TXT,40); // extraire 40 caractères, au max, que l’on transfert dans TXT

cout << TXT; // affiche TXT : Bonjour : Debut du fichier ...

Question :

Si la déclaration char TXT[50] est remplacée par string TXT que deviendra la suite des

instructions ?

Supposons maintenant que le fichier que l’on désire lire soit le suivant :

On suppose que tout ce que l’on a comme information sur ce

fichier, est:

• un titre sur une ligne,

• des données de type entier dont le nombre est inconnu.

Le programme suivant permet de lire ce fichier.

#include <fstream>

using namespace std; void main() {

ifstream input;

input.open("fichier.txt");

if(input) { char buffer[100] ;

input.getline(buffer,100); cout << buffer << endl;

while(!input.eof()){ int i;

input >> i; cout << i << " ; ";

} input.close();

} // end if else{

cout<<"Erreur"<<endl; }

}

Programme 7.3.

Les donnees sont :

1 2 3 4 5

6 7 8 9

10 11 12

13 14

15

Chap. 7: Les fichiers

M.BENJELLOUN Info I S. Informatique

81

L’exécution de ce code affiche :

Les donnees sont : 1 ; 2 ; 3 ; 4 ; 5 ; 6 ; 7 ; 8 ; 9 ; 10 ; 11 ; 12 ; 13 ; 14 ; 15 ;

La fin du fichier ( End Of File) est donnée par la méthode eof(). L’instruction

while(!input.eof()){

signifie : tant que la fin du fichier input n’est pas atteinte, exécuter les instructions entre {…}. Le pointeur sur les données se déplace à chaque input >> i; . Pour ceux qui ne sont pas convaincus, il leur suffit de masquer cette dernière instruction et d'exécuter le programme afin de constater.

Pour finir, on se propose de créer un fichier, nommé par l’utilisateur, qui stocke des noms.

L'utilisateur est invité à introduire au clavier le nom du fichier pour sauvegarder les données ainsi

qu’un certain nombre de noms. Le programme arrête l’acquisition une fois que le nom introduit est ‘’

FIN‘’, puis se chargera de créer le fichier correspondant.

Après avoir écrit et fermé le fichier, le Programme 7.4. va réouvrir le même fichier en lecture et

afficher son contenu.

#include <iostream>

#include <string> #include <fstream>

using namespace std; int main() {

ifstream Lect; ofstream Ecri;

string Nom; char NOM_FICHIER[30];

int i=0;

// Première partie: Créer et remplir le fichier

cout << "Entrez le nom du fichier a creer : " ; cin >> NOM_FICHIER;

Ecri.open(NOM_FICHIER);

if(Ecri) {

cout << "Entrez le Nom No " << i+1 << " : "; cin >> Nom;

while ( Nom != "FIN") { if (i!=0) Ecri << endl;

i++; Ecri << Nom ;

cout << "Entrez le Nom No " << i+1 << " : "; cin >> Nom;

} // while Ecri.close();

} else{

cout << " Probleme d'ouverture de fichier Ecri " << endl; return 0;

} // Deuxième partie: Lire et afficher le contenu du fichier

Lect.open(NOM_FICHIER);

Programme 7.4.

Chap. 7: Les fichiers

M.BENJELLOUN Info I S. Informatique

82

if (Lect){ cout << "\n" << NOM_FICHIER << endl;

i=0; while (!Lect.eof()) {

Lect >> Nom; cout << "Nom " << i+1 << " : " << Nom << endl;

i++; }

Lect.close(); }

else{ cout << " Probleme d'ouverture de fichier Lect " << endl;

return 0;

} return 1;

}

Dans ce programme, par souci de clarté, nous utilisons deux descripteurs (Lect, Ecri) au lieu d’un seul.

L’exécution de ce programme, avec interaction de l’utilisateur, nous donne :

Entrez le nom du fichier a creer : Tintin_et_Compagnie.dat Entrez le Nom No 1 : Tintin Entrez le Nom No 2 : Milou Entrez le Nom No 3 : Haddock Entrez le Nom No 4 : Tournesol Entrez le Nom No 5 : FIN

Tintin_et_Compagnie.dat Nom 1 : Tintin Nom 2 : Milou Nom 3 : Haddock Nom 4 : Tournesol

Le programme effectue évidemment ce que l’on demande. Cependant, si on introduit un nom

contenant un espace, par exemple ‘’Bianca Castafiore‘’, voilà comment se passe l’exécution :

Entrez le nom du fichier a creer : Tintin_et_Compagnie .dat Entrez le Nom No 1 : Tintin Entrez le Nom No 2 : Bianca Castafiore Entrez le Nom No 3 : Entrez la Nom No 4 : Milou Entrez le Nom No 5 : Haddock Entrez le Nom No 6 : FIN

Tintin_et_Compagnie .dat Nom 1 : Tintin Nom 2 : Bianca Nom 3 : Castafiore Nom 4 : Milou Nom 5 : Haddock

Après l’introduction du nom ‘’Bianca Castafiore ‘’, le programme nous demande en même temps le

nom N° 3 et N° 4. Il ne laisse pas la possibilité à l’utilisateur d’introduire le N° 3. A l’affichage du

fichier, on remarque que ‘’Bianca‘’ a été stocké dans Nom 3 alors que ‘’Castafiore‘’ a été stocké dans

Nom 4. On peut conclure que lorsqu’on est en présence de chaînes de caractères contenant des

espaces, le programme ne fonctionne plus convenablement. La correction est demandée au lecteur

sous forme d’un exercice.

Chap. 7: Les fichiers

M.BENJELLOUN Info I S. Informatique

83

Exercices

7.1. Copier un fichier texte dans un autre: créer un fichier "Essai.dat" sous éditeur par exemple.

Tester le programme en vérifiant, après exécution, la présence du fichier copié à l’endroit où

il a été créé.

7.2. Modifier le programme 7.4 pour que le programme accepte des noms contenant des espaces.

7.3. Coder un programme qui enregistre et lit les informations suivantes :

Les infos sont :

1 5.2 3.9

2 4.2 2.0

3 5.7 1.7

7.4. Ecrire un programme interactif qui gère en boucle le menu suivant :

1 : Saisie

2 : Ajout

Saisie est constituée d’une fonction qui crée un fichier contenant, sur chaque ligne, des informations introduites par l’utilisateur sur un ensemble d’étudiants à savoir : le nom, le

prénom et sa date de naissance.

Ajout est une fonction qui a pour rôle d’ajouter un nouvel étudiant dans le même fichier.

Ce qu’il faut au moins retenir :

Un programme qui utilise des fichiers de données doit inclure fstream

Pour écrire dans un fichier, on déclare une variable de type ofstream ofstream ouput;

ouput.open("fichier.txt"); // on ouvre le fichier fichier.txt

if(ouput) { // on teste si l'ouverture s’est bien passée

On utilise l’opérateur d’insertion << pour envoyer dans le fichier les données.

Pour lire un fichier, on déclare une variable de type ifstraem ifstream input;

input.open("fichier.txt"); // on ouvre le fichier fichier.txt

if(input) { // on teste si l'ouverture s’est bien passée

On utilise l’opérateur d’extraction >> pour envoyer dans des variables les informations extraites du fichier.

Pour extraire toute la ligne ou un ensemble de caractères on utilisera le fonction getline().

Utiliser la fonction close() pour fermer le fichier. input.close() ;

Chapitre 8: Les structures

8.1. Principes fondamentaux des structures Une structure est un ensemble de variables (de types éventuellement différents), définissant un nouveau type sous un seul nom, adapté à une gestion spécifique et à une manipulation facile des données. Elles sont fondamentales pour une programmation lisible, car il est rare qu'une application se contente de travailler avec les types de base. Plutôt que de travailler uniquement avec des caractères, des entiers ou des réels, on aura bien souvent besoin d'exprimer des classes d'objets comme des étudiants (possédant un nom, un prénom, une date de naissance, une adresse, Email, …), des comptes en banque (possédant un numéro, un titulaire, un solde,…), etc. Une structure se définit par un identificateur suivant le mot-clé struct, ainsi que par une liste de champs ou membres définis par un identificateur d'un type donné. Par exemple, pour gérer les coordonnées d'un point (abscisse et ordonnée) ou des clients, on pourra définir les types suivants :

struct point { int x; int y;

};

struct Client { string nom; char Code; };

Ces structures peuvent être représentées logiquement par les schémas suivants:

Une écriture comme struct {...} s'appelle un constructeur de type. L’identificateur point (Client) n’est pas une variable, mais le nom de la structure et il va jouer un rôle d’un nouveau type crée par l’utilisateur pour déclarer des instances/objets de cette structure. Entre les accolades { et } on trouve les déclarations des différents champs (ou membres ou attributs) de la structure. Les membres x et y pour la structure point et nom et Code pour la structure Client sont des variables. Le point-virgule, après l'accolade fermante de la déclaration de la structure, est nécessaire.

L’exemple suivant définit une structure d'objet d'un nouveau type Date, composé d'un champ entier JOUR, un champ MOIS de 3 caractères, e.g. "jan" "fev" etc., et d'un champ ANNEE qui est entier aussi.

struct Date { int JOUR; char MOIS[3]; int ANNEE; };

point

x

y

Client

nom

Code

Chap. 8: Les structures

M.BENJELLOUN Info I S. Informatique

85

La structure Date peut éventuellement être utilisée pour des champs d’une autre structure. On peut donc imbriquer les structures les unes dans les autres. Par exemple la structure suivante

struct Etudiant{ string nom; char prenom[30]; char *adresse; int numero; struct Date D_Nais; };

utilise la structure Date pour l’un de ses champs : D_Nais. Ce dernier est composé de JOUR, MOIS et ANNEE. Pour que cela fonctionne, comme pour les fonctions, il faut déclarer la structure Date au-dessus de la déclaration de la structure Etudiant. Bien souvent, les structures sont déclarées en début de programme ou dans un fichier en-tête, car elles seront certainement utilisées par l'ensemble des fonctions du programme.

Maintenant, pour déclarer des variables du type d’une structure, on peut écrire par exemple : struct point pt; // la variable pt est un point dont le type est "struct point".

point Z ; /* en C++ le mot clé struct n'est pas nécessaire devant la structure

lors de la déclaration. struct point Z ; ou point Z ; sont équivalentes, cela signifie que Z est un point */

Client Cli1, Cli2; // Cli1 et Cli2 sont deux clients qui ont un nom et un Code. struct Date paques; /* Le type de la variable paques est "struct Date". paques est la variable

structurée composée de trois champs: JOUR, MOIS et ANNEE.*/

struct Date semaine[7]; // semaine est un tableau de 7 éléments, chacun de type Date

struct Etudiant Grp_1_A[20] ; // Grp_1_A est un groupe de 20 étudiants struct Etudiant Anne, Jean ; // Anne et Jean sont deux nouvelles variables de type Etudiant La déclaration des variables peut se faire aussi au moment de la définition de la structure :

struct Date { int JOUR; char MOIS[3]; int ANNEE; } hier, demain;

On se réfère aux champs individuels des variables par l'opérateur point ‘ . ’, ainsi: demain.JOUR est l'entier JOUR. demain.MOIS[0] est le premier caractère du MOIS. demain.ANNEE est l'entier ANNEE. semaine[0].JOUR est l'entier JOUR de la première composante du tableau semaine. Jean.D_Nais.JOUR est le JOUR de la date de naissance de Jean. Pour initialiser les variables de type struct déclarées précédemment, on peut le faire en accédant aux membres, par exemple:

Chap. 8: Les structures

M.BENJELLOUN Info I S. Informatique

86

pt.x=25; Cli1.Code = ‘D’; Jean.D_Nais.JOUR = 15 ;

Mais il est également possible d'initialiser une structure au moment de sa déclaration, en donnant la liste des valeurs de ses champs, de la même manière que pour un tableau. En reprenant l'exemple précédent, au moment de déclarer la variable pt ou Cli1, on peut écrire :

struct point pt={25,10}; struct Client Cli1{ "Van cool", 'D'};

La variable pt (Cli1) est ici déclarée comme étant de type point (Client) et initialisée comme suit : son x=25 (nom= "Van cool") et son y=10 (Code = 'D'). Les initialisations d’une structure à la déclaration ne peuvent pas être utilisées ailleurs qu'au moment des déclarations. Par exemple, l'écriture suivante est incorrecte:

struct point pt; ... pt = {25,10} ; // Ecriture incorrecte

Si l’on désire initialiser par exemple un étudiant lors de la déclaration de sa variable, on peut le faire comme suit :

struct Etudiant JD = { "Dupont", "Jean", "rue de Houdain, 9, 7000 Mons", 402, { 15, "jan", 1986 } };

En C++ le mot-clé struct n'est pas nécessaire devant la structure lors de la déclaration. En effet, les déclarations suivantes sont équivalentes :

struct Etudiant Grp_1_A[20]; équivalent à Personne Grp_1_A[20] ; struct Etudiant Anne, Jean; équivalent à Personne Anne, Jean ;

8.2. Pointeurs vers les structures Il faut noter que ce point ne fait pas l’objet de l’enseignement durant cette année, mais bien de la matière donnée dans le cadre du cours d’informatique en second Bachelier. Il est donné ici seulement à titre indicatif. Si la structure est un pointeur, comment peut-on accéder à ses membres ? La déclaration suivante

Personne *pp; déclare pp comme un pointeur vers une structure Etudiant. On peut écrire

(*pp).numero

Chap. 8: Les structures

M.BENJELLOUN Info I S. Informatique

87

pour accéder au champ numero. L'opérateur ‘.’ étant prioritaire sur l'indirection ‘*’, il faut des parenthèses. Mais il y a une autre notation d'utilisation. Au lieu de (*pp).numero, on utilise plutôt l'opérateur -> pour écrire plus simplement

pp-> numero pour accéder au champ numero de la structure pointée par pp. Dans (*pp).numero, les parenthèses sont obligatoires à cause de la précédence de ‘.’ sur ‘*’. Auparavant, on aura initialisé pp (ne pas l'oublier) par exemple par

pp = &Grp_1_A[0] ou pp = Grp_1_A;

8.3. Structures et fonctions Les structures peuvent être passées en arguments de fonctions, par valeur ou par référence. Elles peuvent aussi être retournées par une fonction. On peut également utiliser l'affectation entre structures. Construisons par exemple une fonction module, qui donnera la distance euclidienne d'un point à l'origine.

#include <cmath> // pour utiliser la fonction racine carrée sqrt … float module ( point z) { float temp; temp = (z.x)*(z.x); temp += (z.y)*(z.y); return sqrt(temp); }

Nous pouvons de même construire une fonction qui calcule la somme de vecteurs.

point somme(point a, point b) { point temp; temp.x = a.x + b.x; temp.y = a.y + b.y; return temp; }

Les appels suivants de ces fonctions seront corrects:

point z1 = {1, -4}, z2 = {5, 2}, z3; if( module(z1) > 1.0 ) z3 = somme( z1, z2 );

ce qui donnera z3 = (6, -2). Le programme suivant nous montre comment retourner une référence. Nous invitons le lecteur à le tester et déduire comment il fonctionne.

Chap. 8: Les structures

M.BENJELLOUN Info I S. Informatique

88

#include <iostream> #include <string> using namespace std; /* utilisation de l’espace de nommage std pour importer

les symboles de la bibliothèque standard C++ */ struct Client { string nom; string adresse; char Code; }; Client quel_client(int i, Client DataBase[]) { if ((i > 0) && (i < 4 )) return(DataBase[i]); else return(DataBase[0]); } void main(void) { int N; Client DataBase[4] = { {"Picsou", "Pas de sous 00", 'B'}, {"Donald", "Bcp de sous 123", 'D'}, {"Obelix", "Village gaulois 15", 'G'}, {"BISCORNUS", "BABAORUM 2341", 'M'} }; cout << "Pour chercher un Client, donner un numero entre 1 et 4 : "; cin >> N; cout << "Debut de recherche du Client " << N << endl; Client ce_client = quel_client(N-1, DataBase); cout << "\n NOM : " << ce_client.nom << "\n Adresse : " << ce_client.adresse << "\n Code : " << ce_client.Code << endl; }

Programme 8.1 Le programme suivant illustre aussi l’utilisation des structures lors du passage de paramètres par valeur et par référence.

#include <iostream>

using namespace std;

struct point {

int x;

int y; };

Chap. 8: Les structures

M.BENJELLOUN Info I S. Informatique

89

point creer_pt () { // retourne une structure point res = { 10, 20 };

return res;

} void affiche(point le_pt, char titre[]){

cout << titre << " x= "<< le_pt.x << " y= "<< le_pt.y << endl; }

void pt_par_valeur ( point le_pt ) {

le_pt = creer_pt(); affiche(le_pt, "Dans pt_par_valeur : ");

}

void pt_par_pointeur ( point *le_pt ){ *le_pt = creer_pt();

affiche(*le_pt, "Dans pt_par_reference : ");

} void pt_par_reference ( point & le_pt ){

le_pt = creer_pt();

affiche(le_pt, "Dans pt_par_reference : "); }

void main (){

point mon_pt; mon_pt.x= mon_pt.y=0;

affiche(mon_pt, "contenu initial de 'mon_point':\n"); pt_par_valeur (mon_pt);

affiche(mon_pt, "Main Sortie de pt_par_valeur \n");

mon_pt.x= mon_pt.y=0;

pt_par_pointeur (&mon_pt);

affiche(mon_pt, "Main Sortie de pt_par_pointeur \n");

mon_pt.x= mon_pt.y=0;

pt_par_reference (mon_pt); affiche(mon_pt, "Main Sortie de pt_par_reference \n");

}

Programme 8.2 Le résultat de ce programme est :

contenu initial de 'mon_point': x= 0 y= 0 Dans pt_par_valeur : x= 10 y= 20 Main Sortie de pt_par_valeur x= 0 y= 0 Dans pt_par_reference : x= 10 y= 20 Main Sortie de pt_par_pointeur x= 10 y= 20 Dans pt_par_reference : x= 10 y= 20 Main Sortie de pt_par_reference x= 10 y= 20

Le programme suivant doit saisir un tableau de structures, afficher un certain nombre de champs du tableau, échanger deux composants du tableau et finalement libérer la mémoire.

Chap. 8: Les structures

M.BENJELLOUN Info I S. Informatique

90

#include <iostream> #include <string>

#include <iomanip> //pour setw(x):la prochaine sortie sera sur une largeur de x

using namespace std; const int NE_max=20;

const int NC_max=30; struct Date {

int JOUR, MOIS, ANNEE;

}; struct Personne{

char nom[NC_max];

string prenom; int numero;

struct Date D_Nais;

} ;

Personne saisie ();

void affichage (Personne Tab[], int taille); void echanger(Personne &Tab1, Personne &Tab2);

void liberer(Personne Tab[], int taille);

void main( ){ int i=0, NE;

Personne Tab[NE_max];

cout <<"Combien d'etudiants voulez-vous introduire (entre 1 et " << NE_max << ")? "; cin >> NE;

while (i<NE){ Tab[i]=saisie(); // (1)

i++;

} affichage (Tab, NE); // (2)

if(NE>2){

echanger(Tab[1], Tab[2]); // (3) affichage (Tab, NE);

}

}

Personne saisie (){

Personne tmp; int static j=0; // voir chapitre 5 !

j++;

cout << "\nNom_"<< j << " : "; cin >> tmp.nom;

cout << "\nPrenom_"<< j << " : ";

cin >> tmp.prenom; cout << "\nNumero_"<< j << " : ";

cin >> tmp.numero;

cout << "\nJour : "; cin >> tmp.D_Nais.JOUR;

cout << "\nMois : ";

cin >> tmp.D_Nais.MOIS; cout << "\nAnnee : ";

cin >> tmp.D_Nais.ANNEE;

return tmp; }

Chap. 8: Les structures

M.BENJELLOUN Info I S. Informatique

91

void affichage (Personne Tab[], int taille){ int i=0;

while (i<taille){

cout <<setw(20) << Tab[i].nom <<setw(20) << Tab[i].prenom

<<setw(5) << Tab[i].D_Nais.ANNEE<< endl; i++;

}

cout << "\n\n"; }

void echanger (Personne &Tab1, Personne &Tab2){ Personne temp;

temp = Tab1; // (3')

Tab1 = Tab2; Tab2 = temp;

}

Programme 8.3

La fonction main appelle (1) la fonction sans paramètres : saisie(). Cette dernière est de type Personne, donc doit absolument retourner une valeur de même type. En effet, cette fonction saisit les données pour une personne et retourne cette personne à la fonction main().

La fonction affiche (2) permet d’afficher quelques champs pour chaque élément du tableau.

La fonction echanger (3) a pour rôle d’échanger les données de deux éléments du tableau. Pour que le changement se transmette à la fonction appelante, nous avons utilisé les références de ces enregistrements. Il faut noter, que si l’on ne peut affecter un tableau à un autre, les structures le permettent. L’affectation ne se fait pas champ par champ mais structure par structure (3’) même si cette dernière contient un tableau.

Chap. 8: Les structures

M.BENJELLOUN Info I S. Informatique

92

Exercices 8.0. Ecrire une fonction qui traduit le graphique (température en fonction de x) suivant :

8.1. Ecrire un programme qui traite un tableau de structures (taille max = 20) dont chaque élément comprend les informations suivantes :

un nom ( string nom), un numero (int), une date

où date est une structure de type Date struct Date{

int jour, mois, an ; } ;

qui gère en boucle le menu de choix suivant :

Ce qu’il faut au moins retenir :

La déclaration d’une structure :

struct ma_struct {

int x;

float y;

};

Si le tableau est une collection de variables de même type, une structure est un ensemble de

variables de types éventuellement différents.

Si l’affectation d’un tableau à un autre est interdite, il est possible d’assigner une structure à une

autre, même si un élément de la structure est un tableau.

Le point-virgule après l'accolade fermante de la déclaration d’une structure est obligatoire.

On ne peut utiliser une structure A dans une structure B que si A est déclarée avant B.

On se réfère aux champs individuels des variables de type structure par :

A.champ1 ou

A->champ1 si A est une structure sur un pointeur.

température en fct x

0

5

10

15

20

25

0 1 2 3 4 5 6x

t

Chap. 8: Les structures

M.BENJELLOUN Info I S. Informatique

93

1 : Saisie des enregistrements 2 : Affichage des données 3 : Tri suivant le numéro

4 : Tri suivant le nom 5 : Effacer un élément selon le nom

6 : Ajouter un élément selon le nom 7 : Sauvegarde sur fichier

8 : Lecture d’un fichier 9 : Quitter

Le menu sera affiché via une fonction, les choix seront traités via l’instruction case. Les points 1 à 8 seront chacun traités par au moins une fonction. Les points 5 et 6 utiliseront la recherche dichotomique pour la localisation. Le point 8 utilisera un vecteur crée dynamiquement pour recevoir les données se trouvant dans le fichier.

8.2. Ecrire le même programme que 8.1. en remplaçant la structure par :

struct Etudiant{ char *nom ; int numero ; char code ;

} ; 8.3. Ecrire le même programme que 8.1. en remplaçant la structure par :

struct Etudiant{ string nom ; int numero ; int Matieres[3] ;

} ; 8.4. Donnez la structure représentant ce tableau :

Nom adresse Sexe res1 res2 … resN Code Myne

nom1 56 rue je ne sais pas,

bte 10, … M 10 15 … 8 A 13.3

nom2 33, rue qui n’existe pas, …

F 10 20 … 19 V 15.7

nom3 … M 13 14 … 15 C 13.9

nom20 … F 10 10 … 11 D 10.2

Chap. 8: Les structures

M.BENJELLOUN Info I S. Informatique

94

8.5. Enoncé

Ecrire un programme permettant de manipuler un tableau

d’enregistrements ‘’structures’’ (taille max NE_MAX = 20). Ce programme doit gérer en boucle le menu suivant :

1 - SAISIE et AFFICHAGE du tableau 2 - TRI par ordre alphabétique selon le nom et le code

3 - SUPPRESSION d'un élément après localisation

4 - Sauvegarde 5 - ARRET du programme

La structure de chaque élément est : struct Element

{ string nom char code

int cotes[NC_max] ; // NC_max=5

}

1 - SAISIE et AFFICHAGE

est constitué de deux fonctions : Saisie (…...) ; // de type void

Dans cette fonction, on demandera le nombre d’éléments (NE) pour initialiser le tableau (par

exemple 5) et le nombre de cotes (NC<=5) pour tous

les "ELEMENT", et on effectuera la saisie du nom, du code et des cotes pour chaque élément.

Affichage(…….) ;

Après la saisie, une fonction d’affichage est appelée afin d’afficher l’acquisition de la fonction saisie.

Exp : si NE=5 et NC=2

Nom Code Cote1 Cote2

van cool P 45 54

mon_nom M 2 1 son_nom T 29 2

van cool A 40 41

aladdin Y 40 42

2 - TRI par ordre alphabétique selon le nom et le code

Nom Code Cote1 Cote2

aladdin Y 40 42

mon_nom M 2 1

son_nom T 29 2 van cool A 40 41

van cool P 45 54

3 - SUPPRESSION d'un élément après localisation

Nom de l’élément à supprimer : van cool Code de l’élément à supprimer : A

La liste devient (Fonction AFFICHAGE ) :

Nom Code Cote1 Cote2

aladdin Y 40 42

mon_nom M 2 1

son_nom T 29 2 van cool P 45 54

Avant de supprimer l’élément avec la fonction

Suppression, il faut d’abord le localiser avec la fonction Localisation (dichotomique)

RQs: Si l’élément à supprimer n’existe pas, n’oubliez pas de le signaler à l’utilisateur. Le programme doit utiliser une série de fonctions

qui vont permettre de séparer les tâches.

4 - Sauvegarde

On demandera à l’utilisateur d’introduire un seuil (int).

Ce dernier doit être >0 et < 100 (test).

Pour chaque "ELEMENT", si la somme de ses cotes est supérieure au seuil, on l’enregistrera dans un

fichier ‘’Fich.txt’’.

Exp : Seuil = 80

Dans le fichier �

aladdin Y 40 42

van cool P 45 54

Chap. 8: Les structures

M.BENJELLOUN Info I S. Informatique

95

8.6. Enoncé

Ecrire un programme permettant de manipuler un tableau

d’enregistrements "structure" (NP_max = 20) dont

chaque élément comprend les informations suivantes

Ligne{

string Phrase ; Compt ; // une structure

}

avec Compt est une structure de type : Compt{

int N_e ;

int N_i ; int Som;

}

Réaliser un programme qui gère en boucle le menu de choix

suivant :

S- Saisie et affichage T- Tri et affichage

A- Actualisation et affichage

V- Valeurs Q- Quitter

S- Saisie et affichage :

La fonction saisie (de type void) demande à l’utilisateur le nombre de "Ligne" (NL) pour initialiser

le tableau (par exemple 5) et lui permet d’introduire un

texte par ligne.

EXP : saisie(…) puis affichage(….) Ligne_1 : ceci est un test Ligne_2 : qui compte le nombre

Ligne_3 : de e et de i ainsi

Ligne_4 : la longueur de Ligne_5 : la ligne

T- Tri et affichage :

Tri décroissant selon la longueur du texte par ligne(fonction) puis affichage(fonction).

qui compte le nombre 20

de e et de i ainsi 18 ceci est un test 16

la longueur de 14 la ligne 8

A- Actualisation et affichage :

Pour chaque ligne, on compte le nombre de e, de i et

on détermine Som= N_e+N_i . Puis on actualise la

structure (N_e, N_i et Som ). Enfin, on affiche l’actualisation du tableau.

EXP :

TEXTE N_e N_i Som qui compte le nombre 3 1 4

de e et de i ainsi 4 3 7

ceci est un test 3 1 4 la longueur de 2 0 2

la ligne 1 1 2

Dans la fonction Actualisation on cherche le max de

Som (Max_Som) et sa position (pos) dans le

tableau.

V_ Valeurs :

Affiche les nombres : NL, Max_Som et pos.

Remarques:

Il est souhaitable d’utiliser la même fonction Affichage en S, T et A. (une et une seule fonction)

NL, Max_Som et pos sont des variables locales de la fonction main.

NP_max et dim_max sont des constantes.

!

Chap. 8: Les structures

M.BENJELLOUN Info I S. Informatique

96

8.7. Le boulier à quatre

Ce qu’on appelle boulier à quatre, c’est un système de billes suspendues par des fils. Ce système accepte au

maximum quatre billes (un tableau de 4 éléments). Au-delà de quatre, si une bille est ajoutée à droite, la bille de gauche est retirée. De même, si une bille est ajoutée à gauche, celle de droite est retirée.

Ecrire un programme permettant de représenter cette application. Chaque bille est représentée par une structure contenant les données suivantes :

string Couleur_Bil; // couleur de la bille par exemple : vert bouteille

char Code ; // 1 seul caractère et pas un tableau d’un seul caractère Code[1]

Le programme doit gérer en boucle le menu de choix suivant :

1- Saisie et Affichage 2- Ajout Gauche et Affichage

3- Ajout Droite et Affichage

4- Affichage 5- Effacer bille et Affichage + Sauver Fichier

6- Sortir

Exemple d’exécution

1- Saisie et Affichage :

Dans la fonction saisie (Type void), on demandera à l’utilisateur le nombre de billes (NI<=4) pour initialiser le Tableau.

Exp : Si NI=2 //couleur (Code)

2- Ajout Gauche et Affichage :

Bleu (T) �

3- Ajout Droite et Affichage :

Blanc (S) �

2- Ajout Gauche et Affichage :

Jaune (T) �

2- Ajout Gauche et Affichage :

Noire (N) �

5- Effacer bille et Affichage + Sauver Fichier // La bille est choisie par l’utilisateur Il faut se baser aussi sur le code (Si deux billes de même couleur).

Couleur de la bille : Jaune Code : T �

Fichier � Jaune T Si la bille à effacer existe, il faut la supprimer du tableau et l’enregistrer dans un fichier ‘’Fich.txt’’ .

Remarques : Utilisez les règles de bonnes pratiques étudiées durant les travaux pratiques telles que l’indentation syntaxique, la programmation modulaire, …Votre programme utilisera une série de fonctions permettant de séparer les tâches (Saisie, Affichage, Ajout, Localiser , Sauver…). Il doit empêcher l’utilisateur de se retrouver avec un tableau de plus que quatre billes

ou d’en effacer une alors qu’il est vide. Si la bille à supprimer n’existe pas, n’oubliez pas de le signaler à l’utilisateur.

Rouge (E) Vert bouteille (C) ... …

Bleu (T) Rouge (E) Vert bouteille (C) ...

Bleu (T) Rouge (E) Vert bouteille (C) Blanc (S)

Jaune (T) Bleu (T) Rouge (E) Vert bouteille (C)

Noire (N) Jaune (T) Bleu (T) Rouge (E)

Noire (N) Bleu (T) Rouge (E) ...

R V

R VB

R VB Bc

B R J V

B R J N

B R N

Chap. 8: Les structures

M.BENJELLOUN Info I S. Informatique

97

8.8. Rythme cardiaque et sport : seuil de lipolyse

Il est très utile à nombre d'entre nous de définir à quel rythme cardiaque effectuer nos exercices physiques. En premier

lieu pour pratiquer nos activités en toute sécurité et ensuite, pour savoir tout simplement à quel moment notre organisme

puise dans ses réserves de graisses, c'est ce qu’on appelle le seuil de lipolyse. Il semble que c’est cette fréquence cardiaque (seuil de lipolyse) qu’il faut maintenir pendant la durée des activités sportives afin de maigrir.

Ce seuil peut être calculé comme suit: (220 - âge - fréquence cardiaque au repos) divisé par deux + fréquence cardiaque au repos

Par exemple, quelqu'un âgé de 25 ans et dont le rythme cardiaque au repos est de 70 battements par minute :

220 – 25 (âge) – 70 (fréquence cardiaque au repos) = 125 ; ���� 125/2 = 62 ; ���� 62 + 70 = 132

Son seuil de lipolyse est de 132 battements cardiaques par minute.

Ecrire un programme permettant de manipuler un tableau

de personnes (taille max Nmax = 6 ) dont chaque élément comprend les informations suivantes :

{ string Nom ;

int Age; int FreqR ; // fréquence cardiaque au repos

} IL EST INTERDIT DE MODIFIER LA STRUCTURE ;

Ce programme doit gérer en boucle le menu suivant :

1- Saisie et Affichage 2- AAffiffichachagege

3- Seuil de lipolyse et Affichage 4- Effacer et Affichage

5- Tab Dynamique et affichage

1- Saisie et Affichage:

Est constitué de deux fonctions : Saisie (…….) ; // Type void

Dans cette fonction on demandera le nombre

d’éléments NE (<Nmax :tester) à saisir et on effectuera la saisie des éléments.

Affichage(…….) ; Permet l’affichage des données : EXP : Si NE=3 Nom Age FreqR Riri 20 70 Fifi 40 60 Lou Lou 30 75

2- AAffiffichachagege:

Permet d’afficher deux fois chaque ligne: Nom Age FreqR Riri 20 70 Riri 20 70 Fifi 40 60 Fifi 40 60 Lou Lou 30 75 Lou Lou 30 75

RQ : Il est fort souhaitable d’utiliser la même et seule fonction Affichage en 1, 2, 3 et 4.

3- Seuil de lipolyse et Affichage:

Ici on calcule le seuil de lipolyse pour chaque personne et on

affiche les résultats. Nom Age FreqR Seuil Lipo Riri 20 70 135

Fifi 40 60 120 Lou Lou 30 75 132

4- Effacer et Affichage: Est constitué de deux fonctions : Effacer (….) et Affichage Effacer (….) ; Dans cette fonction on demandera à l’utilisateur d’introduire une valeur V (de type int). Cette dernière doit être >10 et < 50 (tester). On supprimera ensuite du tableau chaque personne dont l’âge est strictement inférieur à V.

EXP : Si V = 35 ���� le tableau deviendra

Nom Age FreqR Seuil Lipo Fifi 40 60 120

5- Tab Dynamique et affichage: On désire ici avoir les résultats pour tracer la courbe de l’âge en fonction du rythme cardiaque.

Donc, pour une FreqR F, donnez le Seuil de Lipolyse pour une tranche d’âge entre 15 et 45 avec un pas P. F et P seront demandés à l’utilisateur.

Exp1 : Si F= 70 et P= 5 on aura :

Age 15, 20, 25, 30, 35, 40, 45 Seuil 137 135 132 …

Exp2 : Si F= 70 et P= 10 on aura :

Age 15, 25, 35, 45 Seuil 137 132 …

Selon la valeur de P, la dimension du tableau (âge, seuil) change. C’est pour cette raison qu’il faut utiliser un tableau dynamique pour stocker et afficher les résultats (âge, seuil).

RQs : Les variables globales sont à éviter.

!

Chapitre 9: Quelques erreurs à éviter

Ce chapitre n’aurait pas pu être rédigé sans l’aide d’un certain nombre d’étudiants. En effet, il a été alimenté

par des erreurs que certains d’entre eux ont commises lors des séances de travaux voire à l’examen.

La devise ‘’ C’est en faisant des erreurs que l’on apprend ‘’ me pousse à montrer celles commises par les anciens étudiants afin de permettre aux nouveaux de ne pas commettre les mêmes, au moins je l’espère.

La plupart des erreurs décrites ici sont des erreurs d'inattention ou proviennent de la syntaxe du langage combinée à l'inattention du programmeur. En raison de la puissance d'expression du langage et du peu de

vérifications réalisées par le compilateur, de nombreuses erreurs sont possibles. Certaines d’entre-elles ne seront pas détectées à la compilation mais produiront nécessairement des erreurs plus ou moins faciles à

détecter à l'exécution. Certaines de ces erreurs conduisent systématiquement à un plantage du programme en cours d'exécution ou à un résultat faux, alors que d'autres conduisent à des comportements indéterminés,

c'est-à-dire que n'importe quoi peut se produire.

Déclaration :

Int i,j , Correction � int i, j ;

Une déclaration se termine par un point virgule et le type des variables doit être écrit en minuscule. En

effet le C fait la différence entre les minuscules et les majuscules.

Affectation:

int A, B ; A=10 ;

A=B ;

Correction �

int A, B ; A=10 ;

B=A ;

Le rôle du symbole d’affectation ‘=’ consiste à évaluer l’expression du membre de droite et à transférer ce

résultat comme valeur de l’objet au membre de gauche. Donc pour affecter la valeur de A c.-à-d. 10 à B, il faut écrire B=A à la place de A=B. D’ailleurs on écrit A=10 et pas 10=A.

void main(){

int *adr; *adr = 123;

...

}

Correction �

void main(){

int *adr; adr = new int;

*adr1 = 123; ...

delete adr; }

La valeur d'un pointeur désigne l'adresse de la zone mémoire vers laquelle il pointe. Si cette adresse ne correspond pas à une zone de mémoire utilisable par le programme en cours, une erreur (segmentation

fault) se produit à l'exécution du programme. Mais, même si l'adresse est valide et ne produit pas d'erreur, il faut s'assurer que la valeur du pointeur correspond à une zone allouée correctement, avec new et libérer

avec delete.

int A, B ; float F;

A=10 ; B=25 ;

F= B/A ;

Correction �

int A, B ; float F;

A=10 ; B=25 ;

F= (float)B/A ;

Chap. 9: Quelques erreurs à éviter

M.BENJELLOUN Info I S. Informatique

99

Taille d’une chaîne :

char nom[30] ; int taille ;

cout <<"nom ="; cin >> nom ;

taille = sizeof(nom);

Correction �

Selon la déclaration de type de nom.

int taille ; …

taille = strlen(nom); ou

taille = nom.length()

L’opérateur sizeof nous donne le nombre d’octets que le tableau nom occupe en mémoire et non le

nombre de caractères que l’utilisateur à introduit.

Saisie et/ou affichage :

int i ;

cout <<"valeur de i = " << i;

Correction �

int i ; i=5; //Initialiser la variable avant

cout <<"valeur de i = " << i;

Dans cet exemple, la valeur de ‘i’ n’est pas initialisée, donc l’affichage peut être n’importe quoi.

Confusion entre = et = = :

if (x = 0) {

... }

Correction �

if (x == 0) {

... }

Cette erreur est l'une des plus fréquentes, elle provient de la syntaxe du langage combinée à

l'inattention du programmeur. Elle peut être très difficile à détecter.

En effet, x = 0 est une expression valide en C/C++ qui affecte à x la valeur zéro, il est donc parfaitement légal d'écrire if (x = 0) { …

Malheureusement le programmeur voulait tester si x valait zéro ? Cela entraîne que, même quand x est différent de 0, la variable x prend la valeur 0 !

Cependant, ce problème n'est pas limité au cas de la comparaison avec une constante. Il se pose aussi

lorsque l'on veut comparer deux variables.

Cette erreur n’est pas propre à if..else, mais on peut la rencontrer dans les boucles conditionnelles à l’endroit de <expression logique > ( voir la section 3.3. ).

Boucles conditionnelles :

for (i = 0 , i<N, i++){

Correction �

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

‘;’ et non ‘,’

for (i = 0 ; i<N; i++) ; { …

Correction �

for (i = 0 ; i<N; i++) { …

int Tab[N]; for (i = 0 ; i == N; i++){

Tab[i]=i; }

Correction �

int Tab[N]; for (i = 0 ; i<N; i++){

Tab[i]=i; }

Chap. 9: Quelques erreurs à éviter

M.BENJELLOUN Info I S. Informatique

100

Une boucle qui ne fait pas ce que l’on désire, c’est-à-dire exécuter le bloc d’instruction(s) pour i=0, i=1, etc. Elle ne le fait que quand i vaut N. Malheureusement, en plus dans ce cas-ci, il ne faut pas aller jusqu’à

N (voir la définition d’un tableau 4.1.1. ).

int Tab[N]; for (i = 0 ; i<N; i++)

Tab[i]=i; cout << "Tab[i]= " << i;

Correction �

int Tab[N]; for (i = 0 ; i<N; i++) {

Tab[i]=i; cout << "Tab[i]= " << I;

}

Avec une boucle conditionnelle, s’il faut exécuter plusieurs instructions, il faut les mettre entre { et }.

while (<expression logique >);

{ ...

}

Correction �

while (<expression logique >)

{ ...

}

Pas de ‘ ;’ juste après le while , sinon c’est une boucle qui ne fait qu’attendre.

Oubli du break dans les switch :

void print_chiffre(int i)

{ switch (i) {

case 1: cout <<"un"; case 2: cout <<"deux";

case 3: cout <<"trois"; }

}

Correction �

void print_chiffre(int i) {

switch (i) { case 1: cout <<"un"; break;

case 2: cout <<"deux"; break; case 3: cout <<"trois"; break;

default : cout <<"1,2 ou 3 ?"; }

}

Sans les break l’appel de la fonction print_chiffre(1) affichera undeuxtrois ce qui n'est peut-être pas le

comportement attendu. L’étiquette default est facultative mais recommandée.

Fonctions :

int Cube(int i)

{ i*i*i;

}

Correction �

int Cube(int i)

{ return(i*i*i);

}

void Cube(int &i)

{ i = i * i * i;

}

int Deux_val(int i, int j){

… …

return (i,j); }

int Deux_val(int i, int j)

{ … return i;

return j; }

Correction �

void Deux_val(int &i, int &j) {

i =… j =…

}

void Modif(int *i, int *j) {

i = 5; j = 6;

}

Correction �

void Modif(int *i, int *j) {

*i = 5; *j = 6;

}

Chap. 9: Quelques erreurs à éviter

M.BENJELLOUN Info I S. Informatique

101

void Modif(int *i, int *j)

{ *i = 5; *j = 6;

} void main(){

int k,j ; Modif(k, j);

}

void Modif(int i, int j) { i = 5;

j = 6; }

void main(){ int k,j ;

Modif(k, j); }

Correction

void Modif(int *i, int *j) { *i = 5;

*j = 6; }

void main(){ int k,j ;

Modif(&k, &j); }

void Modif(int &i, int &j) { i = 5;

j = 6; }

void main(){ int k,j ;

Modif(k, j); }

int Modif(int i)

{ i = 5;

return i; }

void main(){ int j ;

Modif(j); cout<< j;

}

Correction �

int Modif(int i)

{ i = 5;

return i; }

void main(){ int j ;

j=Modif(j); cout<< j;

}

int Modif(int i)

{ i = 5;

return i; }

void main(){ int j ;

cout<< Modif(j); }

void Modif(int &i)

{ i = 5;

}

void main(){ int j ;

Modif(j); cout<< j;

}

int Fonc(int Tab[], int N){

int i; for (i = 0 ; i<N; i++)

Tab[i]=i;

return Tab;

}

Correction �

void Fonc(int Tab[], int N){

int i; for (i = 0 ; i<N; i++)

Tab[i]=i; /*une fonction ne peut retourner un tableau avec return */

}

void Saisie1(int Tab1[], int N1){ int i;

for (i = 0 ; i<N1; i++) Tab1[i]=i;

} void Saisie2(int Tab2[], int N2){

int i; for (i = 0 ; i<N2; i++)

Tab2[i]=i; }

void main(){ int Tab1[40] , Tab2[40];

int N1=20, N2=30;

saisie1(Tab1, N1) ; saisie2(Tab2, N2) ;

}

Correction �

void Saisie(int Tab[], int N){ int i;

for (i = 0 ; i<N; i++) Tab[i]=i;

}

void main(){

int Tab1[40], Tab2[40]; int N1=20, N2=30;

saisie(Tab1, N1) ; saisie(Tab2, N2) ;

}

struct Article{ string nom;

}; void main() {

Article T[5]; for (int i=0; i<5; i++){

cout << "Entrez le nom : ";

cin >> T.nom; }

}

Correction �

struct Article{ string nom;

}; void main() {

Article T[5]; for (int i=0; i<5; i++){

cout << "Entrez le nom : ";

cin >> T[i].nom; }

}

Service d’Informatique

Annexes

FACULTE POLYTECHNIQUE DE MONS

Annexes

Voici la table de précédence des opérateurs qui fixe l'ordre de priorité de ces derniers (du plus prioritaire au moins prioritaire) ainsi que l'associativité (ordre dans lequel deux opérateurs de même priorité sont traités pour calculer la valeur) de chacun d'entre eux.

Ordre de priorité des opérateurs

Opérateurs Associativité Niveau de priorité

() [] -> . ++ (postfixé) -- (idem)

de gauche à droite les plus prioritaires

Opérateurs unaires : ! ~ ++ -- + - * & (<type>) sizeof

de droite à gauche

* / % de gauche à droite

+ - de gauche à droite

<<>> de gauche à droite

<<=>= > de gauche à droite

== != de gauche à droite

& de gauche à droite

^ de gauche à droite

| de gauche à droite

&& de gauche à droite

|| de gauche à droite

?: de droite à gauche

= *= /= %= += -= <<=>>= &= ^= |= de droite à gauche

, de gauche à droite le moins prioritaire

Annexes

M.BENJELLOUN Info. I S. Informatique

104

Table des codes ASCII des caractères

Caractère déc. hexa. octal Caractère déc. hexa. octal

C-@ (NUL) 0 0x00 000 espace 32 0x20 040

C-a (SOH) 1 0x01 001 ! 33 0x21 041

C-b (STX) 2 0x02 002 " 34 0x22 042

C-c (ETX) 3 0x03 003 # 35 0x23 043

C-d (EOT) 4 0x04 004 $ 36 0x24 044

C-e (ENQ) 5 0x05 005 % 37 0x25 045

C-f (ACK) 6 0x06 006 & 38 0x26 046

C-g (BEL) 7 0x07 007 ' 39 0x27 047

C-h (BS) 8 0x08 010 ( 40 0x28 050

C-i (HT) 9 0x09 011 ) 41 0x29 051

C-j (LF) 10 0x0a 012 * 42 0x2a 052

C-k (VT) 11 0x0b 013 + 43 0x2b 053

C-l (FF) 12 0x0c 014 , 44 0x2c 054

C-m (CR) 13 0x0d 015 - 45 0x2d 055

C-n (SO) 14 0x0e 016 . 46 0x2e 056

C-o (SI) 15 0x0f 017 / 47 0x2f 057

C-p (DLE) 16 0x10 020 0 48 0x30 060

C-q (DC1) 17 0x11 021 1 49 0x31 061

C-r (DC2) 18 0x12 022 2 50 0x32 062

C-s (DC3) 19 0x13 023 3 51 0x33 063

C-t (DC4) 20 0x14 024 4 52 0x34 064

C-u (NAK) 21 0x15 025 5 53 0x35 065

C-v (SYN) 22 0x16 026 6 54 0x36 066

C-w (ETB) 23 0x17 027 7 55 0x37 067

C-x (CAN) 24 0x18 030 8 56 0x38 070

C-y (EM) 25 0x19 031 9 57 0x39 071

C-z (SUB) 26 0x1a 032 : 58 0x3a 072

C-[ (ESC) 27 0x1b 033 ; 59 0x3b 073

C-\ (FS) 28 0x1c 034 < 60 0x3c 074

C-] (GS) 29 0x1d 035 = 61 0x3d 075

C-$ (RS) 30 0x1e 036 > 62 0x3e 076

C-_ (US) 31 0x1f 037 ? 63 0x3f 077

Caract déc. hexa. octal Caract déc. hexa. octal

@ 64 0x40 100 ` 96 0x60 140

A 65 0x41 101 a 97 0x61 141

B 66 0x42 102 b 98 0x62 142

C 67 0x43 103 c 99 0x63 143

D 68 0x44 104 d 100 0x64 144

E 69 0x45 105 e 101 0x65 145

F 70 0x46 106 f 102 0x66 146

G 71 0x47 107 g 103 0x67 147

H 72 0x48 110 h 104 0x68 150

I 73 0x49 111 i 105 0x69 151

J 74 0x4a 112 j 106 0x6a 152

K 75 0x4b 113 k 107 0x6b 153

L 76 0x4c 114 l 108 0x6c 154

M 77 0x4d 115 m 109 0x6d 155

N 78 0x4e 116 n 110 0x6e 156

O 79 0x4f 117 o 111 0x6f 157

P 80 0x50 120 p 112 0x70 160

Q 81 0x51 121 q 113 0x71 161

R 82 0x52 122 r 114 0x72 162

S 83 0x53 123 s 115 0x73 163

T 84 0x54 124 t 116 0x74 164

U 85 0x55 125 u 117 0x75 165

V 86 0x56 126 v 118 0x76 166

W 87 0x57 127 w 119 0x77 167

X 88 0x58 130 x 120 0x78 170

Y 89 0x59 131 y 121 0x79 171

Z 90 0x5a 132 z 122 0x7a 172

[ 91 0x5b 133 { 123 0x7b 173

\ 92 0x5c 134 | 124 0x7c 174

] 93 0x5d 135 } 125 0x7d 175

^ 94 0x5e 136 ~ 126 0x7e 176

_ 95 0x5f 137 C-? 127 0x7f 177

Service d’Informatique

Introduction à la Programmation en C++

Mohammed Benjelloun [email protected]

Année académique 2010-2011

1ère Bachelier

Copies du support des présentations

FACULTE POLYTECHNIQUE DE MONS

1

++- 1M. BENJELLOUN : 2011

UMONS

Mohammed BENJELLOUN

Service d’Informatique

Faculté Polytechnique de Mons

[email protected]

2011

++- 2M. BENJELLOUN : 2011

UMONS

Acquérir les notions fondamentales du C++ ;permettre aux étudiants débutants de se familiariser avec le C++;construire des bons programmes : corrects, efficaces, robustes, relativement

faciles à comprendre, à modifier et à maintenir.

Objectif

Pré-requis Aucun

Lors des séances d'exercices, où la présence est obligatoirecomme aux TPs, un complément de matière sera abordé. On ne peut donc considérer ces documents comme complet.

2

++- 3M. BENJELLOUN : 2011

UMONS

Moodle

C'est en forgeant que l'on devient forgeron, et c'est en programmant que euh... Peut-être …

Codes sources C++ Programmes

Transp. Info I

Énoncé des TPs

Préparez les TPs

Syllabus.pdf

Document ‘’Environnement logiciel’’

Vidéos

120 tests formatifs sur C++

Note relative à l’organisation de l’examen pratique

….Utilisation du débuggeur

https://applications.umons.ac.be/moodle/

++- 4M. BENJELLOUN : 2011

UMONS

Le langage C++ !!Qu'est-ce que c’est ?

D'où vient-il ?

Que peut-on faire avec et à quoi ça sert ?

De quoi ai-je besoin pour programmer en C++ ?

Caractéristiques du C++

• Langage structuré: tâches d'un programme � en blocs.• Programmes efficaces : génère un code compact et rapide…• Modulaire : découpe en modules • Objets : meilleure qualité de programmation

3

++- 5M. BENJELLOUN : 2011

UMONS

Les autres types seront dérivés de ceux-ci.

Types de base en C++

0.0 1.0E–101.0 -1.34567896

48

+-10 E-37 à +-10 E+38+-10 E-307 à

+-10 E+308

Réel simple précisionRéel double précisionRéel précision étendue

floatdoublelong double

réels

0 1 -1 4589 32000

2 ou 4-231 à 231 –10 à 232

-215 à 215 –1

entier signé (par défaut)entier positifspécifie la taille de l'emplacement mémoire utilisé

signed int ou intunsigned intshort ou long

entiers

'a' 'A' 'Z' '\n''\t'

1-128 à 1270 à 255

Un caractère uniquesigned char ou charunsigned char

caractères

ExempleTaille en octets

IntervalleSignification ReprésentationType

sizeof(i); retourne la taille en octet de la variable i

booléen : bool qui s'accompagne des mots-clés true et false.

string : un tableau de caractères.

++- 6M. BENJELLOUN : 2011

UMONS

void main(void)

{

// corps du programme

déclaration des Cstes et Var ;

instruction1 ;

instruction2 ;

}

Tout programme doit avoir un point d’entrée nommé main()

Pour commencer

void main(void): La fonction main ne prend aucun paramètre et ne retourne pas de valeur.

main(void) ≅≅≅≅ main()

/* un commentaire sur une, deuxou plusieurs

lignes */

// sur une seule ligne

// corps du programmedéclaration des Cstes et Var;instruction1 ;instruction2 ;….

int X;X = 2;

4

++- 7M. BENJELLOUN : 2011

UMONS#include <iostream>using namespace std;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -int fonc1(int x); void fonc2(int y);int Var_Glob;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void main(void){ /* début du bloc de la fonction main*/int i, j=5, k; // définition des variables localesi = 0 ; j=fonc1(i) ; fonc2(i) ;} // fin du bloc de la fonction main- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -int fonc1( int var){

….. ;return var ;

}

void fonc2( int t){ /* Corps de la fonction fonc2 */….. ;

}

Directives du préprocesseur : accès avant la compilation

Directives du préprocesseur : accès avant la compilation

DéclarationsDéclarations

Programme

principal

Programme

principal

Définitions des fonctions

Définitions des fonctions

Structure d'un programme C++

++- 8M. BENJELLOUN : 2011

UMONS

Exemple de structure d’un programme C++

#include <iostream>

using namespace std;int Somme(int x, int y);

void main(void) {

int Som;

Som = Somme(7,9); //appel de la fonction

if ( Som > 10 ) {

cout <<" Som > 10 "<< Som<< endl;

}

else {cout <<" Som <= 10 "<< Som;

}

int Somme(int x, int y) { /* Définition de la fonction */

int S; S= x+y;return S ;

}

Directives du préprocesseur : accès avant la compilation

Programme

principal

DéclarationsDéclarations

TraitementsDonnéesStructures de contrôleCommentaires

5

++- 9M. BENJELLOUN : 2011

UMONS

Directives du préprocesseur

est une directive de compilation (précédées par un #)

Exemples :

Gestion des Entrées-Sorties standard <iostream>

Fonctions mathématiques <cmath>

Traitement de chaînes de caractères <string>

Contrôler les paramètres d'affichage <iomanip>

IdentificateursLes identificateurs nomment les fonctions, variables ...Le C++ distingue les minuscules des majuscules.

Exemples: ab, Ab, AB //identificateurs valides et tous différents

Identificateurs valides : xx y1 somme_5 _position NomsIdentificateurs invalides :

3eme commence par un chiffre x#y caractère non autorisé (#) no-commande caractère non autorisé (-) taux change caractère non autorisé (espace)

++- 10M. BENJELLOUN : 2011

UMONS

Un identificateur ne peut pas être un mot réservé du langage :

asm auto bool break case

catch char class const continue

default delete do double else

enum extern float for friend

goto if inline int long

mutable namespace new operator private

protected public register return short

signed sizeof static struct switch

template this typedef union unsigned

using virtual void volatile while

Les mots rmots rééservservééss du langage C++ doivent être écrits en minuscules.

6

++- 11M. BENJELLOUN : 2011

UMONS

Variables : déclarations,Syntaxe : Type identificateur1, id2,…. ;

char c1, c2, c3;int i, j, var_ent;float f, variable;

Exemple:

initialisations

int i=9; ou int i;i=9;

char c1= 'A'; ou char c1;c1= 'A';

Constantes : déclarations + initialisationsSyntaxe : const Type identificateur1=Val1, identificateur2=Val2, …,…. ;

const char c = 'A';const int i=5, var_ent=8;

Exemple: const int i;

i= 5;

L’initialisation des constantes est obligatoire lors de leur déclaration.

++- 12M. BENJELLOUN : 2011

UMONS

Les entrées /sorties : cin et cout

cout, le flux standard de sortie, habituellement associé à l'écran,Syntaxe : cout << expression << expression … ;

cin, le flux standard d’entrée, habituellement associé au clavier,Syntaxe : cin >> valeur >> valeur >> valeur … ;

�#include <iostream>using namespace std; // espace de nommage std pour importer les

// symboles de la bibliothèque standard C++

void main () {int n=2; char C= 'A' ;

cout << " Entier n = "<< n << " ; Entrez un nouveau = " ;cin >> n ;

cout << " Char C = "<< C << " ; Entrez un nouveau = " ;cin >> C ;

cout << "\n L'entier vaut maintenant : " << n << endl;cout << " Le caractere vaut maintenant :" << C << endl ;}

Entier n = 2 ; Entrez un nouveau = 3

Char C = A ; Entrez un nouveau = y

L'entier vaut maintenant : 3

Le caractere vaut maintenant :y

<iostream>

7

++- 13M. BENJELLOUN : 2011

UMONS

cout <<"Qu'il est agreable d'utiliser "

<<"cout en\t C++,\nlorsqu'on l'utilise " <<endl

<<"\t\tproprement\n";

Résultat :Qu'il est agreable d'utiliser cout en C++,

lorsqu'on l'utilise

proprement

\t

\t \t

Génère une nouvelle ligne (newline)

Tabulation horizontale

Tabulation verticale

Retour d’un caractère en arrière (backspace)

Retour chariot (return)

Saut de page (form feed)

\n ou endl

\t

\v

\b

\r

\f

SignificationCaractères

Formatage de la présentation � écran

++- 14M. BENJELLOUN : 2011

UMONSsetw(int) : impose la largeur d'affichage à int caractères. Texte et nombres sont alignés à droite.

setprecision(int): impose le nombre de caractères du nombre affiché.

setfill(char): remplace les espaces précédant le nombre par le caractère char.

#include <iostream>#include <iomanip>using namespace std;void main(){

const double Pi = 3.14159265359 ;cout << Pi << endl ;cout << setprecision(9) << Pi << endl ;cout << setprecision(2) << Pi << endl ;cout << setw(10) << setprecision(2) << Pi << endl ;cout << setw(15) << setfill('.') << setprecision(9) << Pi << endl ;

}

3.141593.141592653.1

3.1.....3.14159265

Formatage de la présentation � écran

8

++- 15M. BENJELLOUN : 2011

UMONS

Les opérateurs arithmétiquesLe C++ propose les opérateurs suivants :

+ addition- soustraction* multiplication/ division% modulo (reste de la division entière )

% ne peut être utilisé qu'avec des entiers

7/2

7.0/2 7/2.0 7.0/2.0}

3

3.5

…void main() {int i = 5, j = 4, k;float f = 5.0, g = 4.0, h;

k = i / j; // k= 5/4 = 1h = f / g; // h = 5.0/4.0 = 1.25h = i / j; // h = 5/4 = 1.000

}

Modulo : C=A%B; C

4%2 = 0; 5%2 = 1; 8%3= 2;

C=A%2 Si C=0 alors A est pair sinon(=1) A est impair

++- 16M. BENJELLOUN : 2011

UMONS

Comparaisons< plus petit

<= plus petit ou égal

> plus grand

>= plus grand ou égal

== égal

!= différent

Les opérateurs logiques

&& et

|| ou (non exclusif)

! non

Contractions d'opérateurs

Pour

+= -= *= /= %=&= |= ^=<<= >>=

a += 15;

a = a + 15;

f /= 9.2;

f = f / 9.2;

i *= j + 5;

i = i * (j + 5);

!

Le résultat d'une expression logique est un booléen. Il vaut true si elle est vraie et false sinon.

Réciproquement, toute valeur non nulle est considérée comme vraie et la valeur nulle comme fausse.

!!

9

++- 17M. BENJELLOUN : 2011

UMONS

Comparaisons< plus petit

<= plus petit ou égal

> plus grand

>= plus grand ou égal

== égal

!= différent

Les opérateurs logiques

&& et

|| ou (non exclusif)

! non

Contractions d'opérateurs+= -= *= /= %=&= |= ^= <<= >>=

a += 15;

a = a + 15;

i *= j + 5;

i = i * (j + 5);!

Le résultat d'une expression logique est un booléen. Il vaut true si elle est vraie et false sinon.

Réciproquement, toute valeur non nulle est considérée comme vraie et la valeur nulle comme fausse.

!!

Incrément et décrément

Pour

++ incrément : i++ ou ++iest équivalent à i += 1 ou i = i + 1

- - décrément

int i = 5, j = 4;char lettre = 'B';

i++; // i vaudra 6--j; // j vaudra 3++i; // i vaudra 7lettre++; // lettre vaudra Clettre --; // lettre vaudra B

++- 18M. BENJELLOUN : 2011

UMONS

Les structures de contrôleAlternative: if-else

Itérations: for, while, do-while

Rupture de Contrôle: break, continue, return …

Choix Multiple: switch-case

Bloc 1 d’instructions

Bloc 2 d’instructions

oui non

Suite du programme

oui non

Suite du programme

Bloc d’instructions

Conditionvraie

oui

non

Bloc d’instructions

programme

Conditionvraie

Suite du programme

programme

Conditionvraie

if-else while

do-while

10

++- 19M. BENJELLOUN : 2011

UMONS

Les décisions - if … else

if (Condition vraie){

BLOC 1 D'INSTRUCTIONS

}else{

BLOC 2 D'INSTRUCTIONS

}

Bloc 1 d’instructions

Bloc 2 d’instructions

oui non

Suite du programme

programme

Conditionvraie

if (Condition vraie) {instruction 1;instruction 2;

}else {

instruction 3;instruction 4;

}

Bloc 1 d’instructions

oui non

Suite du programme

Conditionvraie

if (Condition vraie){

instruction 1;instruction 2;instruction 3;…

instruction N;}

if (Condition vraie)instruction 1;

if ( temperature > 70.0)

cout << " Alarme "<<endl;

if (Condition vraie) instruction A;

elseinstruction B;

if (a<b)min=a;

elsemin=b;

if (i) ≅≅≅≅ if (i != 0)

++- 20M. BENJELLOUN : 2011

UMONS if ( <expr1> ) <bloc1>else if (<expr2>)<bloc2>else if (<expr3>)<bloc3>else if (<exprN>)<blocN>else <blocN+1>

if ( <expr1> ) <bloc1>

else if (<expr2>)<bloc2>

else if (<expr3>)<bloc3>

else if (<exprN>)<blocN>

else <blocN+1>

if emboîtés

� else est associé avec le if le plus proche

if(i >= 0)

if(i > 1000) cout<<" i > 1000 ";

else cout<<" i < 0 \n";

if(i >= 0) {

if(i > 1000) cout<<" i > 1000 ";

} else cout<<" i < 0 \n";

11

++- 21M. BENJELLOUN : 2011

UMONS

for( ; ; ){

............;

............;

............;}

Les itérations – forfor( initialisation ; (Condition vraie); itération )

{/* liste d'instructions */}

(Boucles)

int i, j;float K;

for(i = 0, j = 2, k = 7.5 ; (i < 20) &&(j==2); i++, k-=0.2)

int i,j;

for (i = 0; i <3; i++) {cout<< "i = " << i << endl;

}

cout <<endl<<endl;

for(j = 5; j > 0; j- -)cout<< "j = " << j << endl;

i = 0

i = 1

i = 2

j = 5

j = 4

j = 3

j = 2

j = 1

boucle infinie

++- 22M. BENJELLOUN : 2011

UMONS

La boucle TANT QUE ... FAIRE ...

Boucle pré-testée

Il s'agit de l'instruction while :tant que (expression vraie)faire{ BLOC D'INSTRUCTIONS }

Organigramme:

while (Condition vraie){............; /* bloc d'instructions */............;............;

}

Le test se fait d'abord, le bloc d'instructions n'est pas forcément exécuté.

Syntaxe:

tant que, pas jusqu’à ce que!

(Boucles)

oui non

Suite du programme

Conditionvraie

Bloc 1 d’instructions

12

++- 23M. BENJELLOUN : 2011

UMONS

i=1;while(i<5){cout<<"Interieur " <<i << endl;i++;

}cout<<"Exterieur " <<i << endl;

Exemples

Interieur 1

Interieur 2

Interieur 3

Interieur 4

Exterieur 5

int j = 5;

cout<<"start\n";while(j == 0)

cout<<"j = "<<j--<<endl;cout<<"end\n";

start

end

i=1;

while(i<5);{cout<<"Interieur " <<i << endl;i++;

}

"tant que l'expression est vraieattendre".

itération

++- 24M. BENJELLOUN : 2011

UMONS

do while = REPETER … tant que

do{ /* bloc d'instructions */

............;

............;} while (Condition vraie);Condition

vraie

oui non

Bloc d’instructions

programme

int j = 5;do

cout<<"j = "<<j--<<endl;while(j > 0);cout <<"stop\n";

Suite du programme

(garantit l’exécution au moins une fois)

int j = 5;do {

cout<<"j = "<<j <<endl;j--;

} while(j > 0);cout <<"stop\n";

j = 5

j = 4

j = 3

j = 2

j = 1

stop

13

++- 25M. BENJELLOUN : 2011

UMONS

#include <iostream>

using namespace std;

void main() {

char lettre;

float pourcent;

for (lettre = 'A'; lettre <= 'M'; lettre++)

cout<<lettre;

cout<<endl;

lettre = 'z';

while (lettre >= 'n') {

cout<<lettre;

lettre--;

}

cout<<endl;

for (pourcent = 0.0; pourcent < 0.8; pourcent += 0.1)

cout<<pourcent<<endl;

}

ABCDEFGHIJKLM

zyxwvutsrqpon

0

0.1

0.2

0.3

0.4

0.5

0.6

0.7

++- 26M. BENJELLOUN : 2011

UMONS

#include …

void main() {

bool sortir = false;

char rep;

cout<<"Avant la boucle"<<endl;

while(!sortir) {

cout<<"Dans la boucle"<<endl;

cout<<"Voulez vous quitter (O/N)?"<<endl;

cin>>rep;

if(rep=='O')

sortir=true;

}

cout<<"Apres la boucle"<<endl;

}

#include …

void main() {

bool sortir = false;

char rep;

cout<<"Avant la boucle"<<endl;

do {

cout<<"Dans la boucle"<<endl;

cout<<"Voulez vous quitter (O/N)?"<<endl;

cin>>rep;

if(rep=='O')

sortir=true;

} while(!sortir) ;

cout<<"Apres la boucle"<<endl;

}

?? Et dans le cas : � bool sortir = true;

14

++- 27M. BENJELLOUN : 2011

UMONS

int i, j=1;char a;for (i = -10; i <= 10; i++){

while(j!=0) {cin >> a ;if(a=='x')break;

}}

Pour rompre le déroulement séquentiel d'une suite d'instructions

Instructions d'échappement

return (expression);permet de sortir de la fonction qui la contient

exit (expression);La fonction est interrompue. ‘expression’ : un entier indiquant le code de terminaison du processus

for (i = -10; i <= 10; i++) {if (i == 0) continue;

// pour éviter la division par zéro

cout << 1 / i;}

while (Condition vraie) {............ ;............ ;continue;............ ;............ ;break;............ ;............ ;}

............ ;

++- 28M. BENJELLOUN : 2011

UMONS

switch = AU CAS OU ... FAIRE ...…switch(variable de type char ou int) // au cas où la variable vaut:

{case valeur1: ......; // variable=valeur1 : exécutez ce bloc d'instructions.

.......;break;

case valeur2:........; // variable=valeur2: exécutez ce bloc d'instructions.........;

break;.. // etc ....

default: .......; /* aucune des valeurs précédentes: exécutez ce bloc........; d'instructions, pas de "break" ici.*/

}

Le bloc "default" n'est pas obligatoire. valeur1, valeur2, …. doivent être des expressions constantes. L’instruction switch correspond à une cascade d’instructions if ...else

15

++- 29M. BENJELLOUN : 2011

UMONS

….

void main( ) {

const float PI= 3.14159;float rayon = 3.5;float diametre, circonference, surface;int choix;

cout << "1. Calculer la circonference\n";cout << "2. Calculer la surface\n";cout << "3. Calculer le diametre\n";cout << "Votre choix?";cin >> choix;

switch (choix) {case 1 : circonference = 2*PI*rayon; break;case 2 : surface = PI*rayon*rayon; break;case 3 : diametre = 2*rayon; break;default : cout << "Mauvais choix…\n";

}}

char choix;

switch(choix)

{

case '1': …

!switch(i) {case 2 * j:

....

float f;

switch(f) {case 2:

....

switch : instruction commode pour les "menus"

++- 30M. BENJELLOUN : 2011

UMONS

Tableaux et StringsUn tableau est une collection de variables de même type, appelées éléments

Type Nom_Tableau[dim];

int tab[4]; déclare un tableau de 4 valeurs entières tab[0] tab[1] tab[2] tab[3]

const int SIZE = 5;int A[SIZE] // A est un vecteur de 5 entiers float B[5]

int A[SIZE] = { 10, 20, 30, 40, 50 };int premier[] = { 1, 2, 3, 5, 7, 11, 13 };char Tab_Char[4] = { 'A', 'C', 'F', 'G' };int Tab[50] = { 0 };

int i = 10;int a[i];int primes[];

Déclaration

Initialisation

Interdiction

Constante

16

++- 31M. BENJELLOUN : 2011

UMONS

Accès aux éléments d’un tableauLes éléments sont numérotés de 0 à dim-1

Il n’y a pas de vérification des bornes

#include …

void main() {int a[6];int i = 7;

a[0] = 9; a[5] = -10;a[i/2] = 2;

a[6] = 0;a[-1] = 5;

}

0

a

1

2

3

4

5

9

?

?

2

?

-10

void main( ) {

const int dim=6;

int i;

int A[dim] = { 1, 2, 3, 4, 5, 6 };

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

A[i] = 2*i;

cout<<"A["<<i<<"]="<<A[i];

}

}

A[0]=0 A[1]=2 A[2]=4 A[3]=6 A[4]=8 A[5]=10

++- 32M. BENJELLOUN : 2011

UMONS

Chaînes de caractères ou String

char Tab[dim];InitialisationPour terminer la chaîne, on place en fin de chaîne le caractère nul '\0', de code ASCII 0.Ce caractère est soit ajouté automatiquement par le compilateur, soit introduit par le programmeur, selon les fonctions utilisées.

char S[] = {'H','e','l','l','o','\0'}; 'H' 'e' 'l' 'l' 'o' '\0'

S[0] S[1] S[2] S[3] S[4] S[5] char S[6] = "Hello";

char S[5] = "Hello"; 'o''l''l''e''H'

char S[] = "Le main"; '\0''n''i''a''m'' ''e''L'

• Un tableau de char à une dimension : char Tab[dim];

• Un type spécial string : string Tab; // Il faut inclure <string>

17

++- 33M. BENJELLOUN : 2011

UMONS

string Tab; string Tab est équivalente à string Tab = "";Donc par défaut la chaîne est vide.

Il existe de nombreuses façons d’initialiser Tab, par exemple :

string s1 = "Bonjour a vous";

string s2 = s1; // s2 contient " Bonjour a vous "string s3(4, 'x'); // équivaut à string s3 = "xxxx"string s4(s1, 4, 8); // s4 contient "our a vo“ :: S1 de 4 + 8

B o n j o u r a v o u s0 1 2 3 4 5 6 7 8 9 10 11 12 13

#include <string>…string ST = "Salut";char H[6] = "Hello";for (int i=0; i<5; i++)

cout << ST[i] << " "; // affichera : S a l u tfor (int i=0; i<5; i++)

cout << H[i] << " "; // affichera : H e l l o

++- 34M. BENJELLOUN : 2011

UMONS

Pourquoi utiliser string Tab; plutôt que char Tab[dim]; ?

char S1[6] = "Salut", S2[6] = "Hello", S3[12];

S1= S2; S1= "Hooo"; S3=S1+S2; if(S1= =S2) … Interdit

string S1 = "Salut", S2 = "Hello", S3;

S1= S2; S1= "Hooo"; S3=S1+S2; if(S1= =S2) … Autorisé

S1= "Hello"

S1= "Hooo"

S3= "HoooHello"

false

18

++- 35M. BENJELLOUN : 2011

UMONS

#include <iostream>#include <string> // traitement de string

using namespace std; // espace de nommage std pour importer les symboles

// de la bibliothèque standard C++

void main(void) { string nom1, nom2, nom3 ; int len, size; cout << "Donnez Nom1 : " ; cin >> nom1;cout << "Donnez Nom2 : " ; cin >> nom2;

len = nom1.length(); // nombre de caractères dans nom1

size = nom2.size(); // nombre de caractères dans nom2

cout << "Nom1 : "<<nom1 << " Dim Nom1 = " << len << endl;cout << "Nom2 : "<<nom2 << " Dim Nom2 = " << size << endl;nom3 = nom2+ " " +nom1;cout << "Nom3 = "<< nom3 << endl;if(nom1>nom2) cout << "nom1 > nom2" << endl;else if (nom1==nom2) cout << "nom1 = nom2" << endl;

else cout << "nom1 < nom2" << endl;

nom1=nom2;cout << "nom1 = " << nom1 << " nom2 : " << nom2 << endl;

}

Donnez Nom1 : salut

Donnez Nom2 : hello

Nom1 : salut Dim Nom1 = 5

Nom2 : hello Dim Nom2 = 5

Nom3 = hello salut

nom1 > nom2

nom1 = hello nom2 : hello

Donnez Nom1 : csou

Donnez Nom2 : pi

Nom1:csou Dim Nom1 = 4

Nom2:pi Dim Nom2 = 2

Nom3 = pi csou

nom1 < nom2

nom1 = pi nom2 : pi

++- 36M. BENJELLOUN : 2011

UMONS

Tri à bullesbubble sort

répéterpermutation = FALSE;commencer au début du drapeau;

Pour i variant de 0 à N – 2 faire si cellule [i] ">" cellule [i+1]alors

débutpermuter cellules;permutation = TRUE;fin

jusqu'à ce que (not permutation)

Complexité : O(N²) Proportionnel à N² permutations

while(???){

for j = 0 to …. {

if (tab[j] < tab[j+1] ) {

<on échange tab[j] et tab[j+1]>

}

}

}

19

++- 37M. BENJELLOUN : 2011

UMONS

Recherche dichotomiqueLa recherche dichotomique (binary search) est une méthode efficace pour retrouver un élément

dans un tableau trié.

A chaque étape de la recherche dichotomique, l’intervalle de recherche est divisé par deux.

On utilise dès lors deux index de recherche : la borne inférieure et la borne supérieure.

� On initialise les bornes inf et sup à la première et dernière valeur des index du tableau tabrespectivement

� Le milieu est donné par

mid = (inf+sup) / 2; (0+6)/2 =3

� Supposons que l’on recherche la valeur i=6. On compare cette valeur avec tab[mid]=8. Comme tab[mid]>i, on change mid en sup.

� mid = (inf+sup) / 2; (0+3)/2 =1

� inf=1 et sup=3

� …

0

2

1

4

2

6

3

8

4

10

5

12

6

14

inf supmid

0

2

1

4

2

6

3

8

4

10

5

12

6

14

inf sup

++- 38M. BENJELLOUN : 2011

UMONS

Matrices� Rangées ligne par ligne� Considérées comme des vecteurs de lignes� Accès aux composantes par double crochets

int a[10][5];a[1][0] = 7;

int x[2][2] = {{1,2},{3, 4}}; // 2 lignes et 2 colonnes

1 2 3 4

#include …

void main(){

int i, j, tab[5][4];

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

for (j = 0; j < 4; j++) tab[i][j] = i;

}

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

for (j = 0; j < 4; j++) cout<< tab[i][j] << ' ' ;

cout << endl;

}

}

4444

3333

2222

1111

0000

20

++- 39M. BENJELLOUN : 2011

UMONS4.4. Écrire un programme qui supprime le premier et le dernier élément d’un vecteur. vecteur initial : Tab ���� 0 1 2 3 4 5 6

sup. bords ���� 1 2 3 4 5 sup. bords ���� 2 3 4

4.5. Écrire un programme qui supprime le milieu du tableau et ajoute son double au début. vecteur initial : Tab ���� 1 2 3 4 5

sup. Aj ���� 6 1 2 4 5 sup. Aj ���� 4 6 1 4 5

4.6. Codez un programme de tri à bulles d'un vecteur de nombres réels et de strings.

4.7. Codez un programme de recherche dichotomique d'un nombre dans un vecteur de strings rangés par ordre croissant.

++- 40M. BENJELLOUN : 2011

UMONS

Fonctions en C++• Encapsule un traitement particulier formant un tout• Peut implémenter la notion de module en logique• Notion de librairie• Augmente la lisibilité d’un programme• Réalise un objectif précis• Améliore le débogage et la maintenance d’un programmeSon utilisation se décompose en trois phases :

• Définition de la fonction• Déclaration de la fonction• Appel de la fonction

Déclarer une fonction

Appeler une fonction

Règles de visibilité des variables

Passage des paramètres par valeur

Fonction renvoyant une valeur au programme

Passage des paramètres par valeur et par adresse

Passage des tableaux aux fonctions

21

++- 41M. BENJELLOUN : 2011

UMONSType nom_fonction(liste_param_typés)

{//déclaration de variables locales

…. /* Corps de la fonction */

….

return (valeur);}

Définition d’une fonction

Une fonction renvoie une valeur ou rien

liste_param_typés = 0, void ou plusieurs

int Somme(int x, int y) {int S; // Variable locale à Somme

S= x+y;return S ;

}

void fonction1(){cout<<"\n fonction1 \n";void fonction2(){cout<<"\n fonction2 \n";

}}

L'imbrication de fonctions n'est pas autorisée

Définition

++- 42M. BENJELLOUN : 2011

UMONS

Fonctions pourquoi ?

#include <iostream>

using namespace std;

void main(){

int i, j, SomCar1=0, SomCar2=0, SomCar3=0;

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

SomCar1 = SomCar1 + i*i ;

}

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

SomCar2 = SomCar2 + i*i ;

}

for (j = 0; j < 7; j++) {

SomCar3 = SomCar3 + j*j ;

}

cout<<" SC1="<<SomCar1<<" SC2="

<< SomCar2 <<" SC3="<<SomCar3;

}

#include <iostream>

using namespace std;

int Fnct_SomCar(int N){

int i, S=0;

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

S = S + i*i ; //S+=i*i;

return S;

}

void main(){

int SomCar1, SomCar2, SomCar3;

SomCar1 = Fnct_SomCar(5) ;

SomCar2 = Fnct_SomCar(10) ;

SomCar3 = Fnct_SomCar(7) ;

cout<<" SC1="<<SomCar1<<" SC2="

<< SomCar2 <<" SC3="<<SomCar3;

}

22

++- 43M. BENJELLOUN : 2011

UMONS…

int Somme(int x, int y);

void main(void) {

int a=5; b=6, Som;

Som = Somme(7,7);

cout <<" Som = "<< Som<< endl;cout <<" Som = "<< Somme(a,b);

}

int Somme(int x, int y) {int S; S= x+y;return S ;

}

int Somme(int x, int y) {int S; S= x+y;return S ;

}

void main(void) {

int a=5; b=6, Som;

Som = Somme(7,7);

cout <<" Som = "<< Som<< endl;cout <<" Som = "<< Somme(a,b);

}

Déclaration et appel d’une fonction

++- 44M. BENJELLOUN : 2011

UMONS

void main(void)void main(void){

int Val ;Val = addition();cout << "val = "<< Val<<endl;

}

int addition(){

float tmp;tmp = calcule(2.5,3) + calcule(5,7.2);return (int)tmp;

}

float calcule(float C, float D){

return ( (C + D ) / 2) ;}

float calcule(float, float);int addition();

float calcule(float A, float B){

return ( (A + B ) / 2) ;}

int addition(){ // Appel d’une fonction dans une fonction

float tmp;tmp = calcule(2.5,3) + calcule(5,7.2);return (int)tmp;

}

void main(void)void main(void){

int Val ;Val = addition();cout << "val = "<< Val<<endl;

}

Déclaration et appel d’une fonction

23

++- 45M. BENJELLOUN : 2011

UMONS

#include <iostream>

using namespace std;

void Addition(int v, float f);

void main(){

int v = 5;

float f= 4.5 ;

Addition(v, f); // Appel de la fonction

cout << "\n Main: v et f " << v << "et " <<f;

}

void Addition(int v, float f){

cout << " Addition: v + f = "<< v+f << endl;

f = f + v;

cout << " Addition: f = "<< f ;

}

Addition : v+f = 9.5

Addition : f = 9.5

Main: v et f = 5 et 4.5

v= 5 et f=4.5

5+ 4.5f = 9.5

Les paramètres sont copiés. La fonction travaille sur des copies de v et de f.

++- 46M. BENJELLOUN : 2011

UMONS

int Fct_test() {

int i, j=1;char a;for (i = -10; i <= 10; i++)

{

while(j!=0) // boucle infinie{

cin >> a;if(a=='x')

break;if(a==‘Q')

return(1);} // while

}// for return (0);}

24

++- 47M. BENJELLOUN : 2011

UMONS

FONCTION RENVOYANT UNE

VALEUR AU PROGRAMME

#include <iostream>

using namespace std;int change(int X);

void main(void){int var = 5;int valeur;valeur = change(var);

cout <<"main: var = "<< var << endl;cout <<"main: valeur = "<< valeur;

}

int change(int v){

v = 100;cout <<"change: v = "<< v << endl;return (v+1);

}

change: v = 100main: var = 5main: valeur = 101

Une fonction se termine et ‘rend la main’ àla fonction appelante lorsque son exécution rencontre l’instruction: return expression;ou return;

++- 48M. BENJELLOUN : 2011

UMONS#include <iostream>using namespace std;

int return_Val(int v);

void main(void){int var = 5, valeur;

valeur = return_Val(var);

cout << "main: var = " << var << endl;cout << "main: valeur = "<< valeur;

}

int return_Val(int v){

if (v == 10) return (2*v);else return (3*v);

}

#include <iostream>using namespace std;

int return_Val(int v);

void main(void){int var = 5;int valeur;valeur = return_Val(var);

cout << "main: var = " << var << endl;cout << "main: valeur = "<< valeur;

}

int return_Val(int v) {if (v == 10) return (2*v);else return (3*v);cout <<" return_Val v = "<< v<<endl;

}

Qu’affiche ce programme à l’écran ? Qu’affiche ce programme à l’écran ?

25

++- 49M. BENJELLOUN : 2011

UMONS

Une fonction est dite récursive lorsqu’elle s’appelle elle-même.

#include …int fact(int n);int return_Val(int v);

void main(void){int var = 5, valeur;valeur = return_Val(var);cout << "main: var = " << var << endl;cout << "main: valeur = "<< valeur;

}

int fact(int n) {return (n<2?1:((int)n * fact(n-1));

}

int return_Val(int v){

if (v == 10) return (2*v);else return fact(v);

}

main: var =

main: valeur =

L’opérateur ternaire “?:”

(condition) ? (instruction_1) : (instruction_2)

s’écrit:

if (n<2 ) return 1;

else return ((int)n*fact(n-1));

int fact (int n ) {

if ( n < 0) return –1; //code d'erreur

else if ( n == 0 ) return 1; // 0! = 1

else

return n*fact( n-1 ); // n! = n*(n-1)!

}

++- 50M. BENJELLOUN : 2011

UMONS

Passer des tableaux aux fonctions� Les tableaux peuvent être passés comme paramètres d'une fonction.

� Ils ne peuvent pas être retournés comme résultat d'une fonction.

� La longueur du tableau ne doit pas être définie à la déclaration de la fonction.

� Un tableau peut être modifié dans une fonction. Il est passé par référence (adresse) et non par valeur.

!

#include …

void Modif(int a[]){a[0] = 5;a[1] = 6;

}

void main(void){void main(void){int p[2] = { 1, 2 };

cout << p[0] << " ; " << p[1];

Modif(p);

cout << p[0] << " ; " << p[1];}

1 ; 2

5 ; 6

#include …void Modif(int x[], int n);

void main(void){int i;int p[6] = { 1, 2, 3, 5, 7, 11 };Modif(p, 6);for (i=0;i<6;i++)

cout<< p[i];}

void Modif(int a[], int n){int i; for(i = 0; i <n ; i++)

a[i] = 5;}

26

++- 51M. BENJELLOUN : 2011

UMONSvoid Saisie_Mat( int tab[][4], int m, int n) {

int i, j;

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

for (j = 0; j < n; j++)

tab[i][j] = i;

}

3333

2222

1111

0000

Matrice Exemple

void Affiche_Mat( int tab[][4], int m, int n) {

for (int i = 0; i <m; i++) {

for (int j = 0; j < n; j++)

cout << tab[i][j] << " ";

cout << endl;

}

}

#include <iostream>using namespace std;void main(){

int T[5][5];

Saisie_Mat( T , 4, 4 );

Affiche_Mat( T , 4, 4 );

}

++- 52M. BENJELLOUN : 2011

UMONS

void Affichage( int A[], int N) {for (int i=0;i < N ;i++) {

cout << " A["<<i<<"]= "<<A[i] <<endl;

}}

#include …

void main() {const int Nmax = 7;int i, A[Nmax], S=0, N=3;

S = LaSomme( );

cout << " Affichage :" << endl;

Affichage(A, N);

cout << "\n La somme est = "<< S ;}

int LaSomme( ) {int A[Nmax], N;int S = 0; for (int i=0;i< N ;i++) {

cout <<"Entrez A["<<i<<"]=";cin >>A[i];S= S+ A[i];}

return S;}

Qu’affiche ce programme à l’écran ?

27

++- 53M. BENJELLOUN : 2011

UMONS

#include …

void Saisie_T( int tab[] ) {

int N, i;

N= 3;

for (i = 0; i <N; i++) tab[i] = i;

}

void Affiche_T(int tab[], int N ) {

int i;

for (i = 0; i <N; i++) cout << tab[i];

}

void main(){

int T[20], n;

Saisie_T( T );

Affiche_T(T, n );

}

tab[0] = 0tab[1] = 1tab[2] = 2

Qu’affiche ce programme à l’écran ?

++- 54M. BENJELLOUN : 2011

UMONS

#include …

int return_Val(int , int);

void main(void){

int var1 = 5, var2=0;

int valeur;

valeur = return_Val(var1, var2);

cout << "var1 = " << var1

<< " var2 = "<<var2 << endl;

cout << "main: valeur = "<< valeur;

}

int return_Val(int v1, int v2) {

v1 = 10;

v2 = 7;

return …;}

Comment retourner v1 et v2?

28

++- 55M. BENJELLOUN : 2011

UMONS #include <iostream> …

void affiche (int a, int b) {cout<<" a = " << a << endl << " b = " << b ;}

void echange(int x,int y){int tmp; tmp = x; x = y;y =tmp;

}

void main() {int a = 1 , b = 5; cout << "Avant " <<endl;affiche(a,b);echange(a,b);cout << "\nAprès " <<endl;affiche(a,b);

}

Avant

a = 1

b = 5

Après

PASSAGE DES PARAMETRES

Transmission des arguments

Comment échanger a et b ?

a = 1

b = 5

PAR VALEUR

++- 56M. BENJELLOUN : 2011

UMONS

Le pointeur prend comme

valeurs des adresses de

données en mémoire

���� ����

Types de base ….donnée35

donnée8

….

Adr i Adr j

Mémoire

12FF80 12FF84

L'adresse de l'élément i est &i � i=8 et &i= 12FF80

int i=8, j=35;

Et les pointeurs ?

p=12FF84…. ….35….

12FF8412FF80

int *p; *p est le contenu pointé par p � 35p est l’adresse � 12FF84

29

++- 57M. BENJELLOUN : 2011

UMONS

Déclaration de PointeursLe symbole * est utilisé entre le type et le nom du pointeur

Déclaration d’un entier: int i;int *p;Déclaration d’un pointeur vers un entier:

Exemples de déclarations de pointeursint *pi; // pi est un pointeur vers un int ; *pi désigne le contenu de l'adresse

float *pf; // pf est un pointeur vers un float

char c, d, *pc; // c et d sont des char; pc est un pointeur vers un char

int C = 4;

int *p;

p = &C; // p reçoit l'adresse de c; donc pointe sur c.

cout << "*p = "<<*p<<endl;

cout << "p = "<<p <<endl;

cout <<" *(&C) "<<*(&C);

*p = 4

p = 0x12FF7C

*(&C) = 4

L'adresse de C est &C

++- 58M. BENJELLOUN : 2011

UMONS

���� ����

Une référence vers un objet permet de définir un nouveau nom, un alias, pour désigner l’emplacement mémoire de l’objet référencé. En tant qu'alias, la référence doit impérativement être initialisée, avec l'objet référencé.

int i = 35, j;int &ref = i ; /* ref une référence sur la variable i. permet au programme de manipuler i

sous un autre nom que celui sous lequel elle a été déclarée.

i et ref deux identificateurs qui représentent la même variable */

int &ErRef; // INTERDIT : une référence doit être initialisée

j = ref; // j = la valeur de l'objet référencé par ref� (j = i = 35)

ref = 100 ; // ref=i= 100 ; j reste à 35

i = j ; // ref=i=j = 35

Les Références en C++

30

++- 59M. BENJELLOUN : 2011

UMONS#include …

void Modifier(int v);

void main(){

int v = 5;

Modifier(v);

cout << "\nmain: v = " << v;

}

void Modifier(int v){

v = v *100;

cout << "Modifier: v = "<< v;

}

#include …

void Modifier(int *v);

void main(){

int v = 5;

Modifier(&v);

cout << "\nmain: v = " << v;

}

void Modifier(int *v){

*v = *v *100;

cout << "Modifier: *v = "<< *v;

}

#include …

void Modifier(int &v);

void main(){

int v = 5;

Modifier(v);

cout << "\nmain: v = " << v;

}

void Modifier(int &v){

v = v *100;

cout << "Modifier: v = "<< v;

}

par Valeur Pointeur Référence

Appel par valeurs, pointeurs, références ??!!

var = 5

var = 500

Modifier: v = 500

main: v = 5v = 5

Modifier: *v = 500

main: v = 5v = 50000

Modifier: v = 500

main: v = 5v = 50000

var = 5

var = 500

++- 60M. BENJELLOUN : 2011

UMONS

#include …

void fonct (int a){

a=1 ;}

void main(void){int var = 5;fonct (var);cout << var << endl;

}

5

#include …

int fonct (int a){

a=1 ;return a;

}

void main(void){int var = 5;var = fonct (var);cout << var << endl;

}

1 1

#include …

void fonct (int *a){

*a=1 ;}

void main(void){int var = 5;fonct (&var);cout << var << endl;

}

#include …

void fonct (int &a){

a=1 ;}

void main(void){int var = 5;fonct (var);cout << var << endl;

}

1

31

++- 61M. BENJELLOUN : 2011

UMONS

i = 1 j = 5i = 5 j = 1

i = 1 j = 5i = 5 j = 1

i = 1 j = 5i = 1 j = 5

void echange (int&, int&);void main () {

int i= 1, j=5;affiche (i, j);echange (i, j);affiche (i, j);

}void echange (int &a, int &b) {

int tmp;tmp = b;b = a;a = tmp;

}

void echange (int*, int*);void main () {

int i= 1, j=5;affiche (i, j);echange (&i, &j);affiche (i, j);

}void echange (int *a, int *b) {

int tmp;tmp = *b;*b = *a;*a = tmp;

}

void echange (int, int);void main () {

int i= 1, j=5;affiche (i, j);echange (i, j);affiche (i, j);

}void echange (int a, int b){

int tmp;tmp = b;b = a;a = tmp;

}

#include <iostream> …void affiche (int a, int b) {

cout<<"\t i = " << a << " j = " << b << endl;}

++- 62M. BENJELLOUN : 2011

UMONS

#include <iostream> …

int return_Val(int , int);

void main(void){

int var1 = 5, var2=0;

int valeur;

valeur = return_Val(var1, var2);

cout << "var1 = " << var1

<< " var2 = "<<var2 << endl;

cout << "main: valeur = "<< valeur;

}

int return_Val(int v1, int v2) {

v1 *= 10;

v2 += 7;

return …;

}

Comment retourner v1 et v2?

#include <iostream> …

void return_Val(int &, int&);

void main(void){

int var1 = 5, var2=0;

int valeur;

valeur = return_Val(var1, var2);

cout << "var1 = " << var1

<< " var2 = "<<var2 << endl;

cout << "main: valeur = "<< valeur;

}

void return_Val(int &v1, int &v2) {

v1 *= 10;

v2 += 7;

return …;

}var1 = 50 var2 = 7

32

++- 63M. BENJELLOUN : 2011

UMONS

#include <iostream> …

void somme(int , int , int &);

int modif(int , int &, int &);

void main(){

int a, b, c;

a = 2; b = 8;

somme(a, b, c);

cout <<"Somme de a="<<a<<" et b="<<b<<" : " << c << endl;

a = modif(a, b, c);

cout << "Modif : a="<<a<<" et b="<<b<<" : " << c << endl;

}

void somme(int x, int y, int &z){

z = x + y;

y = 29;

}

int modif(int x, int &y, int &z){

x *= 2; y= x+ y; z= 5;

return x;

}

Somme de a= et b= :

Modif : a= et b= :

Qu'affiche le code à l'écran ?

++- 64M. BENJELLOUN : 2011

UMONS

Si x=1Si x =2Si x=3Si x=4Si x=5

…int Fonct1(int y){

y = 10; return (y);}void Fonct2(int y){

y = 11; exit(0);}void Fonct3(int y){

y = 12;}

void Fonct4(int &y){y = 13;

}

void main() {int x=0, y=0;cin >> x;

if (x== 1) y = Fonct1(y);if (x== 2) Fonct2(y);if (x== 3) return ( );if (x== 4) Fonct4(y);if (x== 5) Fonct3(y);

cout <<"ICI y="<<y ;}

33

++- 65M. BENJELLOUN : 2011

UMONS

#include <iostream> using namespace std;

void affiche(int a, int b) { cout << "Deux entiers:" << a << "et" << b << '\n';

}void affiche(float a, float b) {

cout << "Deux reels:" << a << "et" << b << endl; }void affiche(int c) { cout << "Un entier:" << c << '\n';

}void main() {

affiche (5.2 , 6.3);affiche (1 , 2);affiche (100);

}

Surcharge des fonctionsLe C++ permet d’utiliser des fonctions qui portent le même nom mais pas le même type et/ou le nombre de paramètres.

++- 66M. BENJELLOUN : 2011

UMONS

Paramètres par défaut

#include <iostream> using namespace std;

void affiche(int un = 1, int deux = 2, int trois = 3) {

cout << un << ' ' << deux << ' ' << trois << '\n';

}

void main(void) {

affiche(1, 2, 3);

affiche(5, 6, 7);

affiche(100, 200);

affiche(1000);

affiche();

}

1 2 35 6 7100 200 31000 2 31 2 3

34

++- 67M. BENJELLOUN : 2011

UMONS

Règles de visibilité des variables

#include <iostream>using namespace std;

void main() {const float PI= 3.14159;float rayon = 3.5;float circonference, surface;int choix;

cout << "1. Calculer la circonference\n";cout << "2. Calculer la surface\n";

cout << "Votre choix?";cin >> choix;switch (choix) {

case 1 : rayon = 0;circonference = 2*PI*rayon;cout << circonference << endl; break;

case 2 : surface = PI*rayon*rayon;cout << surface << endl; break;

}}

Le C++ est un langage structuré en blocs { } , les variables ne peuvent être utilisées que là où elles sont déclarées.

++- 68M. BENJELLOUN : 2011

UMONS

Règles de visibilité des variables

#include …

int globale=0;

void fonc(int v) {double d, f; i++;globale --;

}

void main(void) {int i = 5, j; float f = 2.8, g;d = 3.7;globale =10;cout << " valeur de j= " << j ; cout << "\nglobale = " << globale ;fonc (i);cout << "\nglobale = " << globale ;

}

(0)

(1)(2)

(3)(4)(5)

#include …int g; void affichage(int un, int deux) {

cout << un << " " << deux << " " << g << endl;

}void fonct(int un, int deux){

affichage(un, deux);un += 2; deux += 2; g += 2;affichage(un, deux);

} void main(void) {

int i = 5, j; affichage(i, j);j=10; fonct(i, j);affichage(i, j);

}

5 -858993460 0

5 10 0

7 12 2

5 10 2

// Ou 0 .net

VC++ 6

35

++- 69M. BENJELLOUN : 2011

UMONS

#include …const int Nmax=20;void main() {

int i,N=3,tab[Nmax];

saisie(tab,N);

for(i=0;i<N;i++)cout <<tab[i];

}

void saisie(int tab[], int N) {int i;for(i=0;i<N;i++)

cin>>tab[i];N=15;

}

mon_programme.cppMémoire utilisée par mon_programme.cpp

Nmax=20 i N=3

Mémoire pour saisie()

i N=3N=15

Tab[Nmax] 5 -1 9

i=0i=1i=2

i=0i=1i=2

i=3

++- 70M. BENJELLOUN : 2011

UMONS

Allocation dynamique de la mémoireTableaux ≡ réserver plus de places en mémoire que nécessaire.

Création d’un tableau de taille quelconque ����

l'allocation dynamique

#include <iostream> …void affiche(int T[], int d, char C[]){

for(int i=0; i<d; i++)cout<< "\n T["<<i<<"] = " << T[i] << " C["<<i<<"] = " <<C[i];

cout << endl;}void main(){

int N, *T, i;cout << " N = " ; cin >> N;char *C = new char[N];T= new int[N]; affiche(T, N, C);for(i=0; i<N; i++){

T[i]=i;C[i]='A'+i;

}affiche(T, N, C);delete[] T; affiche(T, N, C);delete[] C; affiche(T, N, C);

}

T[0] = -842150451 C[0] = ◙

T[1] = -842150451 C[1] = ◙

T[2] = -842150451 C[2] = ◙

T[0] = 0 C[0] = A

T[1] = 1 C[1] = B

T[2] = 2 C[2] = C

T[0] = -572662307 C[0] = A

T[1] = -572662307 C[1] = B

T[2] = -572662307 C[2] = C

T[0] = -572662307 C[0] = ¦

T[1] = -572662307 C[1] = ¦

T[2] = -572662307 C[2] = ¦

36

++- 71M. BENJELLOUN : 2011

UMONS

Tableau de taille quelconque

#include <…

void saisie_vect(int Tab[], int n) {

for(int i=0;i<n;i++)

cin >> Tab[i];

}

void Affiche(int Tab[], int dim) {

for(int i=0;i<dim;i++)

cout << "Tab[" << i <<"]="

<< i <<Tab[i];

}void main() {int *tab, n;cout << " N= ";cin >> n;

tab = new int [n];saisie_vect(tab, n);Affiche(tab, n);

delete [] tab;}

#include <…

void main() {

int **T; // pointeur sur un pointeur sur un entier

int i, j, lignes, colonnes;

cout << "\nEntrez le nb de lignes et de colonnes: ";cin >> lignes >> colonnes;T = new int* [lignes]; // Alloc. d’un tableau de pointeurs

// pour chaque ligne, alloc. du nb de col.for (i=0; i<lignes; i++)

T[i] = new int[colonnes];

for (i=0; i<lignes; i++)for (j=0; j<colonnes; j++) {

cout <<"T[" <<i <<"," <<j <<"]: ";cin >> T[i][j];

}//...// Destruction

for (i=lignes-1; i>=0; i--)delete[] T[i];

delete[] T;}

Tableau dynamique à deux dimensions

++- 72M. BENJELLOUN : 2011

UMONS

Les StructuresStructure = ensemble de variables définissant un nouveau type sous

un seul nom.

Les structures sont définies en utilisant le mot-clé struct.

struct Date {int jour;int mois;int an;

};

Déclarer des instances

struct Date paques, semaine[7];

Date noël; // pas de struct

Date nouvel_an = { 1, 1, 2007 }; // Initialisation

37

++- 73M. BENJELLOUN : 2011

UMONS

Structure dans Structurestruct Date{

int jour;int mois;int an;

};

struct Date{

int jour;int mois;int an;

};

struct Etudiant {char nom[30];string prenom;char *adresse;int numero;float Cotes[3];struct Date D_Nais;

};

Etudiant JD = {"Dupont", "Jpp","rue de Houdain, 9, 7000 Mons",102,10.5, 11, 14.5,{ 15, 10, 1986 }

};

Les membres sont accédés par le nom de l’instance, suivi de . , suivi du nom du membre

cout <<"nom = "<< JD.nom;cout <<"\n Cote 0 = "<< JD.Cotes[0]<< endl;cout << " jour de naissance "<<JD. D_Nais.jour;

++- 74M. BENJELLOUN : 2011

UMONS

#include …

struct Article{

string nom;

int prix;

};

void Affiche(Article AR){

cout << "\n\t Nom = " << AR.nom << " Prix = " << AR.prix;

}

void main(void) {

Article X, T[5];

cout << "Entrez le nom : "; cin >> X.nom;

cout << " \nEntrez le prix "; cin >> X.prix;

Affiche(X);

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

cout << "Entrez le nom : "; cin >> T[i].nom;

cout << "\nEntrez le prix "; cin >> T[i].prix;

Affiche(T[i]);

}

}

…nom4prix4

nom3prix3

nom2prix2

nom1prix1

T[0] T[1] T[2]

Et si nom contient un espace ?Exp. GSM 123

38

++- 75M. BENJELLOUN : 2011

UMONS

#include …

struct Article{

string nom;

int prix;

};

void Affiche(Article AR){

cout << "\n\t Nom = " << AR.nom

<< " Prix = " << AR.prix;

}

void Saisie(Article AR){

cout << "Entrez le nom : "; cin >> AR.nom;

cout << "\nEntrez le prix :"; cin >> AR.prix;

}void main(void) {

Article X;

Saisie(X);

Affiche(X);

}

Entrez le nom : Television

Entrez le prix : 300

void Saisie(Article &AR){

Entrez le nom : Television

Entrez le prix : 300

Une structure peut être passée, comme une autre variable, par valeur ou par adresse.

???

???

++- 76M. BENJELLOUN : 2011

UMONS

#include …

struct Article{

string nom;

int prix;

};

void Saisie(Article &AR){

cout << "Entrez le nom : "; cin >> AR.nom;

cout << "\nEntrez le prix :"; cin >> AR.prix;

}

void main( ) {

Article T[5];

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

Saisie(T[i]);

}

T[i]=Nouv();

Article Nouv(){

Article AA;

cout << "Entrez le nom : "; cin >> AA.nom;

cout << "\nEntrez le prix :"; cin >> AA.prix;

return AA;

}

L’opération d’affectation = peut se faire avec des structures

39

++- 77M. BENJELLOUN : 2011

UMONS

Gestion des fichiers en C++

Pour manipuler un fichier en C++ il faut #include <fstream>

On y trouve essentiellement les classes:• ofstream (output file stream) permet d’éécrirecrire les données dans le fichier ;• ifstream (input file stream) permet de lirelire les données du fichier ;

Ouverture et fermeture d’un fichier :

ofstream output, X;output.open("Res.txt"); ou output.open("c:\\labojeudi\\Res.txt");X.open("Data"); ou X.open("A:\\Data");

Écriture :

Lecture : ifstream input ;input.open("Data.txt"); ou input.open("c:\\labojeudi\\Data.txt");

++- 78M. BENJELLOUN : 2011

UMONS

#include <fstream> …

void main() {

ofstream Ecri;

char Tab1[6]="Hello";

int Tab2[5]={1,2,3,4,5};

Ecri.open("Mon_fichier.txt");

Ecri << "Mes Donnees" << endl;

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

Ecri << Tab1[i] << " "

<< Tab2[i]<<endl;

}

Ecri.close();

}

Mes Donnees

H 1

e 2

l 3

l 4

o 5

#include <fstream> …

void main() {

ifstream Lec;

char Tab1[6], Titre[100];

int Tab2[5], i=0;

Lec.open("Mon_fichier.txt");

Lec.getline(Titre,100); cout << Titre << endl;

while(!Lec.eof()){

Lec >> Tab1[i] >> Tab2[i];

cout << Tab1[i] << " ; " << Tab2[i]<< endl;

i++;

}

Lec.close();

}

Mes Donnees

H ; 1

e ; 2

l ; 3

l ; 4

o ; 5

o ; 5??

Test!

Mon_fichier.txt

40

++- 79M. BENJELLOUN : 2011

UMONSEcri.open("Mon_fichier.txt");

if(Ecri) { // if (Ecri!= NULL)// on vérifie si l'ouverture se passe bien

Ecri << "Mes Donnees" << endl;for(int i=0; i<5; i++){

Ecri << Tab1[i] << " " << Tab2[i]<<endl;

}Ecri.close();

}else { // si échec à l'ouverture

cout<<"Erreur"<<endl;}

if (Ecri.bad()) return 0;

Ecri.fail()

++- 80M. BENJELLOUN : 2011

UMONS

const int NMax = 5;

struct Etudiant{string nom ;int numero ;int Matieres[NMax] ;

} ;

void saisie(Etudiant T[], int n, int NM) {…n?NM?

}

const int NMax = 5;

struct Etudiant{char *nom ;int numero ;int Matieres[NMax] ;

} ;

8.2. et 8.3. Écrire le même programme que 8.1. en remplaçant la structure par :

8.4 Donnez la structure représentant ce tableau :

10.2D11…1010F…nom20

13.9C15…1413M…nom3

15.7V19…2010F33, rue qui n’existe pas, …

nom2

13.3A8…1510M56 rue je ne sais pas, bte 10, …

nom1

MyneCoderesN…res2

res1SexeadresseNom

Écrire un programme permettant de manipuler un tableau de cette structure. Ce programme doit gérer en boucle le menu suivant :

1 - SAISIE et AFFICHAGE du tableau2 - Sauvegarde dans un fichier3 - Lecture fichier 4 - ARRET du programme