# Programmation C-C++/Allocation dynamique de mémoire

Ce livre est en cours de copie depuis le site http://casteyde.christian.free.fr/online/cours_cpp/ qui le fournit sous licence GFDL.
Cours de C/C++ :
| Pointeurs et références ||
| | Notion d'adresse |
| | Notion de pointeur |
| | Référencement, indirection |
| | Notion de référence |
| | Lien entre les pointeurs et les références |
| | Passage de paramètres par variable ou par valeur |
| | Références et pointeurs constants et volatiles |
| | Arithmétique des pointeurs |
| | Utilisation des pointeurs avec les tableaux |
| | Les chaînes de caractères : pointeurs et tableaux à la fois ! |
| | Allocation dynamique de mémoire |
| | Pointeurs et références de fonctions |
| | Paramètres de la fonction main - ligne de commande |
| | DANGER |
| Livre original de C. Casteyde ||
Les pointeurs sont surtout utilisés pour créer un nombre quelconque de variables, ou des variables de taille quelconque, en cours d'exécution du programme.
En temps normal, les variables sont créées automatiquement lors de leur définition. Cela est faisable parce que les variables à créer ainsi que leurs tailles sont connues au moment de la compilation (c'est le but des déclarations que d'indiquer la structure et la taille des objets, et plus généralement de donner les informations nécessaires à leur utilisation). Par exemple, une ligne comme :
signale au compilateur qu'une variable tableau de 10000 entiers doit être créée. Le programme s'en chargera donc automatiquement lors de l'exécution.
Mais supposons que le programme gère une liste de personnes. On ne peut pas savoir à l'avance combien de personnes seront entrées, le compilateur ne peut donc pas faire la réservation de l'espace mémoire automatiquement. C'est au programmeur de le faire. Cette réservation de mémoire (appelée encore allocation) doit être faite pendant l'exécution du programme. La différence avec la déclaration de tableau précédente, c'est que le nombre de personnes et donc la quantité de mémoire à allouer, est variable. Il faut donc faire ce qu'on appelle une allocation dynamique de mémoire.

## Allocation dynamique de mémoire en C

Il existe deux principales fonctions C permettant de demander de la mémoire au système d'exploitation et de la lui restituer. Elles utilisent toutes les deux les pointeurs, parce qu'une variable allouée dynamiquement n'a pas d'identificateur étant donné qu'elle n'était a priori pas connue à la compilation, et n'a donc pas pu être déclarée. Les pointeurs utilisés par ces fonctions C n'ont pas de type. On les référence donc avec des pointeurs non typés. Leur syntaxe est la suivante :
malloc (abréviation de « Memory ALLOCation ») alloue de la mémoire. Elle attend comme paramètre la taille de la zone de mémoire à allouer et renvoie un pointeur non typé (void *).
free (pour « FREE memory ») libère la mémoire allouée. Elle attend comme paramètre le pointeur sur la zone à libérer et ne renvoie rien.
Lorsqu'on alloue une variable typée, on doit faire un transtypage du pointeur renvoyé par malloc en pointeur de ce type de variable.
Pour utiliser les fonctions malloc et free, vous devez mettre au début de votre programme la ligne :
Son rôle est similaire à celui de la ligne #include <stdio.h>. Vous verrez sa signification dans le chapitre concernant le préprocesseur.
L'exemple suivant va vous présenter un programme C classique qui manipule des pointeurs. Ce programme réalise des allocations dynamiques de mémoire et manipule une liste de structures dynamiquement, en fonction des entrées que fait l'utilisateur. Les techniques de saisies de paramètres présentées dans le premier chapitre sont également revues. Ce programme vous présente aussi comment passer des paramètres par variable, soit pour optimiser le programme, soit pour les modifier au sein des fonctions appelées. Enfin, l'utilisation du mot clef const avec les pointeurs est également illustrée.

### Exemple 4-13. Allocation dynamique de mémoire en C

Ce programme n'interdit pas les définitions multiples de personnes ayant le même nom. Il n'interdit pas non plus la définition de personnes anonymes. Le lecteur pourra essayer de corriger ces petits défauts à titre d'exercice, afin de s'assurer que les notions de pointeur sont bien assimilées. Rappelons que les pointeurs sont une notion essentielle en C et qu'il faut être donc parfaitement familiarisé avec eux.

## Allocation dynamique en C++

