Vous êtes sur la page 1sur 112

FACULTÉ POLYTECHNIQUE DE MONS

Service d’Informatique

Introduction à la Programmation
en C++

Mohammed Benjelloun

1ère Bachelier

Année académique 2014-2015


FACULTÉ POLYTECHNIQUE DE MONS

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

M.BENJELLOUN Struct. D.A. 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 Struct. D.A. 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: Listes simplement chaînées 98

Chapitre 10: Quelques erreurs à éviter 102

Annexes 106
Annexe A 107
Ordre de priorité des opérateurs 107
Table des codes ASCII des caractères 108
Annexe B : Support des présentations 111

M.BENJELLOUN Struct. D.A. S. Informatique


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 :
Mohammed.Benjelloun@umons.ac.be
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 standard (Ansi C++) par le comité de normalisation
 2003 Le standard a été amendé.
 2011 Approbation du standard C++11.
 2014 Standard en cours.

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

….
….

M.BENJELLOUN Struct. D.A. S. Informatique


Chap.1 : Introduction 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/

M.BENJELLOUN Struct. D.A. S. Informatique


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++ 6

#include <iostream>
using namespace std; Directives du préprocesseur : accès avant la compilation
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int fonc1(int x);
void fonc2(int y); Déclaration des fonctions
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void main(void)
{ /* début du bloc de la fonction main*/
int i, j=5, k; // définition des variables locales Programme
i=0;
j=fonc1(i) ; principal
fonc2(i) ;
} // fin du bloc de la fonction main
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int fonc1( int var){
….. ;
….. ;
return var ; Définitions des
} fonctions
void fonc2( int t)
{ /* Corps de la fonction fonc2 */
….. ;
….. ;
}

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 */

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 2 : Eléments du langage C++ 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> // Entrées-sorties standard


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).

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 2 : Eléments du langage C++ 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.

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 Exemple


octets
caractères signed char ou char Un caractère unique -128 à 127 1 'a' 'A' 'Z' '\n'
unsigned char 0 à 255 '\t'

entiers signed int ou int entier signé (par défaut) -231 à 231 –1 2 ou 4 0 1 -1 4589
unsigned int entier positif 32
0 à 2 -1 32000
short ou long spécifie la taille de 15 15
l'emplacement mémoire utilisé -2 à 2 –1
réels float Réel simple précision +-10 E-37 à +-10 E+38 4 0.0 1.0E–10
double Réel double précision +-10 E-307 à 8 1.0 -
long double Réel précision étendue +-10 E+308 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

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

M.BENJELLOUN Struct. D.A. S. Informatique


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

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.

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 2 : Eléments du langage C++ 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.

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 2 : Eléments du langage C++ 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
Les opérateurs arithmétiques - soustraction
binaires sont :
* 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 = 6, j = 4, k;
double f = 6.0, g = 4.0, h;

k = i / j; // k = 6/4 = 1
h = f / g; // h = 6.0/4.0 = 1.5
h = i / j; // h = 6/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)

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 2 : Eléments du langage C++ 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 Opérateurs de comparaison


&& et logique (and) == égal à
|| ou logique (or) != différent de
! négation logique (not) <, <=, >, >= plus petit que, ...

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 2 : Eléments du langage C++ 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 Ainsi ces expressions sont équivalentes :


+= ajouter à
X= X + 32 ;  X += 32 ;
-= diminuer de F= F/1.5 ;  F /= 1.5 ;
*= multiplier par I= I *(J+10);  I *= J+10;
/= diviser par Tab[n*i+j]= Tab[n*i+j]*3; Tab[n*i+j] *= 3;
%= modulo

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 2 : Eléments du langage C++ 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.

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 2 : Eléments du langage C++ 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

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 2 : Eléments du langage C++ 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

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

M.BENJELLOUN Struct. D.A. S. Informatique


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

#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 qu’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;
Solution : unsigned char cu;
int i;
char= 1 unsigned int iu;
unsigned char = 1 short int is;
int = 4 unsigned short int isu;
unsigned int = 4 long int il;
unsigned long int ilu;
short = 2
float f;
unsigned short = 2 double d;
long = 4 long double ld;
unsigned long = 4
cout << "\n char= " << sizeof(c) << "\n unsigned char = " << sizeof(cu)
float = 4 << "\n int = " << sizeof(i) << "\n unsigned int = " << sizeof(iu)
double = 8 << "\n short = " << sizeof(is) << "\n unsigned short = " << sizeof(isu)
long double = 8 << "\n long = " << sizeof(il) << "\n unsigned long = " << sizeof(ilu)
<< "\n float = " << sizeof(f)
<< "\n double = " << sizeof(d) << "\n long double = " << sizeof(ld)
<< endl;
}
Programme 2.4
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 :

M.BENJELLOUN Struct. D.A. S. Informatique


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

#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> #include <iostream>


using namespace std; #include <iostream> using namespace std;
void main(void) { using namespace std; void main(void) {
int i = 0; void main(void) { int i = 0;
cout.width(5); int i = 0; cout.fill('.'); // voir résultats
cout << i << '\n'; cout.width(i+3); cout.width(i+3);
i = 1; cout << i << '\n'; cout << i << '\n';
cout.width(5); i = 1; i = 1;
cout << i << '\n'; cout.width(i+3); cout.fill('.'); // voir résultats
} cout << i << '\n'; cout.width(i+3);
} cout << i << '\n';
}

