0% ont trouvé ce document utile (0 vote)
53 vues54 pages

Structures de données en C++

Le cours INF3105 à l'Université Adam Barka d'Abéché vise à approfondir les structures de données et les algorithmes en C++. Il couvre les principes de base du langage C++, y compris la structure des programmes, les types de données, les variables, les constantes, et les entrées/sorties. Des exemples pratiques illustrent l'utilisation des fonctionnalités du langage pour une meilleure compréhension.

Transféré par

Giraud Todjirom
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
53 vues54 pages

Structures de données en C++

Le cours INF3105 à l'Université Adam Barka d'Abéché vise à approfondir les structures de données et les algorithmes en C++. Il couvre les principes de base du langage C++, y compris la structure des programmes, les types de données, les variables, les constantes, et les entrées/sorties. Des exemples pratiques illustrent l'utilisation des fonctionnalités du langage pour une meilleure compréhension.

Transféré par

Giraud Todjirom
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Université Adam Barka d'Abéché d'Abéché

Faculté des Sciences et Techniques


Département de Mathématiques
Licence 3

Structure de données

Giraud, Nassinda T.

Année académique : 2024- 2025


Objectif général du cours:
L’objectif du cours INF3105 est d’approfondir le sujet des structures de données et des
algorithmes fondamentaux en informatique. Les structures de données permettent d’organiser
l’information dans la mémoire d’une machine. Des algorithmes exploitent les structures de
données afin de résoudre efficacement des problèmes précis.

2
Chapitre I : Principes de base du langage C++

I.1 Introduction
Le langage C++ peut être considéré comme un perfectionnement du langage C qui offre les
possibilités de la POO.
Les notions de base de la programmation en C restent valables en C++, néanmoins C et C++
diffèrent sur quelques conventions (déclaration des variables et des fonctions, nouveaux mots
clés…)
Ce chapitre retrace ses différences, et traite les autres outils de la programmation structurée
ajouté à C++

I.2 Structure générale d'un programme


I.2.1 Fonction main
Tout programme doit avoir un point d’entrée nommé main
int main()
{
return 0;
}

La fonction main est la fonction appelée par le système d’exploitation lors de l'exécution du
programme
▪ { et } délimitent le corps de la fonction
▪ main retourne un entier au système: 0 (zéro) veut dire succès
▪ Chaque expression doit finir par; (point virgule)

I.2.2 Commentaires
En C et C++: Commentaires sur plusieurs lignes : délimités par /* (début) et */ (fin).

/* Un commentaire en une seule ligne */


/*
* Un commentaire sur plusieurs
* lignes */

En C++ uniquement : Commentaires sur une seule ligne : délimités par // (début) et fin de ligne
(n’existe pas en C)

// Un commentaire jusqu’à la fin de cette ligne

I.2.3 Fichiers Sources


Un programme est généralement constitué de plusieurs modules, chaque module est composé
de deux fichiers sources:

▪ Un fichier contenant la description de l’interface du module


▪ Un fichier contenant l’implémentation proprement dite du module

3
Un suffixe est utilisé pour déterminer le type de fichier

▪ .h, .H, .hpp, .hxx : pour les fichiers de description d’interface (header files ou include
files)
▪ .c, .cc, .cxx, .cpp, .c++ : pour les fichiers d’implémentation

Dans un fichier source on peut trouver:


▪ Commentaires
▪ Instructions pré-processeur
▪ Instructions C++

I.2.4 Construction de l’Exécutable

I.2.5 Bibliothèque de C++


C++ possède une bibliothèque très riche, qui comporte un très grand nombre d'outils
(fonctions, types, …) qui permettent de faciliter la programmation. Elle intègre en plus de la
bibliothèque standard de C, des librairies de gestion des entrées-sorties ainsi que des outils de
manipulation des chaînes de caractères, des tableaux et d'autres structures de données. Pour
utiliser, en C++, les outils qui existaient dans la bibliothèque standard de C (stdio.h, string.h,
…) ainsi que certains nouveaux outils, il suffit de spécifier avec la directive include le fichier
entête (.h) souhaité.

#include <iostream> // En C++ : « Input Output Stream » ou bien « Flux d'entrée-sortie »


#include <cstdio> // En C : « Flux d'entrée-sortie »

4
Exemple:

#include <iostream>
#include <cstdio> // Les librairie C

int main()
{
std ::cout << "Hello !"<<std ::endl;
printf(" Hello bis !\n ") ; // Lesinstructions C return 0;
}

I.2.6 Espace de noms en C++


