Académique Documents
Professionnel Documents
Culture Documents
1ère année
Pointeurs et Allocation
Dynamique
Yacine BELLIK
IUT d’Orsay
Université Paris XI
Plan du cours
Pointeurs
Définition, Dé
Déclaration
Opé
Opérateur &, Opé
Opérateur *
Pointeurs void
Pointeurs d’
d’objets
Opé
Opérateur sizeof
Arithmé
Arithmétique des pointeurs
Tableaux et pointeurs
Affichage de tableaux et de pointeurs
Passage de paramè
paramètres en C
Allocation Dynamique
Structure de la mé mémoire
Inté
Intérêt de l’l’allocation dynamique
Opé
Opérateurs new et new [ ]
Opé
Opérateurs delete et delete [ ]
Gestion des erreurs d’ d’allocation
Tableaux dynamiques à 2 dimensions
Objets ayant une partie dynamique
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 2
1
Pointeurs
Définition
Un pointeur p est une variable qui sert à contenir l'adresse mé
mémoire d'une
autre variable(1) v
– Exemple
2
Déclaration de pointeurs
Lorsqu'on dé
déclare un pointeur on indique le type des
variables sur lesquelles il aura à pointer
Trace d'exécution :
Valeur de i = 5
Adresse de i = 0x0012FF78
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 6
3
Affectation d'une adresse à un pointeur
Adresses
0x05 0x0012FF78
int i=5;
0x00 0x0012FF79
int * pi; i 0x0012FF7A
0x00
pi=&i; 0x00 0x0012FF7B
cout << "Valeur de i = " << i << endl;
cout << "Adresse de i = " << &i << endl;
cout << "Valeur de pi = " << pi << endl;
Mémoire
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 7
Incompatibilité de types
int i=5;
char * pc;
pc=&i; // !!! ERREUR !!!
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 8
4
Opérateur d'indirection *
Cet opé
opérateur permet d'accé
d'accéder à la valeur de la variable
pointé
pointée en utilisant le pointeur
Si p contient l'adresse de la variable v alors *p désigne
la variable v elle-
elle-même
int i=5;
int * pi=&i;
cout << "Valeur de i = " << i << endl;
cout << "Adresse de i = " << &i << endl;
cout << "Valeur de pi = " << pi << endl;
cout << "Valeur de i = " << *pi << endl;
Trace d'exécution :
Valeur de i = 5
Adresse de i = 0x0012FF78
Valeur de pi = 0x0012FF78
Valeur de i = 5
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 9
Opérateur d'indirection *
Trace d'exécution :
Valeur de i = 5
Valeur de i = 9
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 10
5
Exercice
int v1 = 5, v2 = 15;
int *p1, *p2;
p1 = &v1; // p1 = adresse de v1
p2 = &v2; // p2 = adresse de v2
*p1 = 10; // variable pointée par p1 = 10
*p2 = *p1; // variable pointée par p2 = variable pointée par p1
p1 = p2; // p1 = p2
*p1 = 20; // variable pointée par p1 = 20
Devinette
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 12
6
Pointeurs void
pv=pi; // Correct
pi=pv; // !!! Erreur !!!
pi= (int *) pv; // Correct
cout<< *pv; // !!! Erreur !!!
cout<< *((int*)pv); // Correct
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 13
Pointeurs d'objets
Point p(10,20);
Point * pp=&p;
// on peut écrire
cout << "x=" << p.getAbs() << " y=" << p.getOrd() << endl;
// ou encore (parenthèses obligatoires)
cout << "x=" << (*pp).getAbs() << " y=" << (*pp).getOrd() << endl;
// ou encore
cout << "x=" << pp->getAbs() << " y=" << pp->getOrd() << endl;
7
Adresse de l'objet cible
Opérateur sizeof
Cet opé
opérateur permet de connaî
connaître la taille en octets
d’une constante, d’
d’une variable ou d’
d’un type
cout<<“Taille de MAX=“<<sizeof(MAX)<<endl;
cout<<“Taille de c=“<<sizeof(c)<<“ Taille de char=“<<sizeof(char)<<endl;
cout<<“Taille de i=“<<sizeof(i)<<“ Taille de int=“<<sizeof(int)<<endl;
cout<<“Taille de f=“<<sizeof(f)<<“ Taille de float=“<<sizeof(float)<<endl;
cout<<“Taille de pc=“<<sizeof(pc)<<“ Taille de pointeur=“<<sizeof(char*)<<endl;
cout<<“Taille de t1=“<<sizeof(t1)<<endl;
cout<<“Taille de t2=“<<sizeof(t2)<<endl;
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 16
8
Opérateur sizeof
Trace d’
d’exé
exécution
Taille de MAX=4
Taille de c=1 Taille de char=1
Taille de i=4 Taille de int=4
Taille de f=4 Taille de float=4
Taille de pc=4 Taille de pointeur=4
Taille de t1=8
Taille de t2=400
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 17
Le même principe s’
s’applique pour p--
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 18
9
Addition / Soustraction d’un entier
Soit p un pointeur de type T
Soit t la taille en octets du type T
Soit a la valeur de p (adresse contenue dans p)
Soit n un entier
Si on exé
exécute p+n alors la nouvelle valeur de p sera égale à a+n*t
int i=5;
int * pi=&i;
cout << "Valeur de pi avant l’addition = " << pi << endl;
pi=pi+2;
cout << "Valeur de pi après l’addition = " << pi << endl;
Trace d'exécution :
Valeur de pi avant l’addition = 0x0012FF78
Valeur de pi après l’addition = 0x0012FF80
Le même principe s’
s’applique pour la soustraction d’
d’un entier
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 19
Exemple récapitulatif
42 46 47 48 49 50 51 52 53 54 58
pi-- pi++
pi-2 pi+2
pi
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 20
10
Tableaux et pointeurs
Les identificateurs de tableaux et de pointeurs sont trè
très similaires
L’identificateur d’
d’un pointeur dé
désigne l’l’adresse de la premiè
première case
mémoire de la variable sur laquelle pointe le pointeur
De même, l’l’identificateur d’
d’un tableau dé
désigne l’l’adresse de la
premiè
première case mé
mémoire du tableau
Tableaux et pointeurs
const int MAX=7;
int tab[MAX]; int * p;
p=tab; *p=0; // tab[0]=0
p++; *p=10; // tab[1]=10
p=&tab[2]; *p=20; // tab[2]=20
p=tab+3; *p=30; // tab[3]=30
p=tab; *(p+4)=40; // tab[4]=40
p[5]=50; // tab[5]=50
*(tab+6)=60; // tab[6]=60;
for (int n=0; n<MAX; n++) cout << tab[n] << " ";
Trace d'exécution :
0 10 20 30 40 50 60
11
Affichage de tableaux et de pointeurs
L’affichage d’
d’un tableau ou d’
d’un pointeur produit des ré
résultats diffé
différents
selon qu’
qu’il s’
s’agisse d’
d’un tableau ou pointeur de caractè
caractères ou pas.
Dans le cas d’
d’un tableau ou pointeur de caractè
caractères, l’l’affichage produit la
chaî
chaîne de caractè
caractères commenç
commençant à la premiè
première case du tableau ou à la
case d’
d’adresse indiqué
indiquée par le pointeur et finissant à la premiè
première case
qui contient le caractè
caractère \0 (code ASCII=0).
Dans les autres cas (tableau ou pointeur d’
d’un autre type), l’l’affichage
produira l’l’adresse de la premiè
première case du tableau ou la valeur du
pointeur (adresse contenue dans le pointeur).
Pour obtenir l’l’affichage de la valeur d’
d’un pointeur de caractè
caractères (c’
(c’est-
est-à-
dire l’l’adresse qu’
qu’il contient) il faut le convertir en pointeur void avant.
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 23
p=t;
cout << “ t=“ << t << “ p=“ << p << endl;
cout << “&t=“ << &t << “ &p=“ << &p << endl;
cout << “*t=“ << *t << “ *p=“ << *p << endl;
Trace d’exécution
t=0x0012FF54 p=0x0012FF54
&t=0x0012FF54 &p=0x0012FF50
*t=1 *p=1
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 24
12
Affichage de tableaux ou de pointeurs de
caractères
const int MAX=10;
char * p;
char t[MAX]=“ABCDEF”;
p=t;
cout << “ t=“ << t << “ p=“ << p << endl;
cout << “&t=“ << &t << “ &p=“ << &p << endl;
cout << “*t=“ << *t << “ *p=“ << *p << endl;
cout << “(void*) p=“ << (void*) p <<endl;
Trace d’exécution
t=ABCDEF p=ABCDEF
&t=0x0012FF54 &p=0x0012FF50
*t=A *p=A
(void*) p=0x0012FF54
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 25
Passage de paramètres en C
Passage de paramè
paramètres en C++
Par valeur
Par ré
référence
Passage de paramè
paramètres en C
Par valeur uniquement
Problè
Problème
Comment passer en C un paramè
paramètre en donné
donnée/ré
e/résultat ?
Solution
Passer son adresse en paramè
paramètre
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 26
13
Exemple 1
Procé
Procédure permuter : permute 2 variables
// C++ // C
void permuter (int & x, int & y) void permuter (int * px, int * py)
{ {
int temp=x; int temp=*px;
x=y; *px=*py;
y=temp; *py=temp;
} }
// Appel // Appel
permuter (a,b); permuter (&a,&b);
Adresse de X
Copie de adresse de X
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 27
Exemple 2
Procé
Procédure minmax : renvoie le min et le max d’
d’un tableau d’
d’entiers
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 28
14
Allocation dynamique
Structure de la mémoire
La mé
mémoire comporte 3 zones d’
d’allocation diffé
différentes
Chaque zone sert à mémoriser 3 types diffé
différents de variables
Pile (Stack)
Zones
d’allocation
Tas (Heap)
mémoire
Zone statique
Code exécutable
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 30
15
La pile
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 31
La zone statique
La zone d’
d’allocation statique contient les variables qui
sont dé
déclaré
clarées en dehors de toute fonction ou à
l’inté
intérieur d’
d’une fonction mais avec le qualificatif static
Ces variables sont appelé
appelées variables globales ou
statiques
Elles sont créé
créés
s à l’exé
exécution du programme et dé
détruites
à la fin de celui-
celui-ci
Leurs duré
durées de vie est donc la duré
durée de vie du
programme
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 32
16
Le tas
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 33
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 34
17
Intérêt de l’allocation dynamique
L’allocation dynamique sert les autres programmes mais elle peut
également servir le programme même qui l’l’utilise.
Supposons qu’ qu’un programme ait besoin d’ d’afficher 100 objets
graphiques diffé
différents et que ces objets sont trè
très volumineux (en
espace mémémoire).
Supposons également qu’ qu’un instant donné
donnée, le programme n’ n’a
besoin d’
d’afficher simultané
simultanément et au maximum que 10 objets parmi
les 100.
Supposons que la taille de la mé mémoire permet au maximum le
stockage simultané
simultan é de 30 objets graphiques.
Allouer les 100 objets de façfaçon statique est impossible puisque
l’espace mé
mémoire est insuffisant.
En revanche, dé déclarer dynamiquement 10 objets est tout à fait
possible et le programme pourra alors s’ s’exé
exécuter sans problè
problèmes.
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 35
Opérateur new
L’opé
opérateur new permet l’l’allocation dynamique de l’l’espace mé mémoire
nécessaire pour stocker un élément d’ d’un type T donné
donné
Pour que l’l’opé
opérateur new puisse connaî
connaître la taille de l’l’espace à allouer il
faut donc lui indiquer le type T
Si l’l’allocation ré
réussit (il y a suffisamment d’d’espace en mé mémoire) alors
l’opé
opérateur new retourne l’l’adresse de l’l’espace alloué
alloué
Cette adresse doit être alors affecté
affectée à un pointeur de type T pour pouvoir
utiliser cet espace par la suite
Si l’l’allocation échoue (il n’n’y a pas suffisamment d’ d’espace en mé mémoire) alors
l’opé
opérateur new retourne NULL (0)
T * p; ou T * p = new T;
p = new T;
Il est possible d’
d’indiquer aprè
après le type T une valeur pour initialiser l’espace
ainsi alloué
alloué. Cette valeur doit être indiqué
indiquée entre parenthè
parenthèses : new T (val)
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 36
18
Opérateur new : exemple
Allocation d’
d’un entier
Allocation d’
d’un objet
int * p1 = new Point; //Allocation d’un objet Point et appel du constructeur sans paramètres
(ou avec les valeurs par défaut)
int * p2 = new Point(30,40); //Allocation et appel du constructeur avec paramètres
Opérateur new [ ]
Allocation dynamique d’un tableau 1D
Pour allouer dynamiquement un tableau d’é
d’élléments il suffit d’
d’utiliser l’l’opé
opérateur
new [ ] en indiquant entre les crochets le nombre d’é
d’élléments dé
désiré
siré
Contrairement aux tableaux statiques où
où le nombre d’é
d’élléments devait être une
constante (valeur connue à la compilation) ici le nombre d’é
d’élléments peut être
une variable dont la valeur ne sera connue qu’à
qu’à l’exé
exécution (valeur saisie par
l’utilisateur, lue à partir d’
d’un fichier, calculé
calculée,…
e,…)
On ré
récupè
cupère alors l’l’adresse du premier élément du tableau
Il n’
n’est pas possible ici d’
d’indiquer des valeurs d’
d’initialisation
Dans le cas d’
d’un tableau d’
d’objets il faut obligatoirement pré
prévoir un
constructeur sans paramè
paramètres ou avec des valeurs pas dé défaut
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 38
19
Opérateurs delete et delete [ ]
Pour que l’l’allocation dynamique soit utile et efficace, il est impé
impératif de libé
libérer
l’espace ré
réservé
servé avec new dè
dès que le programme n’ n’en a plus besoin.
Cet espace pourra alors être réréutilisé
utilisé soit par le programme lui-lui-même soit
par d’
d’autres programmes.
Libé
Libération d’d’un élément simple : opé
opérateur delete
int * p = new int;
*p=5;
cout<<*p<<endl;
delete p;
Libé
Libération d’
d’un tableau : opé
opérateur delete [ ]
int * p, i, n;
cout<<“Nombre d’éléments ? : “; cin>>n;
p=new int[n];
for (i=0;i<n;i++) cin>>p[i];
for (i=0;i<n;i++) cout<<p[i]*p[i];
delete [] p;
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 39
On peut gé
gérer gérer les erreurs d’
d’allocation dynamique de la
mémoire de 3 faç
façons diffé
différentes
Utilisation de set_new_handler
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 40
20
Test de la valeur retournée par new
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 41
set_new_handler
On indique une fonction (dans l'exemple erreurMemoire)
erreurMemoire) qui sera
appelé
appelée automatiquement si new échoue
Inclure <new>
100 Mégas octets alloués
void erreurMemoire() { 200 Mégas octets alloués
cerr<<"Plus de mémoire"<<endl; 300 Mégas octets alloués
exit(1); 400 Mégas octets alloués
} 500 Mégas octets alloués
int main() { 600 Mégas octets alloués
const int CENTMEGAS=100*1024*1024; 700 Mégas octets alloués
char * p; int i=0; 800 Mégas octets alloués
900 Mégas octets alloués
set_new_handler(&erreurMemoire); 1000 Mégas octets alloués
while(1){ 1100 Mégas octets alloués
p=new char[CENTMEGAS]; 1200 Mégas octets alloués
cout<<++i<<"00 Mégas octets alloués"<<endl; 1300 Mégas octets alloués
} 1400 Mégas octets alloués
return 0; 1500 Mégas octets alloués
} Plus de mémoire
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 42
21
Exception standard bad_alloc
L’exception standard bad_alloc est levé
levée automatiquement si new échoue
Inclure <stdexcept
<stdexcept>
>
100 Mégas octets alloués
const int CENTMEGAS=100*1024*1024; 200 Mégas octets alloués
char * p; int i=0; 300 Mégas octets alloués
400 Mégas octets alloués
try 500 Mégas octets alloués
{ 600 Mégas octets alloués
while(1) 700 Mégas octets alloués
{ 800 Mégas octets alloués
p=new char[CENTMEGAS]; 900 Mégas octets alloués
cout<<++i<<"00 Mégas octets alloués"<<endl; 1000 Mégas octets alloués
} 1100 Mégas octets alloués
} 1200 Mégas octets alloués
catch(bad_alloc & e) 1300 Mégas octets alloués
{ 1400 Mégas octets alloués
cerr<<"Plus de mémoire"<<endl; 1500 Mégas octets alloués
cerr<<e.what()<<endl; Plus de mémoire
terminate(); St9bad_alloc
} Aborted (core dumped)
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 43
22
Exemple 1 : tableau de chaînes
tabMots[0][0]
tabMots[0][1]
tabMots
M i e l \0
Char** tabMots[0]
tabMots[1] A l b u m \0
L u m i è r e s \0
Char*
J a r d i n \0
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 45
int i, nbMots;
char mot[LONGMAX];
char ** tabMots;
for (i=0;i<nbMots;i++) {
cout<<"Mot "<<i+1<<" ? : ";
cin.getline(mot,LONGMAX); //Saisie du ième mot dans une variable temporaire
tabMots[i]=new char[strlen(mot)+1]; //Allocation de l’espace nécessaire au ième mot
strcpy(tabMots[i],mot); //Copie du ième mot saisi dans l’espace alloué
}
for (i=0;i<nbMots;i++)cout<<tabMots[i]<<endl;
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 46
23
Exemple 2 : tableau de pixels (image)
image[1][4]
image[0][4]
image[1][0]
image[0][0]
image[0]
image[1]
int*
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 47
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 48
24
Objets ayant une partie dynamique
On entend par objets ayant une partie dynamique, des objets dont le
constructeur ré
réalise une allocation dynamique
Un certain nombre de dispositions doivent être prises avec de tels
tels
objets
Ecriture d’un destructeur
Redé
Redéfinition du constructeur par recopie
Redé
Redéfinition de l’l’opé
opérateur d’
d’affectation
Ecriture d’un destructeur
– Le destructeur doit libé
libérer l’l’espace alloué
alloué par le constructeur
Redé
Redéfinition du constructeur par recopie et de l’l’opé
opérateur
d’affectation
– Pour éviter dans certains cas le problè
problème de la recopie superficielle
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 49
Recopie superficielle
Objet 1
int * tab 8 2 3 6 2 5
Recopie
par
Objet 2
défaut
int * tab
25
Recopie profonde
Objet 1
int * tab 8 2 3 6 2 5
Nouveau
constructeur
Objet 2
par recopie
int * tab 8 2 3 6 2 5
Opérateur d’affectation
Le nouvel opé
opérateur d’
d’affectation doit :
– Faire le test de l’l’auto-
auto-affectation (A=A) et si ce test est vrai se
contenter simplement de retourner l’l’objet cible (par ré référence)
– Si le test de l’l’auto-
auto-affectation est faux (les 2 objets sont
diffé
diffé rents) alors il doit :
Libé
Libérer le tableau dynamique alloué
alloué par l’l’objet cible
Allouer un nouveau tableau à l’objet cible
Recopier les valeurs du tableau de l’l’objet de droite dans le nouveau
tableau de l’l’objet cible
– Le test de l’l’auto-
auto-affectation se fait en comparant l’l’adresse de
l’objet cible avec celle de l’l’objet passé
passé en paramè
paramètre à
l’opé
opé rateur d’
d’affectation
Yacine.Bellik@iut-
Yacine.Bellik@iut-orsay.fr 52
26