0% ont trouvé ce document utile (0 vote)
24 vues73 pages

CH 3

Le chapitre 3 aborde les tableaux, pointeurs et chaînes de caractères en C++, en se concentrant sur les tableaux unidimensionnels statiques et dynamiques. Il explique la déclaration, l'initialisation, la manipulation des éléments, ainsi que l'utilisation de la classe vector pour les tableaux dynamiques. Les concepts incluent également les fonctions associées aux tableaux, la modification de taille, et l'utilisation d'itérateurs.

Transféré par

Eliza Beth
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)
24 vues73 pages

CH 3

Le chapitre 3 aborde les tableaux, pointeurs et chaînes de caractères en C++, en se concentrant sur les tableaux unidimensionnels statiques et dynamiques. Il explique la déclaration, l'initialisation, la manipulation des éléments, ainsi que l'utilisation de la classe vector pour les tableaux dynamiques. Les concepts incluent également les fonctions associées aux tableaux, la modification de taille, et l'utilisation d'itérateurs.

Transféré par

Eliza Beth
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

CHAPITRE 3 :

TABLEAUX, POINTEURS ET
CHAINES DE CARACTÈRES
EN C++

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.

2
Tableaux unidimensionnels statiques
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.
3
Tableaux unidimensionnels statiques
Déclaration et initialisation
❑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};

4
Tableaux unidimensionnels statiques
Déclaration et initialisation
❑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] = {};
5
Tableaux unidimensionnels statiques
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.

6
Tableaux unidimensionnels statiques
Manipulation des éléments
#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;}
7
Tableaux unidimensionnels statiques
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 :
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.
8
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.

9
Tableaux unidimensionnels dynamiques (classe vector)
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;
10
Tableaux unidimensionnels dynamiques (classe vector)
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.
❑Exemple:
#include <iostream>
#include <vector>
using namespace std;
int main() {
int const TailleTab(5);
vector<int> Scores(TailleTab); //Déclaration du tableau
//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;} 11
Tableaux unidimensionnels dynamiques (classe vector)
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.
❑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

12
Tableaux unidimensionnels dynamiques (classe vector)
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(); // Il y a plus que 2 éléments dans le tableau

13
Tableaux unidimensionnels dynamiques (classe vector)
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)
at()
ex : for(int i=0; i<vi.size(); i++)
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;
14
Tableaux unidimensionnels dynamiques (classe vector)
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.assign(tab, tab + 3); // v2 remplacé par les éléments du tableau tab
// 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
15
Tableaux unidimensionnels dynamiques (classe vector)

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


ajoute un élément x à la position désignée par l'itérateur p et décale le
emplace(p,x) reste. Les arguments passés pour x correspondent à des arguments pour
le constructeur de x
ex : p=v.begin()+2; // ajoute 100 à la position 2
v.emplace(p, 100);
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

16
Tableaux unidimensionnels dynamiques (classe vector)

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


supprime l'élément pointé par l'itérateur p
ex:
//supprime les éléments compris entre les itérateurs premier et dernier
erase(p) vector<float>::iterator prem = v.begin()+1;
vector<float>::iterator dern = v.end()-1;
v.erase(prem,dern);
affiche_vector(v);
efface tous les éléments d'un conteneur. Équivalent à c.erase(c.begin(), c.end())
clear()
ex: v.clear(); // efface tout le conteneur

17
Tableaux unidimensionnels dynamiques (classe vector)
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
resize(nb)
ex: vector<int> v(5);
resize(nb, val) for(auto i(0); i<v.size(); i++)
v[i]=i;
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 18
Tableaux unidimensionnels dynamiques (classe vector)
Taille et capacité
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);
capacity()
cout<<"nombre elements : "<<v.size()<<endl;//10
cout<<"capacite : "<<v.capacity()<<endl; // 10
v.resize(12);
cout<<"nombre elements : "<<v.size()<<endl; //12
cout<<"capacite : "<<v.capacity()<<endl; // 15

19
Tableaux unidimensionnels dynamiques (classe vector)
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 :
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
retourne un itérateur « reverse_iterator » qui pointe sur le premier élément de
rbegin()
la séquence inverse (le dernier) ;
retourne un itérateur « reverse_iterator » qui pointe sur l'élément suivant le
rend()
dernier dans l'ordre inverse (balise de fin, par exemple NULL)
retourne un itérateur « const_iterator » qui pointe sur le premier élément.
cbegin() L'élément pointé n'est alors accessible qu'en lecture et non en écriture. Il ne
peut pas être modifié

20
Tableaux unidimensionnels dynamiques (classe vector)
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
cend()
pointé n'est alors accessible qu'en lecture et non en écriture. Il ne
peut pas être modifié
retourne un itérateur « const_reverse_iterator » qui pointe sur le
crbegin() 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é
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
crend()
sens inverse, par exemple NULL). L'élément pointé n'est accessible
qu'en lecture et ne peut pas être modifié
21
Tableaux unidimensionnels dynamiques (classe vector)

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;

