Pointeurs, Références & Allocation Dynamique de Mémoire
Rappels sur les adresses et pointeurs
- Tout objet manipulé par l’ordinateur est stocké en mémoire. Selon la nature de l’objet, l’espace en mémoire alloué varie : par exemple, entier = 32 ou 64 bits
- L'adresse est l’endroit où se trouve la variable en mémoire. Elle s’obtient
via la syntaxe suivante :
&NomDeLaVariable
- L’adresse n’étant ni plus ni moins qu’une valeur, on peut donc stocker cette valeur dans une variable : un pointeur est ainsi un conteneur d’adresse
Déclaration d’un pointeur :
int i = 10; int * pt_i = &i; int j = *pt_i;
Notion de référence en C++
Le C++ introduit la notion de référence afin de faciliter la manipulation des variables
Pointeur
// Pointeur int i = 10; int * pt_i = &i; (*pt_i)++;
Référence
// Référence int i = 10; int & ref_i = i; ref_i++;
:BEAMER_ENV: ignoreheading
- La déclaration d’une référence ne crée pas de nouvel objet
- Toute référence doit se référer à un identificateur :
int & ref_i;
ne compilera pas. Il est nécessaire d'initialiser une référence. - Une référence pointe toujours vers le même objet, on ne peut pas changer sa destination.
Références en tant qu’arguments de fonctions
Transmission par adresse
void echange(int * a, int * b) { int c = *a; *a = *b; *b = c; } ... int x = 10; int y = 20; echange(&x, &y);
Transmission par référence
void echange(int & a, int & b) { int c = a; a = b; b = c; } ... int x = 10; int y = 20; echange(x, y);
Allocation dynamique de mémoire
L’allocation dynamique de mémoire est nécessaire dès lors que la taille d’un objet (ou sa nature) n’est connue que lors de l’exécution du programme. La déclaration
unsigned int n = 0; std::cin >> n; double tableau[n];
est rejetée du fait que le compilateur ne connait pas, au préalable, l’espace mémoire nécessaire à l’allocation (statique).
Utilisation des opérateurs new
et delete
- Pour rappel, en langage C, la gestion dynamique de mémoire fait appel aux
fonctions
malloc
etfree
(librairiestdlib.h
) - C++ propose deux nouveaux opérateurs :
new
alloue une certaine quantité de mémoire et renvoie un pointeur sur le début du tableau :unsigned int n = 0; std::cin >> n; double * tableau = new double[n];
delete[]
libère l’espace mémoire :delete[] tableau;
Portée & Durée de vie des variables
Durée de vie limitée au bloc (ici boucle for
)
for (int i = 0; i < 10; i++) { int k = 0; // À la fin du bloc, // destruction de k }
Durée de vie indépendante du bloc
for (int i = 0; i < 10; i++) { int * k = new int(0); // À la fin du bloc, // k existe en mémoire }
Fuite de mémoire garantie
Portée & Durée de vie des variables
Durée de vie limitée au bloc (ici boucle for
)
for (int i = 0; i < 10; i++) { int k = 0; // À la fin du bloc, // destruction de k }
Durée de vie indépendante du bloc
for (int i = 0; i < 10; i++) { int * k = new int(0); ... delete k; }
Portée & Durée de vie des variables
Allocation sur la pile (“stack”)
int * pointeur_dix() { int a = 10; return &a; } int main() { int * pb = pointeur_dix(); cout << *pb << endl; return 0; }
Allocation sur le tas (“heap”)
Portée & Durée de vie des variables
Allocation sur la pile (“stack”)
int * pointeur_dix() { int a = 10; return &a; } int main() { int * pb = pointeur_dix(); cout << *pb << endl; return 0; }
Le pointeur retourné contient une adresse obsolète
Allocation sur le tas (“heap”)
int * pointeur_dix() { int * pa = new int(10); return pa; } int main() { int * pb = pointeur_dix(); cout << *pb << endl; delete pb; return 0; }