Programme 2.6
Solutions :
0 0 ..0
1 1 ...1
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).

M.BENJELLOUN Struct. D.A. S. Informatique


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

#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

M.BENJELLOUN Struct. D.A. S. Informatique


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

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.

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

M.BENJELLOUN Struct. D.A. S. Informatique


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 :

non n
oui non oui Condition
Condition
vraie vraie

Bloc Bloc Bloc


d’instructions 1 d’instructions 2 d’instructions

Suite du programme Suite du programme

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> if ( <expression logique > )
else <bloc d'instructions >
<bloc d'instructions 2>
Chap. 3 : Les structures de contrôle 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;
}

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 3 : Les structures de contrôle 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 { }.

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 3 : Les structures de contrôle 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.

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 3 : Les structures de contrôle 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.

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 3 : Les structures de contrôle 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; …..) {

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 3 : Les structures de contrôle 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

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 3 : Les structures de contrôle 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

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 3 : Les structures de contrôle 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 :


oui Condition non
vraie while (<expression logique > )
instruction ;
Bloc Suite du programme
d’instruction
s

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++;
}

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 3 : Les structures de contrôle 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 ( ; ; ) { for ( ; ; ) while ((C = cin.get()) != '&')


cin >> C; if ((C = cin.get()) == '&') break; cout.put(C);
if(C=='&') break; else cout << C ; // ou cout.put(C)
else cout << 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

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 3 : Les structures de contrôle 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

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 3 : Les structures de contrôle 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:
Programme La syntaxe de l’instruction est :
do {
instruction1;
Bloc d’instructions instruction2;
…..
} while (<expression logique >);

oui
Condition
vraie
Suite du programme
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);

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 3 : Les structures de contrôle 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.

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 3 : Les structures de contrôle 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

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 3 : Les structures de contrôle 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; char i;
cin >> i ; cin >> i ;
switch (i) { switch (i) {
case valeur1 : case 'valeur1' :
ensemble_instruction1; ensemble_instruction1;
break; break;
4) Les trois structures de contrôle d'itérations :
for (initialis. ; ( ?test_vrai); itération) while ( ?test_vrai) { do {
{ instruction 1; instruction 1;
instruction 1; instruction 2; instruction 2;
instruction 2; itération ; itération ;
} } }while( ?test_vrai);

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 3 : Les structures de contrôle 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

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 3 : Les structures de contrôle 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.

M.BENJELLOUN Struct. D.A. S. Informatique


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 (constante).

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

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 4 : Les tableaux et les chaînes de caractères 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() Programme 4.1
{
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 ; Programme 4.2
}
}

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.

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 4 : Les tableaux et les chaînes de caractères 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";

M.BENJELLOUN Struct. D.A. S. Informatique


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

Par exemple le programme suivant affiche Hello:

#include <iostream>
using namespace std;
Programme 4.3
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.

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 4 : Les tableaux et les chaînes de caractères 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

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 4 : Les tableaux et les chaînes de caractères 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;
Programme 4.4
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;
}

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 4 : Les tableaux et les chaînes de caractères 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;
Programme 4.5
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);
}

M.BENJELLOUN Struct. D.A. S. Informatique


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

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"; char TabA[4]= "ABC";


string TabB ="def " ; char TabB[4] ="def" ;
TabA=TabB; // Correcte TabA=TabB; // Non correcte

Pour cela, il faut inclure <string>

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.

M.BENJELLOUN Struct. D.A. S. Informatique


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

4.7. Codez un programme de recherche dichotomique d'un nombre dans un vecteur de strings rangés par ordre
croissant.
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 8 2 7 9 1 9
 2 7 1

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

M.BENJELLOUN Struct. D.A. S. Informatique


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 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 ";
}
}

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 5 : Les fonctions 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 int 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.

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 5 : Les fonctions 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.

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 5 : Les fonctions 52

// Version1 // Version2
#include <iostream> #include < iostream>
using namespace std; using namespace std;

void Affiche (int x, int y); int Somme(const int x, const int y){
int Somme (const int x, const int y); return (x+y);
void moyenne(const int x, const int y) ; }

void main(void) { void Affiche (int x, int y){


int i; cout << "\n 2 * " << x << "=" << y ;
for (i=0; i<=10; i++) { }
Affiche (i, Somme(i,i)) ;
moyenne( i, i*i) ; void moyenne(const int x, const int y) {
} float Moy ;
} Moy = (x+y)/2.0 ;
cout << "la moyenne est : " << Moy <<endl ;
void Affiche (int x, int y){ }
cout << "\n 2 * " << x << "=" << y ;
} void main(void) {
int Somme(const int x, const int y) { int i;
return (x+y); for (i=0; i<=10; i++) {
} Affiche (i, Somme(i,i)) ;
moyenne( i, i*i) ;
void moyenne(const int x, const int y) { }
float Moy ; }
Moy = (x+y)/2.0 ;
cout << "la moyenne est : " << Moy <<endl ;
}

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.

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 5 : Les fonctions 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; Programme 5.2
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*/
}

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 5 : Les fonctions 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

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 5 : Les fonctions 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 void TriaBulles(int x[], int N)
{ {
int i, perm=1, tmp; // variables locales à TriaBulles int i, tmp;
bool perm=true;
while (perm) {
while (perm==1) {
perm =false;
perm =0;
for (i=0;i<N-1;i++) {
for (i=0;i<N-1;i++) { if(x[i]>x[i+1]) {
if(x[i] > x[i+1]) {
OU
tmp = x[i];
tmp = x[i]; x[i] = x[i+1];
x[i] = x[i+1]; x[i+1] = tmp;
x[i+1] = tmp; perm = true;
perm = 1; }
} }
}
}
}
}
}

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 5 : Les fonctions 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;
}

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 5 : Les fonctions 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.

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 5 : Les fonctions 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); Programme 5.4
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.

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 5 : Les fonctions 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();
} Programme 5.5
}

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.

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 5 : Les fonctions 60