22
Tableaux unidimensionnels dynamiques (classe vector)
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){
...
} 23
Tableaux unidimensionnels dynamiques (classe vector)
Les vector et les fonctions
❑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 donne :
void afficheTableau(vector<int>& tab)
{…}
❑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()
24
Tableaux unidimensionnels dynamiques (classe vector)
Les vector et les fonctions
❑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;
}

25
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;
❑ 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

26
Tableaux multidimensionnels dynamiques
❑ 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 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 27
Chaines de caractères

❑ En C++, une chaîne de caractères n’est rien d’autre qu’un tableau de


caractères, 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 :
b o n j o u r \0

❑ Les chaînes de caractère peuvent être saisies au clavier et affichées à l’écran


grâce aux objets habituels cin et cout.

28
Chaines de caractères
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é
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; 29
Chaines de caractères
Déclaration
❑ 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.

30
Chaines de caractères
Création d’objets « string»
❑ La création objet ressemble beaucoup à la création d'une variable classique
comme int ou double:
❑ Example
#include <iostream>
#include <string> // Obligatoire pour pouvoir utiliser les objets string
using namespace std;
int main() {
string maChaine; //Création d'un objet 'maChaine de type string
return 0;
}

31
Chaines de caractères
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:
❑ Example
int main() {
//Création d'un objet 'maChaine' de type string et initialisation
string maChaine("Bonjour !");
string s3 = "chaine 3";
return 0;
}

32
Chaines de caractères
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';

33
Chaines de caractères
Concaténation de chaînes
Cette opération permet d’assembler deux chaines de caractères:
#include <iostream>
#include<string>
using namespace std;
int main (void){
string s1, s2, s3;
cout << "Tapez une chaine : "; getline (cin, s1);
cout << "Tapez une chaine : "; getline (cin, s2);
s3 = s1 + s2;
cout << "Voici la concatenation des 2 chaines :" << endl;
cout << s3 << endl;
return 0;} 34
Chaines de caractères
Concaténation de chaînes
❑ Par défaut, lorsqu'on saisit une chaîne de caractères en utilisant cin, le
séparateur est 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.
❑ On peut utiliser une autre syntaxe de concaténation: s1.append (s2) qui est
équivalente à s1 = s1+s2

35
Chaines de caractères
Quelques méthodes utiles du type « string »
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 :
#include <iostream>
#include<string>
using namespace std;
int main(){
string maChaine("Bonjour !");
cout << "Longueur de la chaine : " << maChaine.size();
return 0;
} 36
Chaines de caractères
Quelques méthodes utiles du type « string »
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;
}
37
Chaines de caractères
Quelques méthodes utiles du type « string »
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.

38
Chaines de caractères
Quelques méthodes utiles du type « string »
Méthode « substr() »
❑ Exemple:
#include <iostream>
#include<string>
using namespace std;
int main(){
string chaine("Bonjour !");
cout << chaine.substr(3) << endl;
return 0;
}

39
Chaines de caractères
Quelques méthodes utiles du type « string »
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.

40
Chaines de caractères
Quelques méthodes utiles du type « string »
Méthode «c_str() »
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;
} 41
Chaines de caractères
Quelques méthodes utiles du type « string »
Méthode «c_str() »
❑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.
42
Chaines de caractères
Quelques méthodes utiles du type « string »
Méthode «istr() »
❑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.

43
Chaines de caractères
Quelques méthodes utiles du type « string »
Méthode «istr() »
Exemple :
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int main (void){
string s;
cout << "Tapez une chaine : "; getline (cin, s);
istringstream istr(s);
int i;
if (istr >> i) cout << "VOUS AVEZ TAPE L'ENTIER " << i << endl;
else cout << "VALEUR INCORRECTE" << endl;
return 0;
} 44
Chaines de caractères
Quelques méthodes utiles du type « string »
Méthode «istr() »
❑ Dans cet exemple, s 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.

45
Chaines de caractères
Quelques méthodes utiles du type « string »
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

46
Pointeurs et allocation dynamique
❑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.

47
Pointeurs et allocation dynamique
Différents pointeurs et références
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 »

48
Pointeurs et allocation dynamique
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.

