• Aucun résultat trouvé

15. Allocations dynamiques

15.2. Allocation dynamique de mémoire

Il y a allocation dynamique de mémoire quand une zone de mémoire n'est allouée que par une demande du programme au cours de son exécution. Un pointeur est alors créé, dont la référence contient l'adresse de la zone de mémoire allouée où la variable pourra être stockée.

On utilise surtout les variables dynamiques pour créer des tableaux dont les dimensions ne sont pas connues au moment de la compilation, mais seulement lors de l'exécution du programme en fonction des paramètres qui lui sont alors donnés.

Les fonctions utilisées pour gérer l'allocation dynamique se trouvent dans la bibliothèque

<alloc.h>, mais plusieurs parmi ces fonctions ne sont pas implémentées en C ANSI.

15.2.1. Les fonctions malloc() et free()

15.2.1.1. La fonction malloc()

Elle permet de faire une demande de réservation d'espace mémoire dans le tas:

void * malloc(unsigned int taille);

L'argument du type entier non signé est la taille du bloc que l'on demande dans le tas. Si l'allocation réussit, la fonction renvoie un pointeur sur le bloc alloué. Si la place disponible d'un seul tenant dans la mémoire est insuffisante ou que la taille demande est nulle, la fonction renvoie un pointeur NULL.

Le prototype de la fonction indique qu'elle renvoie un pointeur void. Il faudra donc, au moment de la demande, lui donner le type du pointeur qui correspond à celui que l'on créé.

char * chaine;

chaine = ( char * )malloc(dimension);

L'opérateur de conversion permet d'obtenir le type désiré, char. De même, si on a définit au préalable une structure Date:

struct Date *d0;

d0 = ( Date * )malloc(sizeof(Date)); //pointeur type Date

15.2.1.2. La fonction free()

Elle libère la mémoire quand elle a été allouée avec malloc() ou calloc() et realloc() que nous allons bientôt étudier.

ATTENTION, il faut surtout éviter de libérer deux fois de suite la même zone de mémoire ou de libérer une zone qui n'est pas allouée. Cela peut conduire sans problème à un superbe plantage du programme.

Exemple : Allocation de mémoire pour une chaîne de caractères

#include <stdio.h>

#include <string.h> /* pour strncpy() */

#include <stdlib.h> /* pour malloc() et free() */

#include <process.h> /* pour exit() */

void lireTaille(unsigned *);

void lireChaine(char*, unsigned);

int main() {

char *chaine;

unsigned dim;

printf(" Entrez la dimension de la chaîne de caractères: ");

lireTaille(&dim);

chaine = ( char * )malloc(dim * sizeof(char));

if (chaine == NULL) {

printf("\n La mémoire disponible est insuffisante!\n");

exit( 1);

printf("\n La mémoire disponible est insuffisante!\n");

exit( 1);

}

*( chaine0 + dim_chaine0 – 1 ) = '\0';

while (scanf("%255[^\n]", chaine1)!= 1) while (getchar() != '\n') ;

while (getchar() != '\n') ; strncpy(chaine0, chaine1, dim_chaine0- 1);

free(chaine1);

while (scanf("%lf", &dble)!= 1) while (getchar() != '\n');

while( getchar() != '\n');

} while (dble < 0.0 || dble > 65535.0);

*entier_u = (unsigned)dble;

}

/* Entrez la dimension de la chaîne de caractères: 16 Entrez la chaîne: OBELIX Idefix!!!

Affichage de la chaîne: OBELIX Idefix! */

Le programme permet de créer dynamiquement une chaîne dont on vient de saisir la dimension, de l'initialiser et de l'afficher.

1) Nous avons respecté, pour la dimension de la chaîne, le type unsigned int de l'argument

de malloc(). Vous pouvez passez un argument de type int à cette fonction, le compilateur fera la conversion et si par malheur l'argument est négatif, malloc() renverra un pointeur NULL.

2) Nous avons utilisé l'opérateur de conversion pour rendre le pointeur renvoyé conforme au type de la variable. La boucle if permet de sortir du programme sans trop de casse grâce à l'instruction exit(1) si l'allocation ne s'est pas faite et donc que le pointeur renvoyé est NULL. La fonction free(chaine) libère la mémoire à la fin du programme. C'est une bonne habitude à prendre, bien que dans ce cas particulier ce ne soit pas nécessaire puisque le programme se ferme aussitôt. Dans une application normale ce programme ne serait qu'un module parmi d'autres et il serait alors indispensable de libérer la mémoire allouée.

3) La fonction lireChaine() utilise elle aussi l'allocation dynamique pour la chaîne source et pouvoir employer strncpy() sans surprises avec une chaîne cible créée dynamiquement.

Exemple : Allocation de mémoire pour un tableau

#include <stdio.h>

#include <stdlib.h> /* pour malloc() et free() */

#include <process.h> /* pour exit() */

printf(" Entrez la dimension du tableau: ");

lireTaille(&n);

tab = ( double * )malloc(n * sizeof(double));

if (tab == NULL) {

printf("\n La mémoire disponible est insuffisante!\n");

exit( 1);

void ecrireTab(double *tableau, unsigned dim_tab) {

unsigned i;

double *ptr;

ptr = tableau;

printf("\n (tableau)= ");

for (i = 0; i < dim_tab; i++)

printf("\n\t [%u]= %lf", i, *( ptr++));

}

void lireTaille(unsigned* entier_u) {

double dble;

do {

while (scanf("%lf", &dble)!= 1) while (getchar() != '\n');

while (getchar() != '\n');

} while (dble < 0.0 || dble > 65535.0);

*entier_u = (unsigned)dble;

}

void lireDouble(double *reel) {

while (scanf("%lf", reel)!= 1) while (getchar() != '\n');

while (getchar() != '\n');

}

C'est un second exemple type. Il n'y a rien de bien nouveau, par soucis d'homogénéité nous n'avons utilisé que des variables entières non signées. Dans lireTab() et ecrireTab() nous avons créé un pointeur de transit *ptr pour ne pas avoir à modifier la valeur de tab.

15.2.1.3. La fonction calloc()

void * calloc(unsigned nombre, unsigned taille);

Elle permet d'allouer dans le « tas » un bloc de (nombre* taille) octets. Le bloc est initialisé à zéro. Comme malloc(), elle renvoie un pointeur sur le bloc réservé, ou un pointeur NULL, si la place manque, ou que les valeurs nombre ou taille sont nulles.

Dans le premier exemple, remplacez la ligne de code qui appelle la fonction malloc() par:

chaine = (char *)calloc(dim, sizeof(char));

La fonction réservera un bloc de (dim* 2) octets et initialisera le bloc à zéro ce qui est parfois un avantage déterminant pour l'usage de cette fonction.

15.2.1.4. La fonction realloc()

void * realloc(void * espace, unsigned taille);

Cette fonction permet de modifier la taille du bloc espace déjà créé avec malloc(), calloc() ou realloc(), pour lui donner la nouvelle dimension taille. Si le nouveau bloc est plus petit

que l'ancien, elle renverra l'adresse de l'ancien bloc. S'il est plus grand, elle pourra l'ajuster quand la place nécessaire est disponible ou bien le recopier ailleurs et renvoyer dans ce cas une nouvelle adresse. En cas d'échec de cette réallocation, si aucune zone mémoire d'un seul tenant n'est disponible ou si l'argument taille est nul, la fonction renvoie un pointeur NULL, et elle est donc alors équivalente à un nouveau malloc().