#include <iostream>
using namespace std;
int Somme(int x, int y){ Programme 5.6
cout << "Dans la fonction int " <<(x+y) << endl;
return (x+y);
}
L’exécution nous donne :
float Somme(float x, float y){
cout << "Dans la fonction float "<< (x+y) << endl;
Dans la fonction int 5
return (x+y);
Retour de la fonction int 5
}
Dans la fonction float 4.5
void Somme(const int x, const int y, const int z){ Retour de la fonction float 4.5
cout << "Dans la fonction void "<< (x+y+z) << endl; Dans la fonction void 6
}
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;
Le résultat du programme
void affiche(int un = 1, int deux = 2, int trois = 3) {
cout << un << ' ' << deux << ' ' << trois << '\n'; 5.7 est le suivant :
}
1 2 3
void main () { 100 200 3
affiche(1, 2, 3); 1000 2 3
affiche(100, 200); 1 2 3
affiche(1000); Programme 5.7
affiche();
}

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 5 : Les fonctions 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; Programme 5.8
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.

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 5 : Les fonctions 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).

M.BENJELLOUN Struct. D.A. S. Informatique


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)

p i Noms des variables

5A0F3 35 Valeurs des variables

60C19 5A0F3 Adresses mémoires

Mémoire

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é.
Chap. 6: Les pointeurs et les références 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.

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 6: Les pointeurs et les références 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.

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 6: Les pointeurs et les références 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.

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 6: Les pointeurs et les références 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 echange (int*, int*); void echange (int&, int&);
void main () { void main () { void main () {
int i= 1, j=2; int i= 1, j=2; int i= 1, j=2;
affiche (i, j); affiche (i, j); affiche (i, j);
echange (i, j); echange (&i, &j); echange (i, j);
affiche (i, j); affiche (i, j); affiche (i, j);
} } }

void echange (int a, int b) void echange (int *a, int *b) void echange (int &a, int &b)
{ { {
int tmp; int tmp; int tmp;
tmp = b; tmp = *b; tmp = b;
b = a; *b = *a; b = a;
a = tmp; 2 *a = tmp; 3 a = tmp; 4
} } }
Résultats :
Valeurs i = 1 j=2 Valeurs i = 1 j=2 Valeurs i = 1 j=2
Valeurs i = 1 j=2 Valeurs i = 2 j=1 Valeurs i = 2 j=1

Programme 6.1

Partie commune aux trois programmes


1
2 Passage des paramètres par valeur

3 Passage des paramètres par référence de pointeur

4 Passage des paramètres par référence

2
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

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 6: Les pointeurs et les références 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.

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 6: Les pointeurs et les références 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
}
Programme 6.3
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.

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 6: Les pointeurs et les références 70

Cela permet de considérer les expressions suivantes comme équivalentes :


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

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 6: Les pointeurs et les références 71

#include <iostream>
using namespace std; Programme 6.4
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;
Programme 6.5
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";
}

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 6: Les pointeurs et les références 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;

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 6: Les pointeurs et les références 73

Le programme suivant nous donne un aperçu sur la manière d’allouer et de libérer la mémoire:

#include <iostream> Ce programme produit le résultat suivant :


using namespace std;
T[0] = -842150451 C[0] = ◙
void affiche(int T[], int d, char C[]){ T[1] = -842150451 C[1] = ◙
for(int i=0; i<d; i++) T[2] = -842150451 C[2] = ◙
cout<< "\n T["<<i<<"] = " << T[i] << " C["<<i<<"] = " <<C[i]; T[3] = -842150451 C[3] = ◙
cout << endl; T[4] = -842150451 C[4] = ◙
T[5] = -842150451 C[5] = ◙
}

void main(){ T[0] = 0 C[0] = A


T[1] = 1 C[1] = B
int N=6, *T, i; T[2] = 2 C[2] = C
char *C = new char[N]; T[3] = 3 C[3] = D
T[4] = 4 C[4] = E
T= new int[N]; Programme 6.6 T[5] = 5 C[5] = F
affiche(T, N, C);
T[0] = -572662307 C[0] = A
T[1] = -572662307 C[1] = B
for(i=0; i<N; i++){
T[2] = -572662307 C[2] = C
T[i]=i; T[3] = -572662307 C[3] = D
C[i]='A'+i; T[4] = -572662307 C[4] = E
} T[5] = -572662307 C[5] = F

affiche(T, N, C); T[0] = -572662307 C[0] = ¦


T[1] = -572662307 C[1] = ¦
delete[] T; T[2] = -572662307 C[2] = ¦
affiche(T, N, C); T[3] = -572662307 C[3] = ¦
T[4] = -572662307 C[4] = ¦
delete[] C; T[5] = -572662307 C[5] = ¦
affiche(T, N, C);
}

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 i = 35;
int &p; //Erreur int &p= i; // OK
p = &i; //Erreur ….