❑ C’est exactement ce que l’on utilise lors d’un passage par référence:
49
Pointeurs et allocation dynamique
Référence
Exemple 1 :
#include <iostream>
using namespace std;
void f(int & a )
{ ++a ; }
int main (void) {
int b=1;
f(b) ;
cout << " APRES b = "<<b;
return 0;
}
50
Pointeurs et allocation dynamique
Référence
Exemple 2 : Sémantique de const
#include <iostream>
using namespace std;
int main(){
int i(3);
const int &j(i); // i et j sont les mêmes. On ne peut pas changer la valeur via j
// j=12 ; // Erreur de compilation
i = 12 ; // oui, et j aussi vaut 12
cout << " j = "<<j;
return 0;
}
51
Pointeurs et allocation dynamique
Référence
Spécificités des références Spécificités des références : Contrairement aux
pointeurs, une référence :
❑ 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 !
❑ Ne peut être liée qu’à un seul objet :
int i;
int & r(i);
int j(2) ;
r = 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 52
Pointeurs et allocation dynamique
Référence
Spécificités des références Spécificités des références : Contrairement aux
pointeurs, une référence :
❑ Ne peut pas être référencée
int i(3);
int & ri(i);
int & rri(ri); // Non
int && rri(ri); // Non plus // Non plus !

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

53
Pointeurs et allocation dynamique
Pointeurs
❑ 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 »

54
Pointeurs et allocation dynamique
Pointeurs
❑ 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) ;
55
Pointeurs et allocation dynamique
Pointeurs
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.

56
Pointeurs et allocation dynamique
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).

int x(3);
int * px(NULL);
px = &x;
cout<< "Le pointeur px contient l’adresse " <<px<<endl ;
❑ * 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.
57
Pointeurs et allocation dynamique
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).
int x(3);
int * px(NULL);
px = &x;
cout<< "Le pointeur px contient l’adresse " <<px<<endl ;
❑ * 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.
58
Pointeurs et allocation dynamique
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).
❑ * 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.
int x(3);
int * px(NULL);
px = &x;
cout<< "Le pointeur px contient l’adresse "<<px<<endl ;
cout<< "Le pointeur px contient l’adresse " <<*px<<endl ;
❑ Note : *&i est donc strictement équivalent à i. 59
Pointeurs et allocation dynamique
Opérateurs sur les pointeurs
❑ int& id(i) : id est une référence sur la variable entière i
❑ &id est l’adresse de la variable id
❑ 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 i(3);
int& id(i);
cout<< " i = "<< i <<" id = "<< id <<endl ;
cout<< " Adresse de i = "<< &i S<<endl ;
int* p(NULL);
int i = 2;
p = &i ;
cout<< " i = "<< i <<" p = "<< p <<endl ;
cout<< " Le pointeur p pointe sur la valeur "<< * p <<endl ; 60
Pointeurs et allocation dynamique
Opérateurs sur les pointeurs
❑ int& id(i) : id est une référence sur la variable entière i
❑ &id est l’adresse de la variable id
❑ 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 i(3);
int& id(i);
cout<< " i = "<< i <<" id = "<< id <<endl ;
cout<< " Adresse de i = "<< &i S<<endl ;
int* p(NULL);
int i = 2;
p = &i ;
cout<< " i = "<< i <<" p = "<< p <<endl ;
cout<< " Le pointeur p pointe sur la valeur "<< * p <<endl ; 61
Pointeurs et allocation dynamique
Pointeurs et Tableaux
❑ Le 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
❑ 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 2éme élément du tableau. 62
Pointeurs et allocation dynamique
Pointeurs et Tableaux
❖ pti = &tab[0] 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].

63
Pointeurs et allocation dynamique
Allocation dynamique
❑ 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.
❑ Allocation dynamique: 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écuté. Par exemple, les tableaux de taille variable (vector), les chaînes
de caractères de type), les chaînes de caractères de type string.
❑ Dans le cas particulier des pointeurs particulier, 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.
64
Pointeurs et allocation dynamique
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 moire.
❑ Syntaxe: pointeur = new type
❖ Réserve une zone mémoire de type type et affecte l’adresse dans la
variable pointeur.
❑ Exemple :
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 ; 65
Pointeurs et allocation dynamique
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 !
❑ 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 !

66
Pointeurs et allocation dynamique
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 !
❑ Faire suivre tous les delete par 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 ; 67
Pointeurs et allocation dynamique
Libérer la mémoire allouée
❑ 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.
❑ 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 Segmentation fault se produira à l’exécution.
❑ 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 68
Pointeurs et allocation dynamique
Libérer la mémoire allouée
❑ 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.
❑ 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 Segmentation fault se produira à l’exécution.
❑ 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 69
Pointeurs et allocation dynamique
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[]

70
Pointeurs et allocation dynamique
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.
❑ 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[]
71
Pointeurs et allocation dynamique
#include <iostream>
#include <ctime>
Using namespace std;
void display(double **data, int n, int m) {
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, int m) {
for (int i = 0; i < m; i++)
delete[] data[i];
delete[] data;
} 72
Pointeurs et allocation dynamique
int main(void){
double **data;
int m,n; //m: lignes , n:colonnes
cout<<"Colonnes : n = " ; cin>>n;
cout<< " Lignes : m = " ; cin>>m;
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] =i+j;
display(data,m,n);
de_allocate(data ,m);
return 0;
} 73

Vous aimerez peut-être aussi