Allocation dynamique de mémoire

Chapitres traités   

 

 

 

 

Prise de connaissance entre les allocations statiques et dynamiques

En C++ les variables peuvent être allouées de deux façons différentes :

  1. Soit statiquement  : Avant l'exécution du programme, le compilateur traite le code source et détermine l'ensemble des variables et réserve un emplacement mémoire en conséquence. La durée de vie de ces variables correspond à la durée de vie du programme. C'est ce type d'allocation que nous avons réalisé jusqu'à présent. La zone d'allocation est soit la zone statique, soit la pile (pour les variables locales). Les zones d'allocations seront traitées ultérieurement.
  2. Soit dynamiquement  : Cette fois-ci, pendant l'exécution du programme, il est possible d'avoir besoin d'une variable pour une utilisation relativement brève dans le temps.  Une variable est alors créée à la volée et elle sera détruite quand le besoin ne s'en fera plus sentir. La zone d'allocation est le tas.

Le compromis entre les deux méthodes d'allocation mémoire est l'efficacité contre la flexibilité :

  1. L'allocation de mémoire statique est considérablement plus efficace car effectué avant le commencement du programme. Quand le programme démarre, tout est prêt pour fonctionner correctement et les variables sont très faciles à atteindre et à manipuler. Par ailleurs le temps de réponse est des plus rapide.
  2. Elle est toutefois moins flexible car elle nécessite de connaître, avant l'exécution du programme, la quantité et le type de mémoire désirés. La taille mémoire nécessaire pour tout le temps d'exécution du programme peut être très conséquent suivant le nombre de variables. Par ailleurs, il n'est pas sûr qu'une variable soit utile pendant toute la durée de vie du programme. Imaginons, par exemple, que nous devions construire un logiciel avec une bonne interface IHM (Interface Homme Machine). Ce logiciel doit utiliser vingt fenêtres différentes, seulement, on est sûr que pendant le déroulement du programme, seules trois sont utilisées en même temps. Avec une allocation statique, il y aurait un gros gaspillage de mémoire. C'est vraiment le cas de figure ou l'allocation dynamique est prépondérante. Nous créons les fenêtres juste au moment où nous en avons besoin.

Choix du chapitre Détail des structures entre les allocations statiques et dynamiques

Toutes les déclarations réalisées jusqu'à présent étaient essentiellement pour des variables statiques. Les deux premières différences entre allocations de mémoire statique et dynamique sont les suivantes :

  1. Les variables statiques sont des variables nommées que l'on manipule directement, alors que les variables dynamiques sont des variables non nommées (anonyme) manipulées indirectement au travers de pointeurs.
  2. L'allocation et la désallocation de variables statiques sont gérées automatiquement par le compilateur ; l'allocation se fait au démarrage du programme et la désallocation à la fermeture de ce dernier. Le programmeur doit comprendre ce qui se passe, mais, il n'a rien à faire de spécial à ce sujet. L'allocation et la désallocation de variables dynamiques, en revanche, doivent être explicitement gérées par le programmeur et, en pratique, sont plus susceptibles de générer des erreurs. Ceci s'effectue grâce à l'utilisation des opérateurs new et delete.

Choix du chapitre Allocation dynamique

Sur un type quelconque d'une unité :

Il faut donc utiliser les pointeurs pour gérer l'allocation dynamique puisque l'accès se fait de façon indirecte. Jusqu'à présent, les pointeurs étaient essentiellement utilisés pour se connecter indirectement sur une autre variable statique et éventuellement pour naviguer au travers de celle-ci ( grâce à l'arithmétique des pointeurs ) si cette dernière représente un tableau.

L'opérateur new s'occupe de l'allocation dynamique. Pour qu'il soit à même de réserver un emplacement suffisant, il faut lui indiquer le type correspondant. Le type indiqué avec l'opérateur new doit être de même nature que le type géré par le pointeur. La syntaxe générale sur l'opérateur new est donc la suivante :

Initialisation dans la mémoire allouée :


Il est possible pour les types primitifs de donner une valeur initiale à la valeur pointée. Il suffit d'utiliser les parenthèses associé au type en donnant comme paramètre la valeur initiale désirée, comme suit :

A partir de nos connaissances actuelles, il n'est pas possible d'initialiser explicitement des types définis par l'utilisateur comme les structures, les énumérations et les unions. Il sera nécessaire de connaître la notion de constructeur.

Utilisation des variables dynamiques :

En fait, il n'y a pas de remarque particulière sauf qu'il faut se souvenir qu'une variable dynamique est anonyme, et donc que la seule possibilté de l'atteindre est d'utiliser systématiquement le pointeur associé.

Souvenez-vous que pour les variables statiques, lorsque nous déclarions un pointeur, il référençait une autre variable statique. Il était alors possible, soit d'utiliser directement la variable pointée, soit indirectement par le pointeur. Dans le cas d'une variable dynamique, la question ne se pose pas, nous sommes systématiquement obligés d'utiliser le pointeur.

Les tableaux dynamiques :

L'opérateur new permet également de déclarer des variables tableaux dynamiques d'un type quelconque. Malheureusement, il n'existe aucun moyen de spécifier individuellement une valeur initiale explicite pour chaque élément du tableau. La syntaxe générale est la suivante :

Type *pointeur = new Type [ n ] ;

Cette instruction alloue alors l'emplacement nécessaire pour n éléments du type spécifié ; si l'opération réussit (suffisamment de mémoire), elle fournit alors au pointeur l'adresse du premier élément de ce tableau. n désigne une expression entière quelconque (pas spécialement constante) non négative.

Choix du chapitre Libération de la mémoire allouée dynamiquement

Il est impératif de libérer la mémoire lorsque nous avons fini de nous servir d'une variable dynamique. Sinon, tout le principe que nous venons d'évoquer ne servirait absolument à rien. La mémoire libérée peut servir ensuite pour les autres variables dynamiques. Ainsi, il est possible, avec peu de mémoire, d'utiliser énormément de variables différentes. Par ailleurs, n'oubliez pas que pour un système multitâche, les autres applications ont besoin elles-mêmes de mémoires pour fonctionner, la mémoire étant partagée pour tous les processus.

Pour libérer une mémoire allouée dynamiquement, il suffit d'utiliser l'opérateur delete. Toutefois, cet opérateur a besoin de connaître l'emplacement de la mémoire à libérer. Comme elle est anonyme, encore une fois, il est nécessaire de passer par le pointeur. Pour libérer un tableau, il suffit de placer les crochets à côté de l'opérateur delete sans précision de la dimension.

int *pint = new int ;
Personne *pPersonne = new Personne ;

delete pint ;
delete pPersonne ;

int *pTabInt = new int[27] ;
Personne * pTabPersonne = new Personne[124] ;

delete [ ] pTabInt ;
delete [ ] pTabPersonne ;

Choix du chapitre Les zones d'allocation mémoire

Le terme statique employé dans ce cours fait opposition au terme dynamique. Il ne s'agit en aucun cas des zones mémoires dédiées. Il existe en effet trois zones de mémoire prévues pour l'ensemble des variables utilisées dans un programme :

  1. la zone statique
  2. la pile
  3. et le tas

Les variables statiques évoquées dans ce cours concerne aussi bien la zone statique que la pile, alors que les variables dynamiques sont créées uniquement dans le tas. Ces sujets seront évoqués ultérieurement lorsque nous connaîtrons plus particulièrement les fonctions.

Pour en savoir plus sur les zones de mémoire