int i = 35; int i = 35;
int *p; int *p;
*p = i; //Erreur 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.

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 6: Les pointeurs et les références 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; Exemple : Les sanglots longs des violons de l'automne …
while(*p) Deviendra : Qjx xfslqtyx qtslx ijx antqtsx ij q'fzytrsj ...
Crypt(p++);
cout << "\nResultat :";
Affiche(Tab, ??);
}

M.BENJELLOUN Struct. D.A. S. Informatique


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.

pointeur

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.
Chap. 7: Les fichiers 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.

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 7: Les fichiers 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; ofstream output; fstream output;
output.open("Res.txt"); output.open("Res.txt", ios::app); output.open("Res.txt",
ios::out);

Lecture :
ifstream input ; fstream input;
input.open("Data.txt"); 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; ofstream output;


output.open("Res.txt"); string nom_fichier ;
if( !output) { do{
cout<<"Erreur d’ouverture du fichier"<<endl; cout << "\nNom du fichier : " ;
} cin >> nom_fichier ;
else{
cout<<"Le fichier est bien ouvert"<<endl; 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

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 7: Les fichiers 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.

A
10
Les données dans le fichier "fichier.txt" sont : 3.14
7.12345

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 7: Les fichiers 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 :

A 10 3.14 7.12345

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;

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 7: Les fichiers 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 :

Les donnees sont : On suppose que tout ce que l’on a comme information sur ce
12345 fichier, est:
6789  un titre sur une ligne,
10 11 12  des données de type entier dont le nombre est inconnu.
13 14
15 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.

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 7: Les fichiers 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);
Programme 7.4.

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

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 7: Les fichiers 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

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 7: Les fichiers 83

espaces, le programme ne fonctionne plus convenablement. La correction est demandée au lecteur


sous forme d’un exercice.

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() ;

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.

M.BENJELLOUN Struct. D.A. S. Informatique


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 { struct Client {
int x; string nom;
int y; char Code;
}; };

Ces structures peuvent être représentées logiquement par les schémas suivants:

point Client

x nom

y
Code

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;
};
Chap. 8: Les structures 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:

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 8: Les structures 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

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 8: Les structures 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.

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 8: Les structures 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;
};

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 8: Les structures 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.

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 8: Les structures 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;
}

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 8: Les structures 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.

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 8: Les structures 92

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.

Exercices

8.0. Ecrire une fonction qui traduit le graphique (température en fonction de x) suivant :

température en fct x

t
25
20
15
10
5
0
0 1 2 3 4 5
x
6

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 :

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 8: Les structures 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, M 10 15 … 8 A 13.3


bte 10, …
nom2 33, rue qui n’existe F 10 20 … 19 V 15.7
pas, …
nom3 … M 13 14 … 15 C 13.9
nom20 … F 10 10 … 11 D 10.2

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 8: Les structures 94

8.5.
Enoncé 3 - SUPPRESSION d'un élément après localisation
Ecrire un programme permettant de manipuler un tableau
d’enregistrements ‘’structures’’ (taille max NE_MAX = 20). Nom de l’élément à supprimer : van cool
Ce programme doit gérer en boucle le menu suivant : Code de l’élément à supprimer : A

1 - SAISIE et AFFICHAGE du tableau La liste devient (Fonction AFFICHAGE ) :


2 - TRI par ordre alphabétique selon le nom et le code
3 - SUPPRESSION d'un élément après localisation Nom Code Cote1 Cote2
4 - Sauvegarde
5 - ARRET du programme aladdin Y 40 42
mon_nom M 2 1
La structure de chaque élément est : son_nom T 29 2
struct Element van cool P 45 54
{ string nom
char code
int cotes[NC_max] ; // NC_max=5 Avant de supprimer l’élément avec la fonction
} Suppression, il faut d’abord le localiser avec la
fonction Localisation (dichotomique)
1 - SAISIE et AFFICHAGE
RQs:
est constitué de deux fonctions : Si l’élément à supprimer n’existe pas, n’oubliez pas
Saisie (…...) ; // de type void de le signaler à l’utilisateur.
Dans cette fonction, on demandera le nombre
d’éléments (NE) pour initialiser le tableau (par Le programme doit utiliser une série de fonctions
exemple 5) et le nombre de cotes (NC<=5) pour tous qui vont permettre de séparer les tâches.
les "ELEMENT", et on effectuera la saisie du nom, du
code et des cotes pour chaque élément. 4 - Sauvegarde
Affichage(…….) ;
Après la saisie, une fonction d’affichage est appelée On demandera à l’utilisateur d’introduire un seuil
afin d’afficher l’acquisition de la fonction saisie. (int).
Ce dernier doit être >0 et < 100 (test).
Exp : si NE=5 et NC=2 Pour chaque "ELEMENT", si la somme de ses cotes
est supérieure au seuil, on l’enregistrera dans un
Nom Code Cote1 Cote2 fichier ‘’Fich.txt’’.

van cool P 45 54 Exp : Seuil = 80


mon_nom M 2 1
son_nom T 29 2 Dans le fichier 
van cool A 40 41
aladdin Y 40 42
aladdin Y 40 42
van cool P 45 54
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

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 8: Les structures 95