En plus des fonctions malloc et free du C, le C++ fournit d'autres moyens pour allouer et restituer la mémoire. Pour cela, il dispose d'opérateurs spécifiques : new, delete, new et delete. La syntaxe de ces opérateurs est respectivement la suivante :
Les deux opérateurs new et new permettent d'allouer de la mémoire, et les deux opérateurs delete et delete de la restituer.
La syntaxe de new est très simple, il suffit de faire suivre le mot clé new du type de la variable à allouer, et l'opérateur renvoie directement un pointeur sur cette variable avec le bon type. Il n'est donc plus nécessaire d'effectuer un transtypage après l'allocation, comme c'était le cas pour la fonction malloc. Par exemple, l'allocation d'un entier se fait comme suit :
La syntaxe de delete est encore plus simple, puisqu'il suffit de faire suivre le mot clé delete du pointeur sur la zone mémoire à libérer :
Les opérateurs new et delete sont utilisés pour allouer et restituer la mémoire pour les types tableaux. Ce ne sont pas les mêmes opérateurs que new et delete, et la mémoire allouée par les uns ne peut pas être libérée par les autres. Si la syntaxe de delete est la même que celle de delete, l'emploi de l'opérateur new nécessite de donner la taille du tableau à allouer. Ainsi, on pourra créer un tableau de 10000 entiers de la manière suivante :
et détruire ce tableau de la manière suivante :
L'opérateur new permet également d'allouer des tableaux à plusieurs dimensions. Pour cela, il suffit de spécifier les tailles des différentes dimensions à la suite du type de donnée des éléments du tableau, exactement comme lorsque l'on crée un tableau statiquement. Toutefois, seule la première dimension du tableau peut être variable, et les dimensions deux et suivantes doivent avoir une taille entière positive et constante. Par exemple, seule la deuxième ligne de l'exemple qui suit est une allocation dynamique de tableau valide :
Si l'on désire réellement avoir des tableaux dont plusieurs dimensions sont de taille variable, on devra allouer un tableau de pointeurs et, pour chaque ligne de ce tableau, allouer un autre tableau à la main.
L'opérateur new alloue la mémoire et crée les objets dans l'ordre croissant des adresses. Inversement, l'opérateur delete détruit les objets du tableau dans l'ordre décroissant des adresses avant de libérer la mémoire.
La manière dont les objets sont construits et détruits par les opérateurs new et new dépend de leur nature. S'il s'agit de types de base du langage ou de structures simples, aucune initialisation particulière n'est faite. La valeur des objets ainsi créés est donc indéfinie, et il faudra réaliser l'initialisation soi-même. Si, en revanche, les objets créés sont des instances de classes C++, le constructeur de ces classes sera automatiquement appelé lors de leur initialisation. C'est pour cette raison que l'on devra, de manière générale, préférer les opérateurs C++ d'allocation et de désallocation de la mémoire aux fonctions malloc et free du C. Ces opérateurs ont de plus l'avantage de permettre un meilleur contrôle des types de données et d'éviter un transtypage. Les notions de classe et de constructeur seront présentées en détail dans le chapitre traitant de la couche objet du C++.
Lorsqu'il n'y a pas assez de mémoire disponible, les opérateurs new et new peuvent se comporter de deux manières selon l'implémentation. Le comportement le plus répandu est de renvoyer un pointeur nul. Cependant, la norme C++ indique un comportement différent : si l'opérateur new manque de mémoire, il doit appeler un gestionnaire d'erreur. Ce gestionnaire ne prend aucun paramètre et ne renvoie rien. Selon le comportement de ce gestionnaire d'erreur, plusieurs actions peuvent être faites :
* soit ce gestionnaire peut corriger l'erreur d'allocation et rendre la main à l'opérateur new ( le programme n'est donc pas terminé), qui effectue une nouvelle tentative pour allouer la mémoire demandée ;
* soit il ne peut rien faire. Dans ce cas, il peut mettre fin à l'exécution du programme ou lancer une exception std::bad_alloc, qui remonte alors jusqu'à la fonction appelant l'opérateur new. C'est le comportement du gestionnaire installé par défaut dans les implémentations conformes à la norme.
L'opérateur new est donc susceptible de lancer une exception std::bad_alloc. Voir le Chapitre 9 pour plus de détails à ce sujet.
Il est possible de remplacer le gestionnaire d'erreur appelé par l'opérateur new à l'aide de la fonction std::set_new_handler, déclarée dans le fichier d'en-tête new. Cette fonction attend en paramètre un pointeur sur une fonction qui ne prend aucun paramètre et ne renvoie rien. Elle renvoie l'adresse du gestionnaire d'erreur précédent.
Les opérateurs delete et delete peuvent parfaitement être appelés avec un pointeur nul en paramètre. Dans ce cas, ils ne font rien et redonnent la main immédiatement à l'appelant. Il n'est donc pas nécessaire de tester la non nullité des pointeurs sur les objets que l'on désire détruire avant d'appeler les opérateurs delete et delete.