Pour des raisons liées à la POO (généricité, modularité…) et pour éviter certains conflits qui
peuvent surgir entre les différents noms des outils utilisés (prédéfinis ou définis par l'utilisateur),
C++ introduit la notion de namespace (espace de noms), ce qui permet de définir des zones de
déclaration et de définitions des différents outils (variables, fonctions, types,…). Ainsi, chaque
élément défini dans un programme ou dans une bibliothèque appartient désormais à un
namespace. La plupart des outils d’Entrées / Sorties de la bibliothèque de C++ appartiennent à
un namespace nommé "std".

Syntaxe:
using namespace std;
Exemple d’utilisation:
#include <iostream> #include
<conio.h>
using namespace std; // Importation de l'espace de nom

void main()
{ double x, y;
// Le préfixe n'est plus requis :
cout << "X:";
cin >> x; cout
<< "Y:";
// Il est toujours possible d'utiliser le préfixe :
std::cin >> y;
cout << "x * y = " << x * y << endl;
_getch(); // Les fonctions de la bibliothèque C sont toujours utilisables
}

Il est possible aussi de définir un espace de nom alors les identificateurs de cet espace seront
préfixés.
Syntaxe :
namespace nom
{
// Placer ici les déclarations faisant partie de l'espace de nom
}

5
Exemple:
#include <iostream>
using namespace std; // Importation de l'espace de nom

namespace USTOMB
{
void afficher_adresse()
{ cout << "USTOMB" << endl;
cout << "El Mnaouar, BP 1505 , "<<endl<< " Bir El Djir 31000"<<endl ;
}
void afficher_coordonnees_completes()
{ afficher_adresse(); // Préfixe non requis, car dans le même espace de nom :
cout << "Tel : 041 61 71 46\n";
}
}

int main()
{ USTOMB::afficher_coordonnees_completes(); return 0;
}

I.2.7 Les entrées/sorties en C++


On peut utiliser les routines d’E/S da la bibliothèque standard de C (<stdio.h>). Mais C++
possède aussi ses propres possibilités d’E/S.
Les nouvelles possibilités d’E/S de C++ sont réalisées par l’intermédiaire des opérateurs <<
(sortie), >> (entrée) et des flots (stream) définis dans la bibliothèque <iostream> :

▪ cin : flot d’entrée correspondant à l’entrée standard (instance de la classe


istream_withassign)
▪ cout : flot de sortie correspondant à la sortie standard (instance de la classe
ostream_withassign)
▪ cerr : flot de sortie correspondant à la sortie standard d’erreur (instance de la classe
ostream_withassign)

Syntaxes :
cout << exp_1 << exp_2 << … … << exp_n ;

exp_ k : expression de type de base ou chaîne de caractères

cin >> var_1 >> var_2 >> … … >> var_n ;

var_k : variable de type de base ou char*

Tous les caractères de formatage comme '\t', '\n' peuvent être utilisés. Par ailleurs, l’expression
endl permet le retour à la ligne et le vidage du tampon.

6
Exemple:
#include <iostream> // Standard C++ I/O using
namespace std;

int main()
{ int val1, val2;
cout << "Entrer deux entiers: " << endl;
cin >> val1 >> val2;
cout << "Valeurs entrées: " << val1 << " et " << val2 <<
endl; cout << "valeur 1+valeur 2 =" << val1 + val2 << endl;
return 0;
}

On peut citer quelques avantages des nouvelles possibilités d'E/S :


• Vitesse d'exécution plus rapide.
• Il n'y plus de problème de types
• Autres avantages liés à la POO.

I.2.8 Bibliothèque iomanip


Il existe d’autres possibilités de modifier la façon dont les éléments sont lus ou écrits dans le
flot:
▪ dec: Lecture/écriture d'un entier en décimal.
▪ oct: Lecture/écriture d'un entier en octal.
▪ hex: lecture/écriture d'un entier en hexadécimal.
▪ endl: Insère un saut de ligne et vide les tampons.
▪ setw(int n): Affichage de n caractères.
▪ setprecision(int n): Affichage de la valeur avec n chiffres avec éventuellement un
arrondi de la valeur.

▪ setfill(char): Définit le caractère de remplissage flush vide les tampons après


écriture.

Exemple:
#include <iostream>
#include <iomanip> // attention a bien inclure cette librairie using namespace std ;

int main()
{ int i=1234;
float p=12.3456;
cout << "|" << setw(8) << setfill('*‘) << hex << i << "|" <<endl
<< "|" << setw(6) << setprecision(4) << p <<"|"<< endl;
return 0;
}

7
Affichage de
l’exécution du code:

I.3 Variables et constantes

I.3.1 Noms de variables


En C++, il y a quelques règles qui régissent les différents noms autorisés ou interdits :

▪ Les noms de variables sont constitués de lettres, de chiffres et du tiret-bas « _ »


uniquement. Le double souligné "__" au début du nom est réservé aux
variables/fonctions du système.
▪ Le premier caractère doit être une lettre (majuscule ou minuscule).
▪ On ne peut pas utiliser d'accents.
▪ On ne peut pas utiliser d'espaces dans le nom.

Exemple:
ageEtudiant, nom_etudiant, NOMBRE_Etudiants: Noms valides.
Ageétudiant, nom etudiant: Noms non valides.

I.3.2 Types de base


Après l’identification du nom de la variable, L'ordinateur doit connaitre ce qu'il a dans cette
variable, il faut donc indiquer quel type (nombre, mot, lettre, ou …) d'élément va contenir la
variable que nous aimerions utiliser [4].
Voici donc la liste des types de variables que l'on peut utiliser en C++ (Tableau 1) :

Type de donnée Signification Taille (en octets) Plage de valeurs acceptée


char Caractère 1 -128 à 127
unsigned char Caractère non signé 1 0 à 255
short int Entier court 2 -32 768 à 32 767
unsigned short int Entier court non signé 2 0 à 65 535
int Entier 2 (sur processeur 16 bits) -32 768 à 32 767
4 (sur processeur 32 bits) -2 147 483 648 à 2 147 483 647
unsigned int Entier non signé 2 (sur processeur 16 bits) 0 à 65 535
4 (sur processeur 32 bits) 0 à 4 294 967 295
long int Entier long 4 -2 147 483 648 à 2 147 483 647
unsigned long int Entier long non signé 4 0 à 4 294 967 295
float Flottant (réel) 4 -3.4*10-38 à 3.4*1038
double Flottant double 8 -1.7*10-308 à 1.7*10308
long double Flottant double long 10 -3.4*10-4932 à 3.4*104932

8
bool Booléen Prend deux valeurs: true et false
Même taille que le type int, mais une conversion implicite
parfois 1 sur quelques (valant 0 ou 1) est faite par le
compilateurs compilateur lorsque l'on affecte
un

Tableau 1 : Types détaillés de variables en langage C++ [4]

I.3.3 Déclaration des variables


Avant d’utiliser une variable, il faut indiquer à l'ordinateur le type de la variable que nous
voulons, son nom et enfin sa valeur c.-à-d. les trois caractéristique de la variable. En effet, en
C++, toute variable doit être déclarée avant d’être utilisée. Une variable se déclare de la façon
suivante:
type Nom_de_la_variable < (valeur) > ;
ou bien utiliser la même syntaxe que dans le langage C.
type Nom_de_la_variable < = valeur > ;
Soit l’exemple suivant, dans lequel nous déclarons des variables pour stocker les informations
d’un étudiant:

#include <iostream>
using namespace std; int
main()
{ string nomEtudiant("Moussa");

int ageEtudiant(20);

float moyGen(12.43);

double pi(3.14159);

bool estAdmis(true);

char lettre('a'); return 0;

I.3.4 Constantes
Contrairement aux variables les constantes ne peuvent pas être initialisées après leur
déclaration et leur valeur ne peut pas être modifiée après initialisation. Elles doivent être
déclarées avec le mot clé const et obligatoirement initialisées dès sa définition.

Syntaxe:
const <type> <NomConstante> = <valeur>;

Exemple:

9
const char c ('A'); //exemple de constante caractère const
int i (2017); //exemple de constante entière const double PI
(3.14); //exemple de constante rèel

I.3.5 Types dérivés


Il existe d’autres dérivés des types fondamentaux et des types définis par le programmeur,
cette dérivation se fait à l’aide d’opérateurs de déclaration. On peut distinguer quatre types de
dérivation:
▪ Pointeur
▪ Référence
▪ Tableau
▪ Prototype des fonctions (voir chapitre 3)
a. Pointeurs
Un pointeur contient l’adresse d’un objet en mémoire, l’objet pointé peut être référencé via
le pointeur. On peut utiliser les pointeurs pour la manipulation des objets alloués
dynamiquement, création et manipulation de structures chaînées, etc.
Exemple:
int* ptrInt, q; // attention ptrInt est un pointeur sur un entier et q est un entier
double* ptrDouble, *ptr, **data ; // ptrDouble et ptr sont des pointeurs sur des doubles et data
//pointe deux fois sur un double
char *text ; // text pointe sur un caractère
const char* PCste = "this is a string"; // PConst pointe sur une chaine constante

b. Références
En C++, on peut coller plusieurs étiquettes à la même case mémoire (ou variable). On obtient
alors un deuxième moyen d'y accéder. On parle parfois d'alias, mais le mot correct
en C++ est référence. Pour déclarer une référence sur une variable, on utilise une esperluette
(&). Exemple:
#include <iostream>
using namespace std; int
main()
{ int i(5); int & j(i); // la variable ‘j ‘ fait référence à ‘i '. On peut utiliser à partir
d'ici 'j ' ou 'i '
// indistinctement puisque ce sont deux étiquettes de la même case en mémoire
int k=j ;
cout<<" Valeur de i: "<<i<< " || "<<" Valeur de j: "<<j<<" || "<<" Valeur de k: "<<k<<
endl; j=j+2; k=i ;
cout<<" Valeur de i: "<<i<< " || "<<" Valeur de j: "<<j<<" || "<<" Valeur de k: "<<k<< endl;
return 0;
}

10
Affichage de l’exécution du
code:

▪ Remarque: Si la variable est définie dans un autre module il faut la déclarer avant de
l’utiliser en utilisant le mot clé « extern » Exemple: [1]
extern float maxCapacity; // declaration float
limit = maxCapacity * 0.90; // usage

c. Tableaux
Un tableau est une collection d’objets du même type, chacun de ces objets est accédé par sa
position. La dimension du tableau doit être connue en temps de compilation.
Exemple:
const int numPorts = 200; double
portTable[numPorts]; int
bufferSize;
char buffer[bufferSize]; // ERROR: the value of bufferSize is unknown
Le tableau peut être initialisé lors de la définition :
int groupTable[3] = {134, 85, 29}; int userTable[] = {10, 74, 43, 45, 89}; // 5 positions

I.3.6 Définition de Nouveaux Types


On peut définir un synonyme pour un type prédéfini à l’aide du mot clé typedef.
typedef float Angle;
Le nouveau type Angle peut être utilisé pour définir des variables
Angle rotation = 234.78;
Toutes les opérations valides avec le type original le sont aussi avec le synonyme
Angle rotation = "white"; // ERROR: Angle is float not char*

11
I.3.7 Conversion de Type
Ce type d’opération consiste à modifier la façon d’interpréter la séquence de bits contenue
dans une variable
a. Conversion implicite
Faite automatiquement par le compilateur (non sans warnings) lorsque plusieurs types de
données sont impliqués dans une opération.
▪ Lors de l’affectation d’une valeur à un objet, le type de la valeur est modifié au type de
l’objet
int count = 5.890; // conversion à int 5
▪ Chaque paramètre passé à une fonction est modifié au type de l’argument espéré par la
fonction
extern int add(int first, int second); count =
add(6.41, 10); // conversion à int 6
▪ Dans les expressions arithmétiques, le type de taille supérieure détermine le type de
conversion
int count = 3;
count + 5.8978; // conversion à double 3.0 + 5.8978

Exemple :
int count = 10;
count = count * 2.3; // Conversion de 23.0 à int 23
▪ Tout pointeur à une valeur non constante de n’importe quel type, peut être affecté à un
pointeur de type void*
char* name = 0;
void* aPointer = name; // conversion implicite
b. Conversion explicite ou casting
Cette conversion est demandée par le programmeur Exemple :
int result = 34;
result = result + static_cast<int>(10.890);

Peut être aussi écrit


result = result + int(10.890);

Permet d’utiliser des pointeurs génériques


char* name; void*
genericPointer;
name = (char*)genericPointer; // Conversion explicite

En combinant des noms de variables, des opérateurs, des parenthèses et des appels de
fonctions on obtient des expressions.

12
I.4.1 Opérateurs Arithmétiques et logiques
a. Opérateurs multiplicatifs
Opération Symbole Exemple

Multiplication * resultat = a * b; Si a=5 et b=2 resultat = 10


Division / resultat = a / b; Si a=5 et b=2 resultat = 2
Modulo % resultat = a % b; Si a=5 et b=2 resultat = 1
Le Modulo représente le reste de la division entière. Cet opérateur n'existe que pour les
nombres entiers.
La division est entière si les deux arguments sont entiers.

b. Opérateurs additifs
Opérateur Signification Arité Associativité
+ Addition unaire unaire Non
- négation unaire unaire Non
+ Addition binaire Gauche à droite
- Soustraction binaire Gauche à droite

Il est clair que « l’addition » unaire ne fait rien pour les types simples (nombres entiers
et réels), mais grâce à la surcharge des opérateurs, on peut donner un sens à cet opérateur
sur des types (=classes) complexes

c. Opérateurs de décalage
Opérateur Signification Arité Associativité
<< Décalage à droite Binaire Gauche à droite
>> Décalage à gauche binaire Gauche à droite

Ces opérateurs sont assez peu employés dans leur contexte « C » (décalage de bits),
mais ils prennent une importance considérable en C++ dans la manipulation des flux
d’entrées/sorties.

Exemple :
int b=100 ; // b=1100100 en code binaire std
::cout<< " (b<<1) = " <<(b<<1) << std ::endl ;
std ::cout<< " (b<<2) = " <<(b<<2) << std
::endl ; std ::cout<< " (b>>1) = " <<(b>>1) <<
std ::endl ; std ::cout<< " (b>>2) = " <<(b>>2)
<< std ::endl ;

Affichage de l’exécution du code:

13
d. Opérateurs relationnels
Opérateur Signification Arité Associativité
Inférieur à binaire Gauche à droite
<
> Supérieur à binaire Gauche à droite
<= Inférieur ou égal à binaire Gauche à droite

>= Supérieur ou égal à binaire Gauche à droite


en C++, ces opérateurs retournent un type bool (valeurs true ou false) contrairement au
C où ils retournent un type int (valeurs 0 ou 1)

e. Opérateurs d’égalité
Opérateur Signification Arité Associativité
== Egalité binaire Gauche à droite
!= Inégalité binaire Gauche à droite

Erreur classique: ne pas confondre l’opérateur d’égalité (==) avec l’opérateur


d’affectation (=).

f. Opérateurs sur les bits


Opérateur Signification Arité Associativité
& Et binaire binaire Gauche à droite
^ Ou exclusif binaire binaire Gauche à droite
| Ou binaire binaire binaire Gauche à droite

Ces opérateurs travaillent sur les bits, comme les décalages. Attention de ne pas les
confondre avec les opérateurs logiques.

g. Opérateurs logiques
Opérateur Signification Arité Associativité
&& Et logique binaire Gauche à droite
|| Ou logique binaire Gauche à droite
e1 ?e2 :e3 Condition if - else ternaire De droite à gauche
Ces opérateurs travaillent sur le type bool et retournent un bool.
Le dernier opérateur signifie: « si e1 est vraie alors faire e2, sinon faire e3 »

h. Opérateurs d’affectation
Opérateur Signification Arité Associativité
= Affectation R=a Droite à gauche

14
*= Multiplication et affectation a = a*b Droite à gauche
/= Division et affectation a = a/b Droite à gauche
%= Modulo et affectation a = a%b Droite à gauche
+= Addition et affectation a = a+b Droite à gauche
-= Soustraction et affectation a = a-b Droite à gauche
<<= Décalage à gauche et affectation a = a<<b Droite à gauche
>>= Décalage à droite et affectation a = a>>b Droite à gauche
&= Et binaire et affectation a = a&b Droite à gauche
|= Ou binaire a = a|b Droite à gauche
^= Ou exclusif binaire et affectation a = a^b Droite à gauche
++ Incrémentation a = a+1 ≈ a++ Droite à gauche
-- Décrémentation a = a-1 ≈ a-- Droite à gauche

Exemple:
int a, b=3, c, d=3 ; a = ++b ; // équivalent à b++ ; puis
a=b ; => a=b=4 c = d++ ; // équivalent à c=d ; puis
d++ ; => c=3 et d=4

I.5 Structures de contrôle


I.5.1 Structures de contrôle conditionnelles
On appelle structures de contrôle conditionnelles les instructions qui permettent de tester si
une condition est vraie ou non. Il s'agit des instructions suivantes:
La structure conditionnelle if
Le branchement conditionnel switch

I.5.1.1 Structure conditionnelle « if » a.


Première condition if
La structure conditionnelle if permet de réaliser un test et d’exécuter une instruction ou
non selon le résultat de ce test [4a]. Syntaxe :
if (condition) {
instruction ; }

où condition est une expression dont la valeur est booléenne ou entière. Toute valeur non nulle
est considérée comme vraie. Si le test est vrai, instruction est exécutée.
Il est possible de définir plusieurs conditions à remplir avec les opérateurs ET et OU (&&
et ||). Par exemple, l'instruction ci-dessous exécutera les instructions si l'une ou l'autre
des deux conditions est vraie :
Exemple:
if ((condition1) || (condition2))
{ instruction ; }

15
b. if … else : ce qu'il faut faire si la condition n'est pas vérifiée
L'instruction if dans sa forme basique ne permet de tester qu'une condition, or la plupart du
temps on aimerait pouvoir choisir les instructions à exécuter en cas de non réalisation de la
condition. L'expression if ... else permet d'exécuter une autre série d'instructions en cas de non-
réalisation de la condition.

c. else if : effectuer un autre test


Il est possible de faire plusieurs tests à la suite. Pour faire tous ces tests un à un dans l'ordre,
on va avoir recours à la condition else if qui signifie « sinon si ». Les tests vont être lus dans
l'ordre jusqu'à ce que l'un d'entre eux soit vérifié.
Exemple:
#include <iostream> using
namespace std; int main() {
float a; cout<<"un réel : ";
cin>>a ; if(a>0)
cout<< a << " est positif " << endl;
else if(a==0) cout<< a << " est
nul " << endl; else cout<< a <<
" est négatif " << endl; return 0;
}

I.5.1.2 Branchement conditionnel switch


Dans le cas où plusieurs instructions différentes doivent être exécutées selon la valeur d’une
variable de type intégral, l’écriture des if successifs peut être relativement lourde. Le C++
fournit donc la structure de contrôle switch, qui permet de réaliser un branchement conditionnel.
Syntaxe :

16
switch (choix)
{
case valeur1 :
instruction1;
break;
case valeur2 :
instruction2;
break;
case valeur3 :
instruction3;
break;
default:
instructionParDéfaut;
}
La variable choix est évaluée en premier. Son type doit être entier. Selon le résultat de
l’évaluation, l’exécution du programme se poursuit au cas de même valeur. Si aucun des cas ne
correspond et si default est présent, l’exécution se poursuit après default. Si en revanche default
n’est pas présent, on sort du switch. Pour forcer la sortie du switch, on doit utiliser le mot-clé
break.
Exemple: [4a]
#include <iostream> using
namespace std;
int main()
{ int a;
cout << "Tapez la valeur de a : "; cin >> a; // Ce
programme demande à l'utilisateur de taper une switch(a) //
valeur entière et la stocke dans la variable a. On teste
{ case 1 : // ensuite la valeur de a : en fonction de cette valeur on
cout << "a vaut 1" << endl; // affiche respectivement les messages "a vaut 1","a vaut 2
break; // ou 4","a vaut 3, 7 ou 8", ou "valeur autre". case 2 :
case 4 :
cout << "a vaut 2 ou 4" <<
endl; break; case 3 :
case 7 : case 8 :
cout << "a vaut 3, 7 ou 8" <<
endl; break; default :
cout << "valeur autre" << endl;
}
return 0;
}

17
Affichage de l’exécution du code:

I.5.2 Structures de contrôle itératives


Les structures de contrôle itératives sont des structures qui permettent d'exécuter plusieurs fois
la même série d'instructions jusqu'à ce qu'une condition ne soit plus réalisée. On appelle ces
structures des boucles.
Il existe 3 types de boucles à connaître :
la boucle for, la boucle while et la boucle do … while.

I.5.2.1 Boucle for


La structure de contrôle for est sans doute l’une des plus importantes. Elle permet de
condenser:
Un compteur: une instruction (ou un bloc d’instructions) exécutée avant le premier
parcours de la boucle du for. Il consiste à préciser le nom de la variable qui sert de
compteur et éventuellement sa valeur de départ.
Une condition: une expression dont la valeur déterminera la fin de la boucle.
Une itération: une instruction qui incrémente ou décrémente le compteur.
Syntaxe:
for (compteur; condition; itération))
{
Iinstructions ;
}

18
Exemple :
#include <iostream>
using namespace std;

int main()
{ int compteur(0);
for (compteur = 1 ; compteur < 10 ; compteur++)
cout << compteur <<" || " ;
cout << endl ; return 0 ;
}

Affichage de
l’exécution du code:

I.5.2.2 Boucle while


Le while permet d’exécuter des instructions en boucle tant qu’une condition est vraie.
Syntaxe:
while (condition)
{ instructions ; }
Exemple :
#include <iostream> using
namespace std;

int main()
{ int i = 1;
while (i < 10) //affiche les valeurs de 1 jusqu’à
9
{ cout <<" "<< i <<endl ;
i++ ;
}
return 0 ;
}

Affichage de l’exécution du
code:

19
I.5.2.3 Boucle do … while
La structure de contrôle do … while permet, tout comme le while, de réaliser des boucles en
attente d’une condition. Cependant, contrairement à celui-ci, le do … while effectue le test sur
la condition après l’exécution des instructions. Cela signifie que les instructions sont toujours
exécutées au moins une fois, que le test soit vérifié ou non.

Syntaxe:
do
{
instructions ; } while (condition) ;

Exemple:
#include <iostream>
using namespace std;

int main()
{ int i = 1;
do
{ cout << i << endl;
i++;
} while(i<10); //affiche les valeurs de 1 jusqu’à 9
return 0;
}

I.5.3 Ruptures de Séquence


I.5.3.1 break
L'instruction break sert à "casser" ou interrompre une boucle (for, while et do … while), ou
un switch. L’exécution reprend immédiatement après le bloc terminé.
Exemple:
#include <iostream>
using namespace std;

int main()
{ int i;
for (i = 0 ; i < 10 ; i++)
{ cout <<" i= " <<i<< endl;
if (i==3)
break;
}
cout<<" Valeur de i lors de la sortie de la boucle : " << i <<endl ; return 0;
}

20
Affichage de l’exécution du
code:

I.5.3.2 continue
L'instruction continue sert à "continuer" une boucle (for, while et do … while) avec la
prochaine itération.
Exemple:
#include <iostream>
using namespace std;

int main()
{ int i;
for (i = 0 ; i < 5 ; i++)
{ if (i==3)
continue;
cout <<" i= " <<i<< endl;
}
cout<<" Valeur de iaprès la sortie de la boucle : " << i <<endl ; return 0;
}

Affichage de l’exécution du code:

21
Chapitre II : Fonctions et structures de programmes

II.1 Création et utilisation d’une fonction


Une fonction est une opération définie par le programmeur caractérisée par :
Son nom,
le type de valeur qu’elle renvoie,
les paramètres qu’elle reçoit pour faire son travail,
l’instruction-bloc qui effectue le travail (corps de la fonction).

Exemple:
Liste des
Type de la Nom de la
paramètres
valeur de retour fonction

Corps de la fonction
}

II. 1.1 Déclaration d’une fonction


Toute fonction doit être déclarée avant d’être appelée pour la première fois. La définition
d’une fonction peut faire office de déclaration.

Il peut se trouver des situations où une fonction doit être appelée dans une autre fonction
définie avant elle. Comme cette fonction n’est pas définie au moment de l’appel, elle doit être
déclarée.

Le rôle des déclarations est donc de signaler l’existence des fonctions aux compilateurs afin
de les utiliser, tout en reportant leur définition de ces fonctions plus loin ou dans un autre fichier.
Syntaxe :
type nomDeLafonction (paramètres) ;
Exemple [4b]:
double Moyenne(double x, double y); // Fonction avec paramètres et type retour char
LireCaractere(); // Fonction avec type de retour et sans paramètres
void AfficherValeurs(int nombre, double valeur); //Fonction avec paramètres et sans type de
retour

II.1.2 Définition d’une fonction


Toute fonction doit être définie avant d’être utilisée.
Syntaxe:
type nomDeLafonction (paramètres)
{
Instructions ;
}

22
Il est possible de créer plusieurs fonctions ayant le même nom. Il faut alors que la liste des
arguments des deux fonctions soit différente. C'est ce qu'on appelle la surcharge d'une fonction.
Exemple:
#include <iostream>
using namespace std;

double moyenne(double a, double b); // déclaration de la fonction somme :

int main()
{ double x, y, resultat; cout << "Tapez
la valeur de x : "; cin >> x; cout << "Tapez
la valeur de y : "; cin >> y;
resultat = moyenne(x, y); //appel de notre fonction somme
cout <<" Moyenne entre "<< x <<" et " << y << " est : " << resultat << endl;
return 0;
}
// définition de la fonction moyenne :
double moyenne(double a, double b)
{ double moy;
moy = (a + b)/2;
return moy;
}

Dans ce programme, on a créé une fonction nommée moyenne qui reçoit deux nombres réels
a et b en paramètre et qui, une fois qu'elle a terminé, renvoie un autre nombre moy réel qui
représente la moyenne de a et b. l’appel de cette fonction se fait au niveau du programme
principale (main).

Dans cet exemple, le prototype est nécessaire, car la fonction est définie après la fonction
main() qui l'utilise. Si le prototype est omis, le compilateur signale une erreur.

Le prototype peut être encore écrit de la manière suivante:


double moyenne(double , double )

Affichage de l’exécution:

23
II.1.3 Fonctions sans retour
C++ permet de créer des fonctions qui ne renvoient aucun résultat. Mais, quand on la déclare,
il faut quand même indiquer un type. On utilise le type void. Cela veut tout dire : il n'y a vraiment
rien qui soit renvoyé par la fonction.

Exemple:
#include <iostream> using namespace std;

void presenterPgm () ;

int main ()
{
presenterPgm(); return 0;
}

void presenterPgm ()
{
cout << "ce pgm …";
}

Une fonction produit toujours au maximum un résultat, c’est-à-dire qu’Il n'est pas possible de
renvoyer plus qu'une seule valeur.

II.2 Surcharge des fonctions


En C++, Plusieurs fonctions peuvent porter le même nom si leurs signatures diffèrent. La signature
d'une fonction correspond aux caractéristiques de ses paramètres
leur nombre
le type respectif de chacun d'eux
Le compilateur choisira la fonction à utiliser selon les paramètres effectifs par rapport aux
paramètres formels des fonctions candidates.

24
Exemple:

#include <iostream>
using namespace std;

// définition de la fonction somme qui calcul la somme de deux réels

double somme(double a, double b)


{ double r; r = a + b;
return r;
}

// définition de la fonction somme qui calcul la somme de deux entiers

double somme(int a, int b)


{ double r; r = a + b;
return r;
}

// définition de la fonction somme qui calcul la somme de trois réels

double somme(double a, double b, double c)


{ double r; r = a + b + c;
return r;
}
int main()
{ double x, y,z, resultat; cout << "Tapez
la valeur de x : "; cin >> x; cout <<
"Tapez la valeur de y : "; cin >> y; cout
<< "Tapez la valeur de z : "; cin >> z;

//appel de notre fonction somme (double,double) resultat


= somme(x, y);
cout << x << " + " << y << " = " << resultat << endl;

//appel de notre fonction somme (int,int)


resultat = somme(static_cast<int>(x), static_cast<int> ( y));
cout << static_cast<int>(x) << " + " << static_cast<int>( y) << " = " << resultat << endl;

//appel de notre fonction somme (double, double, double) resultat


= somme(x, y,z);
cout<< x << " + " << y << " + " << z<< " = " << resultat << endl;

//appel de notre fonction somme (double, double, double)


resultat = somme(static_cast<int>(x), static_cast<int>( y), static_cast<int>(z));
cout<< static_cast<int> ( x) << " + " << static_cast<int>(y) << " + " << static_cast<int>( z)<< " = "
<< resultat << endl;

25
return 0;
}

26
Affichage de l’exécution:

II.3 Arguments par défaut


On peut, lors de la déclaration d’une fonction, donner des valeurs par défaut à certains
paramètres des fonctions. Ainsi, lorsqu'on appelle une fonction, on ne sera pas obligé d'indiquer
à chaque fois tous les paramètres! [4b] Exemple :
#include <iostream>
using namespace std;

void afficheLigne(const char c, const int n=5)


{ for(int i(0) ; i<n ; ++i)
cout<<c ;
}

int main()
{ afficheLigne(‘+’) ;
cout<<endl ;
afficheLigne(‘*’,8) ;
return 0 ;
}

Affichage de l’exécution:

Il y a quelques règles que vous devez retenir pour les valeurs par défaut:

Seul le prototype doit contenir les valeurs par défaut.


Les valeurs par défaut doivent se trouver à la fin de la liste des paramètres.
Vous pouvez rendre tous les paramètres de votre fonction facultatifs.

II.4 Passage des paramètres par valeur et par variable


Il y a deux méthodes pour passer des variables en paramètres dans une fonction: le passage par
valeur et le passage par variable. Ces méthodes sont décrites ci-dessous.
II.4.1 Passage par valeur
La valeur de l’expression passée en paramètre est copiée dans une variable locale. C’est cette
variable qui est utilisée pour faire les calculs dans la fonction appelée.

27
Pour mieux comprendre, Prenons le programme suivant qui ajoute 2 à l'argument fourni en
paramètre.

Exemple : include <iostream>


using namespace std;

void sp (int );

int main()
{int n = 3;
cout << "n=" << n << endl;
sp (n);
cout << "n=" << n;
return 0 ;
}
void sp (int nbre)
{ cout << "----------"<<endl ;
cout << "nbre=" << nbre << endl;
nbre = nbre + 2; cout
<< "nbre=" << nbre;
cout << "----------"<<endl ;
}

Affichage de l’exécution:

II.4.2 Passage par variable


Consiste à passer l'adresse d'une variable en paramètre. Toute modification du paramètre dans
la fonction affecte directement la variable passée en argument correspondant, puisque la
fonction accède à l’emplacement mémoire de son argument. Il existe 2 possibilités pour
transmettre des paramètres par variables :
Les références
Les pointeurs
II.4.2.1 Passage par références
Consiste à passer une référence de la variable en argument. Ainsi, aucune variable temporaire
ne sera créée par la fonction et toutes les opérations (de la fonction) seront effectuées
directement sur la variable. Le plus simple est d’utiliser le mécanisme de référence « & ».

28
Exemple :
#include <iostream>
using namespace std;

void sp (int &); // ajout de la référence au niveau du prototype

int main()
{int n = 3;
cout << "n=" << n << endl;
sp (n); //appel de la fonction
cout << "n=" << n<<endl ;;
return 0 ;
}
void sp (int & nbre) //Ajout de la référence dans la fonction
{ cout << "----------"<<endl ;
cout << "nbre=" << nbre << endl;
nbre = nbre + 2;
cout << "nbre=" << nbre<<endl ;
cout << "----------"<<endl ;
}

Affichage de l’exécution:

II.4.2.2 Passage par adresses (pointeurs)


Consiste à passer l'adresse d'une variable en paramètre. Toute modification du paramètre dans
la fonction affecte directement la variable passée en argument correspondant, puisque la
fonction accède à l’emplacement mémoire de son argument. Le pointeur indique au compilateur
que ce n’est pas la valeur qui est transmise, mais une adresse (un pointeur).

29
Exemple :
#include <iostream>
using namespace std;

void sp (int *); // ajout de l’étoile au niveau du prototype

int main()
{int n = 3;
cout << "n=" << n << endl; sp
(&n); //appel de la fonction
cout << "n=" << n<<endl ;
return 0 ;
}
void sp (int * nbre) //Ajout de la référence dans la fonction
{ cout << "----------"<<endl ;
cout << "nbre=" << *nbre << endl;
*nbre = *nbre + 2;
cout << "nbre=" <<* nbre<<endl ;
cout << "----------"<<endl ;
}

Affichage de l’exécution:

II.5 Fonctions inline


Parfois le temps d'exécution d'une fonction est petit comparé au temps nécessaire pour appeler
la fonction. Le mot clé inline informe le compilateur qu'un appel à cette fonction peut être
remplacé par le corps de la fonction.
Syntaxe : inline type fonct(liste_des_arguments){...}
Exemple
inline int max(int a, int b)
{
return (a < b) ? b : a;
}
void main()
{ int m = max(134, 876);
cout<< "m=" << m<<endl ;
}

Les fonctions "inline" permettent de gagner au niveau de temps d'exécution, mais augmente
la taille des programmes en mémoire.
Contrairement aux macros dans C, les fonctions "inline" évitent les effets de bord (dans une
macro.

30
Les fonctions "inline" doivent être définies dans des fichiers d'entête (.h) qui seront inclus
dans des fichiers source (.cpp), pour que le compilateur puisse faire l'expansion.

Chapitre III : Tableaux, Pointeurs et Chaines de caractères

III.1 Tableaux unidimensionnels


Une variable entière de type int ne peut contenir qu'une seule valeur. Si on veut stocker en
mémoire un ensemble de valeurs, il faut utiliser une structure de données appelée tableau qui
consiste à réserver espace de mémoire contiguë dans lequel les éléments du tableau peuvent être
rangés. On peut distinguer deux sortes de tableaux unidimensionnels :

Les tableaux statiques : Ceux dont la taille est connue à l'avance, et


Les tableaux dynamiques : ceux dont la taille peut varier en permanence.

III.1.1 Tableaux unidimensionnels statiques


II.1.1.1 Déclaration et initialisation
Comme toujours en C++, une variable est composée d'un nom et d'un type. Comme les
tableaux sont des variables, cette règle reste valable. Il faut juste ajouter une propriété
supplémentaire, la taille du tableau.

Syntaxe :

type nomDuTableau [taille];

type: définit le type des éléments que contient le tableau.


nomDuTableau: le nom que l'on décide de donner au tableau.
taille: nombre entier qui détermine le nombre de cases que le tableau doit comporter.
Rappelez-vous qu’un tableau en langage C++ est composé uniquement d'éléments de même
type.
Le nom du tableau suit les mêmes règles qu'un nom de variable.

Exemple:

int meilleurScore [5];


char voyelles [6]; double
notes [20];
Il est possible aussi d'initialiser le tableau à la déclaration en plaçant entre accolades les valeurs,
séparées par des virgules. Exemple :

int meilleurScore [5] = {100, 432, 873, 645, 314};

31
Le nombre de valeurs entre accolades ne doit pas être supérieur au nombre d'éléments du
tableau.
Les valeurs entre accolades doivent être des constantes, l'utilisation de variables provoquera
une erreur du compilateur.
Si le nombre de valeurs entre accolades est inférieur au nombre d'éléments du tableau, les
derniers éléments sont initialisés à 0.
Si le nombre de valeur entre accolades est nul, alors tous les éléments du tableau s’initialisent
à zéro. Exemple :
int meilleurScore [5] = {};
III.1.1.2 Manipulation des éléments
Un élément du tableau (repéré par le nom du tableau et son indice) peut être manipulé
exactement comme une variable, on peut donc effectuer des opérations avec (ou sur) des
éléments de tableau.

Le point fort des tableaux, c'est qu'on peut les parcourir en utilisant une boucle. On peut ainsi
effectuer une action sur chacune des cases d'un tableau, l'une après l'autre : par exemple afficher
le contenu des cases.

#include <iostream> using


namespace std;

int main()
{
int const taille(10); //taille du tableau int
tableau[taille]; //declaration du tableau

for (int i(0); i<taille; i++ )


{ tableau[i]=i*i; cout<<"Le tableau ["<<i<<"] contient la valeur "<<tableau[i]<<endl; }
return 0;
}

Affichage de l’exécution:

III.1.1.3 Tableaux et fonctions


Au même titre que les autres types de variables, on peut passer un tableau comme argument
d'une fonction. Voici donc une fonction qui reçoit un tableau en argument :

32
void afficheTableau(int tableau[], int n)
{
for(int i = 0; i < n; i++)
cout << tableau[i] << endl;
}

À l'intérieur de la fonction afficheTableau(), il n'y a aucun moyen de connaître la taille du


tableau ! C’est pour cela nous avons ajouté un deuxième argument n contenant la taille du
tableau.

III.1. 2 Tableaux unidimensionnels dynamiques (classe vector)


Les tableaux que nous avons vus jusqu'ici sont des tableaux statiques. Cette forme de tableaux
vient du langage C, et est encore très utilisée. Cependant, elle n'est pas très pratique. En
particulier :

Un tableau de cette forme ne connaît pas sa taille.


On ne peut pas faire d'affectation globale.
une fonction ne peut pas retourner de tableaux.

Pour remédier ces trois problèmes, Le C++ a introduit la notion des tableaux dynamiques ces
derniers sont des tableaux dont le nombre de cases peut varier au cours de l'exécution du
programme. Ils permettent d'ajuster la taille du tableau au besoin du programmeur.

III.1. 2.1 Déclaration


La première différence se situe au début de votre programme. Il faut ajouter la ligne #include
<vector> pour utiliser ces tableaux. Et la deuxième différence se situe dans la manière de
déclarer un tableau.
Syntaxe:
vector <type> nomDuTableau(taille);

Par exemple, pour un tableau de 3 entiers, on écrit :


vector<int> tab(3);
Pour initialiser les éléments de tab2 aux mêmes valeurs que tab1.
vector<int> tab2(tab1);
On peut même déclarer un tableau sans cases en ne mettant pas de parenthèses du tout:
vector<int> tab;

III.1.2.2 Accès aux éléments


On peut accéder aux éléments de tab de la même façon qu'on accéderait aux éléments d'un
tableau statique : tab[0] = 7.

33
Exemple:
#include <iostream>
#include <vector>
using namespace std;

int main()
{
int const TailleTab(5); //Déclaration
du tableau
vector<int> Scores(TailleTab);

//Remplissage du tableau
Scores[0] = 100432; Scores[1] = 87347; Scores [2] = 64523; Scores[3] = 31415; Scores[4] =
118218;

for(int i(0); i<TailleTab; ++i)


cout << "Meilleur score de joueur " << i+1 << " est : " << Scores[i] << endl;
return 0;
}

III.1.2.2 Modification de la taille

On peut modifier la taille d’un tableau soit en ajoutant des cases à la fin d'un tableau ou en
supprimant la dernière case d'un tableau.

Commençons par ajouter des cases à la fin d'un tableau.

a. Fonction push_back()

Il faut utiliser la fonction push_back(). On écrit le nom du tableau, suivi d'un point et du mot
push_back avec, entre parenthèses, la valeur qui va remplir la nouvelle case.

Exemple :
vector<int> tableau(3,2); //Un tableau de 3 entiers valant tous 2
tableau.push_back(8); //On ajoute une 4ème case au tableau qui contient la valeur 8

b. Fonction pop_back()
On peut supprimer la dernière case d'un tableau en utilisant la fonction pop_back() de la même
manière que push_back(), sauf qu'il n'y a rien à mettre entre les parenthèses.

Exemple :
vector<int> tableau(3,2); //Un tableau de 3 entiers valant tous 2 tableau.pop_back(8); // Il y a
plus que 2 éléments dans le tableau

34
IV.1.2.3 Autres opérations de la classe "vector"

Opération Description
Accès aux éléments, itérateurs
retourne une référence sur le premier élément
front() ex : vf.front() += 11; // +11 au premier élément

retourne une référence sur le dernier élément


back() ex : vf.back()+=22; // +22 au dernier élément

méthode d’accès avec contrôle de l’existence de l’élément (possibilité de récupérer


une erreur en cas de débordement)
ex : for(int i=0; i<vi.size(); i++)
at()
vi.at(i)=rand()%100;
retourne un pointeur sur le premier élément du tableau interne au conteneur ex:
int*p2=vi.data();
data() for(int i=0; i<vi.size(); i++,
p2++) cout<<*p2<<"-";
cout<<endl;

Affectation, Insertion et Suppression d'éléments n'importe où dans le tableau


remplace le contenu d'un vecteur par un nouveau contenu en adaptant sa taille si
besoin
ex: vector<int> v1;
vector<int> v2;
assign() v1.assign(10, 50); // v1 remplacé par 10 entiers à 50

int tab[] = { 10, 20, 30 }; // v2 remplacé par les éléments du tableau tab
v2.assign(tab, tab + 3);
// affichage des tailles des vecteurs
cout << int(v1.size()) << endl; cout << int(v2.size()) << endl;

ajoute un élément x avant l'élément désigné par l'itérateur p


insert(p, x) ex: vector<float> v;
v.insert(v.begin(),1.5); // ajoute 1.5 avant, au début
ajoute n copies de x avant l'élément désigné par l'itérateur p
insert(p, n, x) ex: v.insert(v.end(),5,20) ; // ajoute cinq éléments initialisés 20 à la fin

ajoute un élément x à la position désignée par l'itérateur p et décale le reste. Les


arguments passés pour x correspondent à des arguments pour le constructeur de x
emplace(p,x)
ex : p=v.begin()+2; // ajoute 100 à la position 2
v.emplace(p, 100);
supprime l'élément pointé par l'itérateur p

35
ex: //supprime les éléments compris entre les itérateurs premier et dernier
vector<float>::iterator prem = v.begin()+1; vector<float>::iterator dern
= v.end()-1; v.erase(prem,dern); affiche_vector(v);
erase(p)

efface tous les éléments d'un conteneur. Équivalent à c.erase(c.begin(), c.end()) ex:
v.clear(); // efface tout le conteneur
clear()
Taille et capacité
retourne le nombre d'éléments du « vector »
size() ex : vector<int> v(5);
cout<<v.size()<<endl; // 5
redimensionne un « vector » avec nb éléments. Si le conteneur existe avec une
taille plus petite, les éléments conservés restent inchangés et les éléments
supprimés sont perdus. Avec une taille plus grande, les éléments ajoutés sont
initialisés avec une valeur par défaut ou avec une valeur spécifiée en val ex:
vector<int> v(5);
for(unsigned i=0; i<v.size(); i++)
resize(nb) v[i]=i;
resize(nb, val) affiche_vector(v); // 0,1,2,3,4
v.resize(7); affiche_vector(v); //
0,1,2,3,4,0,0 v.resize(10,99);
affiche_vector(v); // 0,1,2,3,4,0,0,99,99,99
v.resize(3); affiche_vector(v); // 0,1,2

retourne le nombre courant d'emplacements mémoire réservés. C'est-à-dire le total


de mémoire allouée en nombre d'éléments pour le conteneur. Attention, à ne pas
confondre avec le nombre des éléments effectivement contenus retourné par size().
ex : vector<int>v(10); cout<<"nombre elements
: "<<v.size()<<endl;//10 cout<<"capacite :
capacity()
"<<v.capacity()<<endl; // 10 v.resize(12);
cout<<"nombre elements : "<<v.size()<<endl; //12
cout<<"capacite : "<<v.capacity()<<endl; // 15

Le parcours d'un « vector » peut aussi s'effectuer en utilisant des itérateurs (pointeurs) plutôt
que le système d'indice. De ce fait, la classe « vector » est équipée avec toutes les méthodes les
concernant :
Méthode Description
begin() retourne un itérateur « iterator » qui pointe sur le premier élément
end() retourne un itérateur « iterator » qui pointe sur l'élément suivant le dernier
rbegin() retourne un itérateur « reverse_iterator » qui pointe sur le premier élément de la séquence
inverse (le dernier) ;
rend() retourne un itérateur « reverse_iterator » qui pointe sur l'élément suivant le dernier dans
l'ordre inverse (balise de fin, par exemple NULL)

36
cbegin() retourne un itérateur « const_iterator » qui pointe sur le premier élément. L'élément
pointé n'est alors accessible qu'en lecture et non en écriture. Il ne peut pas être modifié

cend() retourne un itérateur « const_iterator » qui pointe sur ce qui suit le dernier élément (balise
de fin, par exemple NULL). L'élément pointé n'est alors accessible qu'en lecture et non
en écriture. Il ne peut pas être modifié
crbegin() retourne un itérateur « const_reverse_iterator » qui pointe sur le premier élément de la
séquence inverse (le dernier). L'élément pointé n'est accessible qu'en lecture et ne peut
pas être modifié
crend() retourne un itérateur « const_reverse_iterator » qui pointe sur la fin de la séquence
inverse, avant le premier élément (balise de fin sens inverse, par exemple NULL).
L'élément pointé n'est accessible qu'en lecture et ne peut pas être modifié

Exemple :
vector<int>vi(15, 7); // 15 cases entières initialisées avec 7 vector<int>::iterator
it;
for (it=vi.begin(); it!=vi.end();
it++) cout<<*it<<"-";
cout<<endl;

III.1.2.3 Les vector et les fonctions

Passer un tableau dynamique en argument à une fonction est beaucoup plus simple que pour
les tableaux statiques. Comme pour n'importe quel autre type, il suffit de mettre vector<type>
en argument. Et c'est tout. Grâce à la fonction size(), il n'y a même pas besoin d'ajouter un
deuxième argument pour la taille du tableau :
Exemple :
void afficheTableau(vector<int> tab)
{ for(int i = 0; i < tab.size(); i++)
cout << tab[i] << endl;
}
Si le tableau contient beaucoup d'éléments, le copier prendra du temps. Il vaut donc mieux
utiliser un passage par référence constante const& pour optimiser la copie. Ce qui donne:
void afficheTableau(vector<int> const& tab)
{
// ...
}

37
Dans ce cas, le tableau dynamique ne peut pas être modifié. Pour changer le contenu du tableau,
il faut utiliser un passage par référence tout simple (sans le mot const donc). Ce qui

void

Pour appeler une fonction recevant un vector en argument, il suffit de mettre le nom du tableau
dynamique comme paramètre entre les parenthèses lors de l'appel. Ce qui donne :

vector<int> tab(3,2); //On crée un tableau de 3 entiers valant 2 afficheTableau(tab);


//On passe le tableau à la fonction afficherTableau()

Il est possible d'écrire une fonction renvoyant un vector.


vector<int> premierCarres(int n)
{ vector<int> tab(n);
for(int i = 0; i < tab.size(); i++)
tab[i] = i * i;
return tab;
}
III.2 Tableaux multidimensionnels dynamiques
Notez qu’il est aussi possible de créer des tableaux multi-dimensionnels de taille variable en
utilisant les vectors.
Pour un tableau 2D d'entiers, on devra écrire:
vector < vector<int> > tab; // un vecteur de vecteurs :« tab » qui ne contient aucun élément

Pour dimensionner ce vecteur de vecteurs, il faudra le faire en 2 fois:


tab.resize(3); // 3 vecteurs de vecteurs vide pour l’instant for (int ligne=0;
ligne<tab.size(); ligne++ )
tab [ligne].resize (4); // dimensionner chacun des vecteurs imbriqués

Cela nous permet de créer un vecteur de 3 éléments désignant chacun 1 vecteur de 4


éléments. Finalement, on peut accéder aux valeurs dans les cases de du tableau en utilisant deux
paires de crochets tab[i][j], comme pour les tableaux statiques. Il faut par contre s'assurer que
cette ligne et cette colonne existent réellement.
Exemple :
vector<vector<int> > matrice; matrice.push_back(vector<int>(5)); //On ajoute une ligne de
5 cases à notre matrice matrice.push_back(vector<int>(3,4)); //On ajoute une ligne de 3 cases
contenant chacune 4 matrice[0].push_back(8); //Ajoute une case contenant 8 à la première
ligne de la matrice

38
III.3 Les strings sont des tableaux de caractères (lettres)
En C++, une chaîne de caractères n’est rien d’autre qu’un tableau de caractères, avec un
caractère nul ’\0’ marquant la fin de la chaîne.
On peut par exemple représenter la chaîne « Bonjour » de la manière suivante :

Les chaînes de caractère peuvent être saisies au clavier et affichées à l’écran grâce aux objets
habituels cin et cout.

III.3.1 Déclaration
Pour définir une chaîne de caractères en langage C, il suffit de définir un tableau de caractères.
Le nombre maximum de caractères que comportera la chaîne sera égal au nombre d'éléments
du tableau moins un (réservé au caractère de fin de chaîne).
Exemple :
char nom[20], prenom[20]; // 19 caractères utiles char adresse[3][40]; // trois
lignes de 39 caractères utiles char texte[10] = {‘B’,’o’, ‘n’, ‘j’,’o’, ‘u’,’r’, ‘\0’}; //
ou : char texte[10] = "Bonjour"; On peut utiliser un vector si l'on souhaite
changer la longueur du texte :
vector<char> texte;

En théorie, on pourrait donc se débrouiller en utilisant des tableaux statiques ou dynamiques


de char à chaque fois que l'on veut manipuler du texte. Mais ce serait fastidieux. C'est pour ça
que les concepteurs du langage ont décidé de cacher tout ces mécanismes dans une boîte fermée
(Objet) en utilisant la classe string. Cette dernière, propose en fait une encapsulation de la
chaîne de caractères C. La bibliothèque standard du C++ propose d'autres types de chaînes de
caractères, notamment celles qui sont capables de gérer un encodage comme l'UTF-8 où un
caractère est stocké sur plusieurs octets.

III.3.2 Création d’objets « string »


La création d'un objet ressemble beaucoup à la création d'une variable classique comme int
ou double:
#include <iostream>
#include <string> // Obligatoire pour pouvoir utiliser les objets string

39
III.3.3 Instanciation et initialisation de chaînes
Pour initialiser notre objet au moment de la déclaration (et donc lui donner une valeur !), il y
a plusieurs possibilités:
int main()
{ string maChaine("Bonjour !"); //Création d'un objet 'maChaine' de type string et initialisation
String s3 = "chaine 3";
return 0;
}

III.3.4 Accès à un caractère


On manipule une chaîne C++ comme une chaîne C : on peut utiliser les crochets pour accéder
à un élément même si la chaîne C++ n'est pas un tableau. Nous verrons plus tard comme on
peut faire cela.

// lecture d'un caractère cout <<


s2[3]; // 4eme caractère cout <<
s2.at(3); // 4eme caractère
// modification de caractère
s2[2] = 'A'; s2.at(3) = 'B';

III.3.5 Concaténation de chaînes


Cette opération permet d’assembler deux chaines de caractères:
<iostream>

main (void
s1, s2, s3;

l'espace : cela empêche de saisir une chaîne de caractères comportant un espace.

La fonction getline(iostream &,string) permet de saisir une chaîne de caractères en utilisant


le passage à la ligne comme séparateur : notre chaîne de caractères peut alors comporter des
espaces.

40
On peut utiliser une autre syntaxe de concaténation: s1.append (s2) qui est équivalente à s1
= s1+s2
III.3.6 Quelques méthodes utiles du type « string »

III.3.6.1 Méthode size()


La méthode size() permet de connaître la longueur de la chaîne actuellement stockée dans
l'objet de type string. Exemple :
int main()
{ string maChaine("Bonjour !"); cout << "Longueur de la chaine : " <<
maChaine.size(); return 0; }

III.3.6.2 Méthode « erase() »

Cette méthode très simple supprime tout le contenu de la chaîne :


Exemple :
int main()
{ string chaine("Bonjour !"); chaine.erase(0, 4); // efface
les 4 premiers caractères cout << "La chaine contient : " <<
chaine << endl; chaine.erase();
cout << "La chaine contient : " << chaine << endl;
return 0;
}
Affichage de l’exécution: j La chaine contient :our !
La chaine contient : i
III.3.6.3 Méthode « substr() »
Une autre méthode peut se révéler utile: substr(). Elle permet d'extraire une partie de la chaîne
stockée dans un string.
Syntaxe : string substr( size_type index, size_type num = npos );

Index: permet d'indiquer à partir de quel caractère on doit couper (ce doit être un numéro
de caractère).
Num : permet d'indiquer le nombre de caractères que l'on prend. Par défaut, la valeur
est npos, ce qui revient à prendre tous les caractères qui restent. Si vous indiquez 2, la
méthode ne renverra que 2 caractères.
Exemple:

Affichage de
l’exécution

41
int main()
{ string chaine("Bonjour !"); cout << chaine.substr(3, 4) << endl;
return 0; }

Jour Affichage de l’exécution

On a demandé à prendre 4 caractères en partant du caractère n°3, ce qui fait qu'on a récupéré
« jour ».

IV.3.6.4 Méthode «c_str() »


Cette méthode permet de renvoyer un pointeur vers le tableau de char que contient l'objet de
type string.
Transformation de chaîne de type C en string: on peut utiliser le constructeur string(char
*) ou l'affectation grâce au symbole = d'un char * vers une string.
Transformation d'un string en chaîne de type C : il suffit d'utiliser la méthode : c_str()
qui renvoie un char * qui est une chaîne de type C.
Exemple:
#include <iostream> using
namespace std;
#include<string>

int main (void) {


string s1, s2;
char c1 []=
"BONJOUR";
const char * c2;
s1 = c1;
cout << s1 << endl;
s2 = "AU REVOIR";
c2 = s2.c_str();
cout << c2 << endl;
return 0;
}

BONJOUR
AU REVOIR
Affichage de l’exécution
Dans cet exemple, c1 est un tableau de 8 char contenant la chaîne "BONJOUR " sans oublier
le caractère de fin de chaîne '\0'.
Le pointeur c2 est un pointeur vers un tableau non modifiable de char.
Les variables s1 et s2 sont des string. On peut affecter directement s1=c1 : le tableau de char
sera transformé en string.
Dans c2, on peut récupérer une chaîne « de type C » identique à notre string en écrivant
c2=s2.c_str(). On peut transformer aisément un string en tableau de char et inversement.
IV.3.6.5 Méthode «istr() »

42
Pour transformer une chaîne en double ou en int, il faut transformer la chaîne en flot de
sortie caractères : il s'agit d'un istringstream. Ensuite, nous pourrons lire ce flot de
caractères en utilisant les opérateurs usuels >>.
La méthode eof() sur un istringstream permet de savoir si la fin de la chaîne a été atteinte.
Chaque lecture sur ce flot grâce à l'opérateur >> renvoie un booléen qui nous indique
d'éventuelles erreurs.

<iostream>
<sstream>

main (void

Affichage de l’exécution
Tapez une chaîne : 12345
VOUS AVEZ TAPE L'ENTIER 12345

Dans cet exemple, est une chaîne : on saisit une chaîne au clavier en utilisant getline(cin,s).
On crée ensuite un istringstream appelé istr et construit à partir de s.

On peut lire un entier i à partir de istr en utilisant : istr>>i . (istr>>i) renvoie true si un entier
valide a pu être lu et renvoie false sinon. De la même manière, on pourrait lire des données
d'autres types, double par exemple.

III.3.6.6 Autres opérations : insert, replace, find

s1.insert(3, s2); // insère s2 à la position 3


s3.replace(2,3,s1); // remplace la portion de s3 définie de la position 2 à 5 par la chaîne s1 unsigned
int i = s.find("trouve", 4); // recherche "trouve" à partir de la 4ème position, renvoie
// std::string::npos le cas échéant

43
III.4 Pointeurs et allocation dynamique [9]
Dans un programme, pour garder un lien vers une donnée (une variable), on utilise des
références ou des pointeurs. En programmation, les pointeurs et références servent
essentiellement à trois choses:
1. Permettre à plusieurs portions de code de
partager des objets (données, fonctions,.. )
sans les dupliquer => Référence

2. Pouvoir choisir des éléments non connus a


priori (au moment de la programmation)
=> Généricité

3. Pouvoir manipuler des objets dont la durée


de vie dépasse la portée

=> Allocation dynamique

La durée de vie de la variable a est égale


au temps d’exécution de sa portée.

III.4.1 Différents pointeurs et références [9]


En C++, il existe plusieurs sortes de « pointeurs » (pointeur et références) :
Les références: totalement gérées en interne par le compilateur. Très sûres, donc; mais
sont fondamentalement différentes des vrais pointeurs.

Les « pointeurs intelligents » (smart pointers): gérés par le programmeur, mais avec des
gardes-fous. Il en existe 3: unique_ptr, shared_ptr, weak_ptr (avec #include <memory>)

Les « pointeurs « hérités de C » » (build-in pointers): les plus puissants (peuvent tout
faire) mais les plus « dangereux »
III.4.1.1 Référence
Une référence est un autre nom pour un objet existant, un synonyme, un alias.
La syntaxe de déclaration d’une référence est: <type>& nom_reference(identificateur) ; Après
une telle déclaration, nom_reference peut être utilisé partout où identificateur peut l’être [9].

int val(1) ;
int& x(val) ;

44
C’est exactement ce que l’on utilise lors d’un passage par référence:

Spécificités des références : Contrairement aux pointeurs, une référence :


a. doit absolument être initialisée (vers un objet existant) :
int i; int & rj; // Non, la référence rj doit être liée à un
objet !

b. ne peut être liée qu’à un seul objet :


int i; int
& ri; int
j(2) ;
ri = j ; /* Ne veut pas dire que ri est maintenant un alias de j mais que
i prend la valeur de
j */ j = 3 ;
cout<< i <<endl ; // affiche 2

c. ne peut pas être référencée

rri(ri); // Non

45
On ne peut donc pas faire de tableau de références

IV.4.1.2 Pointeurs [9]


Une variable est physiquement identifiée de façon unique par son adresse, c’est-à-dire
l’adresse de l’emplacement mémoire qui contient sa valeur.
Un pointeur est une variable qui contient l’adresse d’un autre objet informatique (par ex,
variable) => une « variable de variable »

Notes :
Une référence n’est pas un «vrai pointeur» car ce n’est pas une variable en tant que telle.
Une référence est «une autre étiquette».
Un pointeur «une variable contenant une adresse»

La déclaration d’un pointeur se fait selon la syntaxe suivante : type* identificateur ;


Cette instruction déclare une variable de nom identificateur de type pointeur sur une valeur de
type type.
Exemple: déclare une variable ptr qui pointe sur une valeur de type int: int* ptr ;

L’initialisation d’un pointeur se fait selon la syntaxe suivante:


type* identificateur(adresse) ; Exemple:
Int* ptr(NULL);
int * ptr(&i);
int * ptr(new int(33));

nullptr : mot clé C++, spécifie que le pointeur ne pointe sur rien
Pour le compilateur, une variable est un emplacement dans la mémoire, Cet
emplacement est identifié par une adresse
Chaque variable possède une et une seule adresse.
Un pointeur permet d’accéder à une variable par son adresse.
Les pointeurs sont des variables faites pour contenir des adresses.

III.4.1.2.1 Opérateurs sur les pointeurs


C++ possède deux opérateurs particuliers en relation avec les pointeurs : & et *.

& est l’opérateur qui retourne l’adresse mémoire de la valeur d’une variable. Si x est de
type type, &x est de type type* (pointeur sur type).

46
* est l’opérateur qui retourne la valeur pointée par une variable pointeur. Si px est de
type type*, (*px) est la valeur de type type pointée par px.

Note : *&i est donc strictement équivalent à i

Attention aux confusions


int& id(i) - id est une référence sur la variable entière i &id est l’adresse de la variable
id

int i(3); int& id(i); cout<< " i = "<< i


<<" id = "<< id <<endl ; cout<< "
Adresse de i = "<< &i <<endl ;

int* id; déclare une variable id comme un pointeur sur un entier


*id (où id est un pointeur) représente le contenu de l’endroit pointé par id

int*
ptr(NULL); int
i = 2; p = &i ;
cout<< " i = "<< i <<" id = "<< id <<endl ;
cout<< " Le pointeur p pointe sur la valeur "<< *p <<endl
;

III.4.1.2.2 Pointeurs et Tableaux

Par convention, le nom d’une variable utilisé dans une partie droite d’expression donne le
contenu de cette variable dans le cas d’une variable simple. Mais un nom de tableau donne
l’adresse du tableau qui est l’adresse du premier élément du tableau.
Nous faisons les constatations suivantes :
un tableau est une constante d’adressage ;
un pointeur est une variable d’adressage.

47
Ceci nous amène à regarder l’utilisation de pointeurs pour manipuler des tableaux, en prenant
les variables: long i, tab[10], *pti ;
tab est l’adresse du tableau (adresse du premier élément du tableau &tab[0] ;
pti = tab ; initialise le pointeur pti avec l’adresse du début de tableau. Le & ne sert à rien
dans le cas d’un tableau. pti = &tab est inutile et d’ailleurs non reconnu ou ignoré par
certains compilateurs ;
&tab[1] est l’adresse du 2e élément du tableau.
pti = &tab[1] est équivalent à: pti = tab ; où pti pointe sur le 1er élément du tableau.
pti += 1 ; fait avancer, le pointeur d’une case ce qui fait qu’il contient l’adresse du 2ème
élément du tableau.

Nous pouvons déduire de cette arithmétique de pointeur que :


tab[i] est équivalent à *(tab +i). *(pti+i) est équivalent à pti[i].
La figure suivante est un exemple dans lequel sont décrites les différentes façons d’accéder aux
éléments d’un tableau tab et du pointeur pt après la définition suivante:
int tab[6], *pt = tab;

III.4.1.2.3 Tableau de pointeurs


La définition d’un tableau de pointeurs se fait par : type *nom[taille];

Le premier cas d’utilisation d’un tel tableau est celui où les éléments du tableau de pointeurs
contiennent les adresses des éléments du tableau de variables. La figure suivante illustre un
exemple d’utilisation de tableau de pointeurs à partir des définitions de variables suivantes :

int tab[3], *pt[6] = {tab,tab+1,tab+2,tab+3,tab+4,tab+5};

48
Tab[0] /*pt[0]
pt pt[0]/*pt
*tab / **pt
Tab[1]/*pt[1]
pt[1]/*(pt+1)
*(tab+1)/**(pt+1)
Tab[2]/*pt[2]
pt[2]/*(pt+2) *(tab+2)/**(pt+2)

Tab[3]/*pt[1]
pt[3]/*(pt+3)
*(tab+3)/**(pt+3)
Tab[4]/*pt[4]
pt[4]/*(pt+4)
*(tab+4)/**(pt+4)
Tab[5]/*pt[5]
pt[5]/*(pt+5)
*(tab+5)/**(pt+5)

III.4.2 Allocation dynamique


Il y a trois façons d’allouer de la mémoire en C++
1. Allocation statique : La réservation mémoire est déterminée à la compilation. L'espace
alloué statiquement est déjà réservé dans le fichier exécutable du programme lorsque le
système d'exploitation charge le programme en mémoire pour l'exécuter. L'avantage de
l'allocation statique se situe essentiellement au niveau des performances, puisqu'on évite
les coûts de l'allocation dynamique à l'exécution.

2. Allocation dynamique: La mémoire est réservée pendant l’exécution du programme.


L'espace alloué dynamiquement ne se trouve pas dans le fichier exécutable du
programme lorsque le système d'exploitation charge le programme en mémoire pour
l'exécute.
Par exemple, les tableaux de taille variable (vector), les chaînes de caractères de type
string.
Dans le cas particulier des pointeurs, l’allocation dynamique permet également de réserver de
la mémoire indépendamment de toute variable: on pointe directement sur une zone mémoire
plutôt que sur une variable existante.

III.4.2.1 Allocation d’une case mémoire

C++ possède deux opérateurs new et delete permettant d’allouer et de libérer dynamiquement
de la mémoire.

Syntaxe: pointeur = new type


Réserve une zone mémoire de type type et affecte l’adresse dans la variable pointeur.
Exemple :

49
int* p; p =
new int ; *p
=2;
cout<< " p = "<< p <<" *p = "<< *p
<<endl ;

int* p; p = new
int (2);
cout<< " p = "<< p <<" *p = "<< *p
<<endl ;

IV.4.2.2 Libérer la mémoire allouée


Syntaxe : delete pointeur libère la zone mémoire allouée au pointeur

C’est-à-dire que cette zone mémoire peut maintenant être utilisée pour autre chose. On ne peut
plus y accéder !

Bonnes pratiques
Faire suivre tous les delete de l’instruction « pointeur = NULL; ».
Toute zone mémoire allouée par un new doit impérativement être libérée par un delete
correspondant !

Exemple :
int*
px(NULL); px
= new int ; *px
= 20 ;
cout<< "*px = "<<*px
;
delete px ; px
= NULL ;

Notes :
Une variable allouée statiquement est désallouée automatiquement (à la fermeture du
bloc).
Une variable (zone mémoire) allouée dynamiquement doit être désallouée
explicitement par le programmeur.

int* px(NULL);
{px = new int(4) ; int n=6 ;}
cout<< "*px = "<<*px ;
delete px ; px = NULL ;
cout<< "*px = "<<*px
<<endl; cout<< "n = "<< n ;

50
Attention
Si on essaye d’utiliser la valeur pointée par un pointeur pour lequel aucune mémoire n’a été
réservée, une erreur de type

int* px;
*px = 20 ; // ! Erreur : px n’a pas été alloué
cout<< "*px = "<<*px<<endl;
Segmentation fault se produira à
l’exécution.

Compilation : Ok
Exécution: arrêt programme
(Segmentation fault)

Bonnes pratiques
int* px(NULL); if(px != NULL)
{ *px = 20 ;
cout<< "*px = "<<*px<<endl;
}

Initialisez toujours vos pointeurs


Utilisez NULL si vous ne connaissez pas encore la mémoire pointée au moment de
l’initialisation

III.4.2.3 Allocation dynamique de tableau unidimensionnel


Syntaxe: new nom_type [n]
Permet d'allouer dynamiquement un espace mémoire nécessaire pour un tableau de n
éléments de type nom_type.
Syntaxe : delete [] adresse
Permet de libérer l'emplacement mémoire désigné par adresse alloué préalablement par new[]
III.4.2.4 Allocation dynamique de tableau bidimensionnel
Syntaxe :
type **data;
data = new type*[ligne]; // Construction des lignes for (int j = 0; j < ligne; j++)
data[j] = new type[colonne]; // Construction des colonnes

Permet d'allouer dynamiquement un espace mémoire nécessaire pour un tableau de ligne ×


colonne éléments de type type.

51
Syntaxe :
for (int i = 0; i < ligm; i++)
delete[lignes] data[i]; // Suppression des colonnes delete[] data; //
Suppression des lignes

Permet de libérer l'emplacement mémoire désigné par data alloué préalablement par new[]

Exemple 1:
#include <iostream>
#include <ctime>
Using namespace std;

const int MAX =50;


Const int MIN =25;

void display(double **data)


{ for (int i = 0; i < m; i++)
{ for (int j = 0; j < n; j++)
cout << data[i][j] << " ";
cout << "\n" << endl;
}
}

void de_allocate(double **data)


{ for (int i = 0; i < m; i++)
delete[] data[i];
delete[] data; }

int main(void) { double **data;


int m,n; //m: lignes , n:colonnes
srand(time(NULL));

cout<<"Colonnes : n = " ;
cin>>n; cout<< " Lignes : m = " ;
cin>>m;

52
data = new double*[m];
for (int j = 0; j < m; j++)
data[j] = new double[n];

for (int i = 0; i < m; i++) for (int j = 0; j < n;


j++) data[i][j] = (rand() % (MAX - MIN +
1)) + MIN;

display(data);
de_allocate(data); return
0;
}

Exemple 2:
#include <iostream>
Using namespace std;

struct Etudiant { string


nom ; double moyenne ;
char sexe ; // ’F’ ou ‘M’
};
void initialiserEtudiant(Etudiant *e) ;
void afficherEtudiant(Etudiant *e) ;

int main(void)
{ Etudiant *ptrEtudiant ;

ptrEtudiant = new Etudiant;

initialiserEtudiant(ptrEtudiant) ;
afficherEtudiant(ptrEtudiant) ;

delete (ptrEtudiant) ;
return 0;
}
void initialiserEtudiant(Etudiant *e)
{ cout<< " Saisir le nom : " ; cin>>(*e).nom ;
cout<< " Saisir la moyenne : " ; cin>>(*e).moyenne ;
cout<< " Saisir le sexe : " ; cin>>(*e).sexe ;
}
void afficherEtudiant(Etudiant *e)
{ if((*e).sexe== 'F') cout<< " Madame " ; else cout<< " Monsieur "
; cout<< (*e).nom <<" Votre moyenne est de "<<
(*e).moyenne<<endl ;

53
}

54

Vous aimerez peut-être aussi