8.6.
Enoncé
A- Actualisation et affichage :
Ecrire un programme permettant de manipuler un tableau
Pour chaque ligne, on compte le nombre de e, de i et
d’enregistrements "structure" (NP_max = 20) dont
on détermine Som= N_e+N_i . Puis on actualise la
chaque élément comprend les informations suivantes
structure (N_e, N_i et Som ). Enfin, on affiche
Ligne{ l’actualisation du tableau.
string Phrase ;
Compt ; // une structure EXP :
}
avec Compt est une structure de type : TEXTE N_e N_i Som
Compt{ qui compte le nombre 3 1 4
int N_e ; de e et de i ainsi 4 3 7
int N_i ; ceci est un test 3 1 4
int Som; la longueur de 2 0 2
} la ligne 1 1 2

Réaliser un programme qui gère en boucle le menu de choix Dans la fonction Actualisation on cherche le max de
suivant : Som (Max_Som) et sa position (pos) dans le
S- Saisie et affichage tableau.
T- Tri et affichage
A- Actualisation et affichage
V- Valeurs V_ Valeurs :
Q- Quitter
Affiche les nombres : NL, Max_Som et pos.
S- Saisie et affichage :

La fonction saisie (de type void) demande à


l’utilisateur le nombre de "Ligne" (NL) pour initialiser Remarques:
le tableau (par exemple 5) et lui permet d’introduire un
texte par ligne. Il est souhaitable d’utiliser la même fonction
Affichage en S, T et A. (une et une seule fonction)
EXP : saisie(…) puis affichage(….)
Ligne_1 : ceci est un test NL, Max_Som et pos sont des variables locales de la
!
Ligne_2 : qui compte le nombre fonction main.
Ligne_3 : de e et de i ainsi
Ligne_4 : la longueur de NP_max et dim_max sont des constantes.
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

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 8: Les structures 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)

Rouge (E) Vert bouteille (C) ... …


R V
2- Ajout Gauche et Affichage : B

Bleu (T) 
B R V
Bleu (T) Rouge (E) Vert bouteille (C) ...
B
3- Ajout Droite et Affichage :

Blanc (S)  Bleu (T) Rouge (E) Vert bouteille (C) Blanc (S)

B R Bc
V
2- Ajout Gauche et Affichage : B
Jaune (T) Bleu (T) Rouge (E) Vert bouteille (C) J B R V
Jaune (T) 
B
2- Ajout Gauche et Affichage :

Noire (N) Jaune (T) Bleu (T) Rouge (E) N J B R


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  Noire (N) Bleu (T) Rouge (E) ...
N B R
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.

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 8: Les structures 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 3- Seuil de lipolyse et Affichage:


de personnes (taille max Nmax = 6 ) dont chaque élément Ici on calcule le seuil de lipolyse pour chaque personne et on
comprend les informations suivantes : affiche les résultats.
{ string Nom ; Nom Age FreqR Seuil Lipo
int Age; Riri 20 70 135
int FreqR ; // fréquence cardiaque au repos Fifi 40 60 120
} Lou Lou 30 75 132
IL EST INTERDIT DE MODIFIER LA STRUCTURE ;
4- Effacer et Affichage:
Ce programme doit gérer en boucle le menu suivant : Est constitué de deux fonctions : Effacer (….) et Affichage
1- Saisie et Affichage Effacer (….) ;
2- AAffiffichachagege Dans cette fonction on demandera à l’utilisateur d’introduire
3- Seuil de lipolyse et Affichage une valeur V (de type int). Cette dernière doit être >10 et <
4- Effacer et Affichage 50 (tester). On supprimera ensuite du tableau chaque personne
5- Tab Dynamique et affichage dont l’âge est strictement inférieur à V.

EXP : Si V = 35  le tableau deviendra


1- Saisie et Affichage:
Est constitué de deux fonctions : Nom Age FreqR Seuil Lipo
Saisie (…….) ; // Type void Fifi 40 60 120
Dans cette fonction on demandera le nombre
d’éléments NE (<Nmax :tester) à saisir et on 5- Tab Dynamique et affichage:
effectuera la saisie des éléments. On désire ici avoir les résultats pour tracer la courbe de l’âge en
fonction du rythme cardiaque.
Affichage(…….) ; Permet l’affichage des données :
EXP : Si NE=3 Donc, pour une FreqR F, donnez le Seuil de Lipolyse pour une
Nom Age FreqR tranche d’âge entre 15 et 45 avec un pas P.
Riri 20 70 F et P seront demandés à l’utilisateur.
Fifi 40 60
Exp1 : Si F= 70 et P= 5 on aura :
Lou Lou 30 75
Age 15, 20, 25, 30, 35, 40, 45
2- AAffiffichachagege:
Seuil 137 135 132 …
Permet d’afficher deux fois chaque ligne:
Nom Age FreqR Exp2 : Si F= 70 et P= 10 on aura :
Riri 20 70
Riri 20 70 Age 15, 25, 35, 45
Fifi 40 60 Seuil 137 132 …
Fifi 40 60
Selon la valeur de P, la dimension du tableau (âge, seuil) change.
Lou Lou 30 75
C’est pour cette raison qu’il faut utiliser un tableau dynamique
Lou Lou 30 75
pour stocker et afficher les résultats (âge, seuil).
RQ : Il est fort souhaitable d’utiliser la même
et seule fonction Affichage en 1, 2, 3 et 4. RQs : Les variables globales sont à éviter.

!
M.BENJELLOUN Struct. D.A. S. Informatique
Chapitre 9: Les listes simplement chaînées

9.1. Généralités

Jusqu'à présent, nous n'avons travaillé qu'avec des structures de données statiques, dont l'espace
mémoire occupé pouvait être défini avant l'exécution. Dans le cas d’une déclaration d’un tableau de
structure par la syntaxe:
struct Etudiant Tab[100] ;

Cette déclaration permet de réserver automatiquement une zone mémoire d’une longueur de 100
éléments de type Etudiant. Les cellules du tableau sont alors accessibles par Tab[0] à Tab[99].
Cependant, le problème réside dans le fait que la déclaration du tableau avec une dimension
constante fige le nombre d’éléments de ce tableau. Autrement dit, même si un programme n’a besoin
que de 20 éléments, on réservera malgré tout 100 cellules de type Etudiant. Comme nous l’avons déjà
vu en 1ère Bachelier, nous pouvons remédier à cela en utilisant les pointeurs. Les instructions
deviennent dans ce cas-ci :

struct Etudiant *Tab;

Pour pouvoir stocker en mémoire une valeur de type Etudiant, il faut donc explicitement demander
une adresse disponible en utilisant le mot réservé new et la libérer avec delete. L’allocation
dynamique de la mémoire correspondant au nombre de cellules est maintenant au gré de l’utilisateur.
On peut, par exemple, lui demander d’introduire un nombre N, puis nous réservons l’espace mémoire
strictement nécessaire.

cout<< " Dimension du Tableau N : " ;


cin >> N;
// Allocation mémoire : new[]
Tab = new Etudiant[N];

// Restituer la mémoire : delete[]
delete[] Tab;

Supposons qu’à un moment donné dans le programme même, nous n’ayons plus besoin que de (N-5)
éléments. La logique, comme nous désirons optimiser la gestion de la mémoire, est de libérer la place
de mémoire pour 5 cellules. Mais cela ne peut se faire ni automatiquement ni directement. Donc cela
reste bien sûr limitatif. En effet, cela ne permet pas d'exploiter les structures tels que les listes
chaînées, les piles, les arbres, structures fondamentales pour de nombreux algorithmes.
Structures de données et algorithmes

9.2. Listes Simplement chaînées

Une liste chaînée est une séquence d'objets dynamiques dont chacun contient une indication
explicite vers l'élément suivant. Une liste est faite de cellules. Chaque cellule contient un élément de
la liste et l'adresse de l'élément suivant (structure de même type).
Considérons donc la définition d'une liste simplement chaînée de structures contenant des strings.

"Etud1" "Etud2" "Etud3"


NULL
Chap. 9: Les listes simplement chaînées 99

Chaque cellule de la liste chaînée peut être définie comme une structure:

struct Etudiant {
string Nom;
struct Etudiant *next; // Etudiant suivant
};

Chaque cellule comporte donc une zone mémoire pour stocker son contenu (ici string Nom), ainsi
qu'une zone mémoire pour stocker un pointeur vers la cellule suivante (c'est-à-dire l'adresse de la
cellule suivante).

Pour pouvoir travailler avec une liste simplement chaînée, il faut connaître l'adresse de sa première
cellule. Nous définirons donc un pointeur:

struct Etudiant *debut; // ou simplement Etudiant *debut;

Si les tableaux permettent un accès direct aux éléments, les listes chaînées en sont dépourvues. La
définition et la nature même de la liste chaînée font apparaître que l’on ne peut parcourir
intégralement la liste qu’en partant du premier élément et en visitant les éléments l’un après l’autre.
En contrepartie nous gagnons en souplesse lorsqu’il s’agit de modifier la liste. Des opérations telles
que : ajout, suppression d’un élément à une position quelconque dans la liste, la suppression avec
libération de mémoire d’un nombre quelconque d’éléments consécutifs, …

Voici un programme simple mais complet, dans lequel nous traitons un certain nombre d’opérations
sur la liste. Par exemple : l'insertion d'une nouvelle cellule en début de liste, le parcours de la liste
pour l’affichage, l’effacement d'une cellule, la libération de la mémoire.

#include <iostream>
#include <string>
using namespace std ;

struct Etudiant{
string Nom;
struct Etudiant *next; // le lien : pointeur sur la cellule suivante
};

//insérer un nouvel élément en tête de Etudiant et retourner un pointeur


Etudiant *insere_en_tete(string x , Etudiant *lis ) {
Etudiant *c;
c = new Etudiant ; // créer une cellule
if (c !=NULL){
c->Nom=x; // assignation de x
c->next=lis; // assignation de suivant: chaîner avec la première cellule de la Etudiant
lis=c; // chaînage: faire pointer la tête de Etudiant vers la nouvelle cellule.
}
return (lis);
}

void parcours (Etudiant *lis) {


Etudiant *p;
p = lis;

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 9: Les listes simplement chaînées 100

cout << "Affichage : "<< endl;


while ( p != NULL) {
cout << p->Nom << endl;
p = p->next;
}
cout <<endl<<endl;
}
void liberation (Etudiant *lis) {
Etudiant *p;
p = lis;
while ( p != NULL) { // si p=NULL, donc fin de Etudiant
lis=lis->next;
delete p;
p = lis;
}
}
void main() {
int i, n;
string Name;
Etudiant *debut;
debut=NULL; // Etudiant vide
cout << "nombre d'elements N: " ;
cin >> n;
cout << "\n Creer un Etudiant avec insertion en tete de Etudiant + affichage \n";
for(i=1; i<=n; i++){
cout << "\n Nom "<< i << " : ";
cin >> Name;
debut=insere_en_tete(Name , debut);
}
parcours(debut); // Affichage
//-----------------------
cout << "Inserer une nouvelle cellule apres la cellule debut " << endl;
Etudiant *p;
p = new Etudiant;
p->Nom = "Nouveau";
p->next = debut->next;
debut->next = p;
parcours (debut);
//-----------------------
cout << "Supprimer la 1ere cellule " << endl;
Etudiant *q;
q=debut;
debut=debut->next;
delete q; // mémoire dynamique n'est plus utilisée -> doit être libérée.
parcours (debut);
//------------------------
// Libération
cout << " Liberation de la memoire ";
liberation (debut);
}

M.BENJELLOUN Struct. D.A. S. Informatique


Chap. 9: Les listes simplement chaînées 101

nombre d'elements N: 3

Creer une liste avec insertion en tete de liste + affichage

Nom 1 : premier
Nom 2 : second
Nom 3 : nom3

Affichage :
nom3
second
premier

S
O
L Inserer une nouvelle cellule apres la cellule debut
U Affichage :
nom3
T Nouveau
second
I premier

O
N
Supprimer la 1ere cellule
Affichage :
Nouveau
second
premier

Liberation de la memoire

M.BENJELLOUN Struct. D.A. S. Informatique


Chapitre 10: 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 ; int A, B ;
A=10 ; Correction  A=10 ;
A=B ; 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(){ void main(){


int *adr; Correction int *adr;
*adr = 123;  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 ; int A, B ;
float F; Correction  float F;
A=10 ; A=10 ;
B=25 ; B=25 ;
F= B/A ; F= (float)B/A ;
Chap. 10: Quelques erreurs à éviter 103

Taille d’une chaîne :

char nom[30] ; int taille ;


int taille ; Correction  …
cout <<"nom ="; Selon la déclaration de taille = strlen(nom);
cin >> nom ; type de nom. ou
taille = sizeof(nom); 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 ; int i ;
Correction  i=5; //Initialiser la variable avant
cout <<"valeur de i = " << i; 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) { if (x == 0) {
... Correction  ...
} }

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++){ for (i = 0 ; i<N; i++){


… Correction  …

‘;’ et non ‘,’

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


{ … Correction  { …

int Tab[N]; int Tab[N];


for (i = 0 ; i == N; i++){ Correction  for (i = 0 ; i<N; i++){
Tab[i]=i; Tab[i]=i;
} }

M.BENJELLOUN Struct. D .A S. Informatique


Chap. 10: Quelques erreurs à éviter 104

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]; int Tab[N];


for (i = 0 ; i<N; i++) Correction  for (i = 0 ; i<N; i++) {
Tab[i]=i; Tab[i]=i;
cout << "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 >); while (<expression logique >)


{ Correction  {
... ...
} }

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) void print_chiffre(int i) {


{ switch (i) {
switch (i) { case 1: cout <<"un"; break;
case 1: cout <<"un"; Correction case 2: cout <<"deux"; break;
case 2: cout <<"deux";  case 3: cout <<"trois"; break;
case 3: cout <<"trois"; 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) int Cube(int i) void Cube(int &i)


{ Correction  { {
i*i*i; return(i*i*i); i = i * i * i;
} } }

int Deux_val(int i, int j){ int Deux_val(int i, int j) void Deux_val(int &i, int &j) {
… {… Correction i =…
… return i;  j =…
return (i,j); } return j; } }

void Modif(int *i, int *j) { void Modif(int *i, int *j) {
i = 5; Correction  *i = 5;
j = 6; *j = 6;
} }

M.BENJELLOUN Struct. D .A S. Informatique


Chap. 10: Quelques erreurs à éviter 105

void Modif(int *i, void Modif(int i, int j) void Modif(int *i, int *j) void Modif(int &i, int &j)
int *j) { i = 5; Correction { *i = 5; { i = 5;
{ *i = 5; j = 6;  *j = 6; j = 6;
*j = 6; } } }
} void main(){ void main(){ void main(){
void main(){ int k,j ; int k,j ; int k,j ;
int k,j ; Modif(k, j); Modif(&k, &j); Modif(k, j);
Modif(k, j); } } }
}

int Modif(int i) int Modif(int i) int Modif(int i) void Modif(int &i)


{ { { {
i = 5; i = 5; i = 5; i = 5;
return i; Correction return i; return i; }
}  } }
void main(){ void main(){ void main(){ void main(){
int j ; int j ; int j ; int j ;
Modif(j); j=Modif(j); cout<< Modif(j); Modif(j);
cout<< j; cout<< j; } cout<< j;
} } }

int Fonc(int Tab[], int N){ void Fonc(int Tab[], int N){
int i; int i;
for (i = 0 ; i<N; i++) Correction  for (i = 0 ; i<N; i++)
Tab[i]=i; Tab[i]=i;
/*une fonction ne peut retourner
return Tab; un tableau avec return */
} }

void Saisie1(int Tab1[], int N1){ void Saisie(int Tab[], int N){
int i; int i;
for (i = 0 ; i<N1; i++) for (i = 0 ; i<N; i++)
Tab1[i]=i; Tab[i]=i;
} }
void Saisie2(int Tab2[], int N2){
int i; Correction 
for (i = 0 ; i<N2; i++) void main(){
Tab2[i]=i; int Tab1[40], Tab2[40];
} int N1=20, N2=30;
void main(){ saisie(Tab1, N1) ;
int Tab1[40] , Tab2[40]; saisie(Tab2, N2) ;
int N1=20, N2=30; }
saisie1(Tab1, N1) ;
saisie2(Tab2, N2) ;
}

struct Article{ struct Article{


string nom; string nom;
}; };
void main() { void main() {
Article T[5]; Article T[5];
for (int i=0; i<5; i++){ for (int i=0; i<5; i++){
cout << "Entrez le nom : "; Correction  cout << "Entrez le nom : ";
cin >> T.nom; cin >> T[i].nom;
} }
} }

M.BENJELLOUN Struct. D .A S. Informatique


FACULTÉ POLYTECHNIQUE DE MONS

Service d’Informatique

Annexes
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é


() [] -> .
de gauche à droite les plus prioritaires
++ (postfixé) -- (idem)
Opérateurs unaires : ! ~
de droite à gauche
++ -- + - * & (<type>) sizeof
*/% 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 108

Table des codes ASCII des caractères

Caractère déc. hexa. octal Caractère déc. hexa. octal Caract déc. hexa. octal Caract déc. hexa. octal
C-@ (NUL) 0 0x00 000 espace 32 0x20 040 @ 64 0x40 100 ` 96 0x60 140
C-a (SOH) 1 0x01 001 ! 33 0x21 041 A 65 0x41 101 a 97 0x61 141
C-b (STX) 2 0x02 002 " 34 0x22 042 B 66 0x42 102 b 98 0x62 142
C-c (ETX) 3 0x03 003 # 35 0x23 043 C 67 0x43 103 c 99 0x63 143
C-d (EOT) 4 0x04 004 $ 36 0x24 044 D 68 0x44 104 d 100 0x64 144
C-e (ENQ) 5 0x05 005 % 37 0x25 045 E 69 0x45 105 e 101 0x65 145
C-f (ACK) 6 0x06 006 & 38 0x26 046 F 70 0x46 106 f 102 0x66 146
C-g (BEL) 7 0x07 007 ' 39 0x27 047 G 71 0x47 107 g 103 0x67 147
C-h (BS) 8 0x08 010 ( 40 0x28 050 H 72 0x48 110 h 104 0x68 150
C-i (HT) 9 0x09 011 ) 41 0x29 051 I 73 0x49 111 i 105 0x69 151
C-j (LF) 10 0x0a 012 * 42 0x2a 052 J 74 0x4a 112 j 106 0x6a 152
C-k (VT) 11 0x0b 013 + 43 0x2b 053 K 75 0x4b 113 k 107 0x6b 153
C-l (FF) 12 0x0c 014 , 44 0x2c 054 L 76 0x4c 114 l 108 0x6c 154
C-m (CR) 13 0x0d 015 - 45 0x2d 055 M 77 0x4d 115 m 109 0x6d 155
C-n (SO) 14 0x0e 016 . 46 0x2e 056 N 78 0x4e 116 n 110 0x6e 156
C-o (SI) 15 0x0f 017 / 47 0x2f 057 O 79 0x4f 117 o 111 0x6f 157
C-p (DLE) 16 0x10 020 0 48 0x30 060 P 80 0x50 120 p 112 0x70 160
C-q (DC1) 17 0x11 021 1 49 0x31 061 Q 81 0x51 121 q 113 0x71 161
C-r (DC2) 18 0x12 022 2 50 0x32 062 R 82 0x52 122 r 114 0x72 162
C-s (DC3) 19 0x13 023 3 51 0x33 063 S 83 0x53 123 s 115 0x73 163
C-t (DC4) 20 0x14 024 4 52 0x34 064 T 84 0x54 124 t 116 0x74 164
C-u (NAK) 21 0x15 025 5 53 0x35 065 U 85 0x55 125 u 117 0x75 165
C-v (SYN) 22 0x16 026 6 54 0x36 066 V 86 0x56 126 v 118 0x76 166
C-w (ETB) 23 0x17 027 7 55 0x37 067 W 87 0x57 127 w 119 0x77 167
C-x (CAN) 24 0x18 030 8 56 0x38 070 X 88 0x58 130 x 120 0x78 170
C-y (EM) 25 0x19 031 9 57 0x39 071 Y 89 0x59 131 y 121 0x79 171
C-z (SUB) 26 0x1a 032 : 58 0x3a 072 Z 90 0x5a 132 z 122 0x7a 172
C-[ (ESC) 27 0x1b 033 ; 59 0x3b 073 [ 91 0x5b 133 { 123 0x7b 173
C-\ (FS) 28 0x1c 034 < 60 0x3c 074 \ 92 0x5c 134 | 124 0x7c 174
C-] (GS) 29 0x1d 035 = 61 0x3d 075 ] 93 0x5d 135 } 125 0x7d 175
C-$ (RS) 30 0x1e 036 > 62 0x3e 076 ^ 94 0x5e 136 ~ 126 0x7e 176
C-_ (US) 31 0x1f 037 ? 63 0x3f 077 _ 95 0x5f 137 C-? 127 0x7f 177

M.BENJELLOUN Info. I S. Informatique