Sie sind auf Seite 1von 7

L'allocation dynamique

Toutes les variables que nous avons créées jusqu'ici étaient construites automatiquement par
le compilateur du langage C. C'était la méthode simple. Il existe cependant une façon plus
manuelle de créer des variables que l'on appelle l'allocation dynamique.
Un des principaux intérêts de l'allocation dynamique est de permettre à un programme de
réserver la place nécessaire au stockage d'un tableau en mémoire dont il ne connaissait pas la
taille avant la compilation. En effet, jusqu'ici, la taille de nos tableaux était fixée « en dur »
dans le code source.
Quand on déclare une variable, on dit qu'on demande à allouer de la mémoire :
int monNombre = 0;

Lorsque le programme arrive à une ligne comme celle-ci, il se passe en fait les choses
suivantes :
1. votre programme demande au système d'exploitation (Windows, Linux, Mac OS…) la
permission d'utiliser un peu de mémoire ;
2. le système d'exploitation répond à votre programme en lui indiquant où il peut stocker
cette variable (il lui donne l'adresse qu'il lui a réservée) ;
3. lorsque la fonction est terminée, la variable est automatiquement supprimée de la
mémoire. Votre programme dit au système d'exploitation : « Je n'ai plus besoin de
l'espace en mémoire que tu m'avais réservé à telle adresse, merci ! L'histoire ne précise
pas si le programme dit vraiment « merci » à l'OS, mais c'est tout dans son intérêt
parce que c'est l'OS qui contrôle la mémoire !
Jusqu'ici, les choses étaient automatiques. Lorsqu'on déclarait une variable, le système
d'exploitation était automatiquement appelé par le programme.

La taille des variables


Selon le type de variable que vous demandez de créer (char,int,double,float…), vous avez
besoin de plus ou moins de mémoire.
En effet, pour stocker un nombre compris entre -128 et 127 (un char), on n'a besoin que d'un
octet en mémoire. C'est tout petit.
En revanche, un int occupe généralement 4 octets en mémoire. Quant au double, il occupe 8
octets.
Le problème est… que ce n'est pas toujours le cas. Cela dépend des machines : peut-être que
un int occupe 8 octets, qui sait ?
Il y a un moyen très facile pour savoir cela : utiliser l'opérateur sizeof().
Contrairement aux apparences, ce n'est pas une fonction mais une fonctionnalité de base du
langage C. Vous devez juste indiquer entre parenthèses le type que vous voulez analyser.
Pour connaître la taille d'un int, on devra donc écrire :
sizeof(int)

À la compilation, cela sera remplacé par un nombre : le nombre d'octets que prend int en
mémoire.
Une nouvelle façon de voir la mémoire
Si on déclare une variable de typeint:
int nombre = 18;

… et que sizeof(int)indique 4 octets sur notre ordinateur, alors la variable occupera 4


octets en mémoire !

Ici, on voit bien que notre variable nombre de type int qui vaut 18 occupe 4 octets dans la
mémoire.
Elle commence à l'adresse 1600 (c'est son adresse par exemple) et termine à l'adresse 1603.
La prochaine variable ne pourra donc être stockée qu'à partir de l'adresse 1604 !
Si on avait fait la même chose avec un char, on n'aurait alors occupé qu'un seul octet en
mémoire.
Imaginez maintenant un tableau de int!
Chaque « case » du tableau occupera 4 octets. Si notre tableau fait 100 cases :
int tableau[100];

on occupera alors en réalité 4 * 100 = 400 octets en mémoire.


Même si le tableau est vide, il prend 400 octets ?
Bien sûr ! La place en mémoire est réservée, aucun autre programme n'a le droit d'y toucher (à
part le vôtre). Une fois qu'une variable est déclarée, elle prend immédiatement de la place en
mémoire.
Notez que si on crée un tableau de type Coordonnees:
Coordonnees tableau[100];

… on utilisera cette fois : 8 * 100 = 800 octets en mémoire.


Allocation de mémoire dynamique
On va avoir besoin d'inclure la bibliothèque<stdlib.h>. Cette bibliothèque contient deux
fonctions dont nous allons avoir besoin :
 malloc(« Memory ALLOCation », c'est-à-dire « Allocation de mémoire ») : demande
au système d'exploitation la permission d'utiliser de la mémoire ;
 free(« Libérer ») : permet d'indiquer à l'OS que l'on n'a plus besoin de la mémoire
qu'on avait demandée. La place en mémoire est libérée, un autre programme peut
maintenant s'en servir au besoin.
Quand vous faites une allocation manuelle de mémoire, vous devez toujours suivre ces trois
étapes :
1. appeler malloc pour demander de la mémoire ;
2. vérifier la valeur retournée par malloc pour savoir si l'OS a bien réussi à allouer la
mémoire ;
3. une fois qu'on a fini d'utiliser la mémoire, on doit la libérer avec free. Si on ne le fait
pas, on s'expose à des fuites de mémoire, c'est-à-dire que votre programme risque au
final de prendre beaucoup de mémoire alors qu'il n'a en réalité plus besoin de tout cet
espace.
malloc: demande d'allocation de mémoire
Le prototype de la fonction malloc est assez comique, vous allez voir :
void* malloc(size_t nombreOctetsNecessaires);

La fonction prend un paramètre : le nombre d'octets à réserver. Ainsi, il suffira d'écrire


sizeof(int)dans ce paramètre pour réserver suffisamment d'espace pour stocker un int.

Mais c'est surtout ce que la fonction renvoie qui est curieux : elle renvoie un…void*! Si vous
vous souvenez du chapitre sur les fonctions, je vous avais dit que void signifiait « vide » et
qu'on utilisait ce type pour indiquer que la fonction ne retournait aucune valeur.
Rassurez-vous, il y a une raison. En fait, cette fonction renvoie un pointeur indiquant l'adresse
que l'OS a réservée pour votre variable. Si l'OS a trouvé de la place pour vous à l'adresse 1600
par xemple, la fonction renvoie donc un pointeur contenant l'adresse 1600.
Le problème, c'est que la fonction malloc ne sait pas quel type de variable vous cherchez à
créer. En effet, vous ne lui donnez qu'un paramètre : le nombre d'octets en mémoire dont vous
avez besoin. Si vous demandez 4 octets, ça pourrait aussi bien être un int qu'un long par
exemple !
Comme malloc ne sait pas quel type elle doit retourner, elle renvoie le type void*. Ce sera
un pointeur sur n'importe quel type. On peut dire que c'est un pointeur universel.
Passons à la pratique.

int* memoireAllouee = NULL; // On crée un pointeur sur int


memoireAllouee = malloc(sizeof(int)); // La fonction malloc inscrit dans notre pointeur l'adresse qui a
été reservée.
À la fin de ce code, memoireAllouee est un pointeur contenant une adresse qui vous a été
réservée par l'OS, par exemple l'adresse 1600.
Tester le pointeur
La fonction malloc a donc renvoyé dans notre pointeur memoireAllouee l'adresse qui a été
réservée pour vous en mémoire. Deux possibilités :
 si l'allocation a marché, notre pointeur contient une adresse ;
 si l'allocation a échoué, notre pointeur contient l'adresse NULL.
Il est peu probable qu'une allocation échoue, mais cela peut arriver. Imaginez que vous
demandiez à utiliser 34 Go de mémoire vive, il y a très peu de chances que l'OS vous réponde
favorablement.
Il est néanmoins recommandé de toujours tester si l'allocation a marché. On va faire ceci : si
l'allocation a échoué, c'est qu'il n'y avait plus de mémoire de libre (c'est un cas critique). Dans
un tel cas, le mieux est d'arrêter immédiatement le programme parce que, de toute manière, il
ne pourra pas continuer convenablement.
On va utiliser une fonction standard: exit(). Elle arrête immédiatement le programme. Elle
prend un paramètre : la valeur que le programme doit retourner (cela correspond en fait au
return du main()).

int main(int argc, char *argv[])


{
int* memoireAllouee = NULL;
memoireAllouee = malloc(sizeof(int));
if (memoireAllouee == NULL) // Si l'allocation a échoué
{
exit(0); // On arrête immédiatement le programme
}
// On peut continuer le programme normalement sinon
return 0;
}

Si le pointeur est différent de NULL, le programme peut continuer, sinon il faut afficher un
message d'erreur ou même mettre fin au programme, parce qu'il ne pourra pas continuer
correctement s'il n'y a plus de place en mémoire.
free: libérer de la mémoire
on va utiliser la fonction free pour libérer la mémoire dont on ne se sert plus.
void free(void* pointeur);

La fonction free a juste besoin de l'adresse mémoire à libérer. On va donc lui envoyer notre
pointeur, c'est-à-dire memoireAllouee dans notre exemple.
Voici le schéma complet et final:
int main( )
{
int* memoireAllouee = NULL;

memoireAllouee = malloc(sizeof(int));
if (memoireAllouee == NULL) // On vérifie si la mémoire a été allouée
{
exit(0); // Erreur : on arrête tout !
}

// On peut utiliser ici la mémoire


free(memoireAllouee); // On n'a plus besoin de la mémoire, on la libère

return 0;
}
Exemple concret d'utilisation
Demander l'âge de l'utilisateur et le lui afficher. La seule différence avec ce qu'on faisait
avant, c'est qu'ici la variable va être allouée manuellement (on dit aussi dynamiquement)
plutôt qu'automatiquement comme auparavant.
int main()
{
int* memoireAllouee = NULL;
memoireAllouee = malloc(sizeof(int)); // Allocation de la mémoire
if (memoireAllouee == NULL)
{
exit(0);
}
// Utilisation de la mémoire
printf("Quel age avez-vous ? ");
scanf("%d", memoireAllouee);
printf("Vous avez %d ans\n", *memoireAllouee);
free(memoireAllouee); // Libération de mémoire
return 0;
}
Allocation dynamique d'un tableau
Imaginons par exemple un programme qui stocke l'âge de tous les amis de l'utilisateur dans un
tableau. Vous pourriez créer un tableau de int pour stocker les âges, comme ceci :
int ageAmis[15];

Mais qui vous dit que l'utilisateur a 15 amis ? Peut-être qu'il en a plus que ça !
Lorsque vous écrivez le code source, vous ne connaissez pas la taille que vous devez donner à
votre tableau. Vous ne le saurez qu'à l'exécution, lorsque vous demanderez à l'utilisateur
combien il a d'amis.
L'intérêt de l'allocation dynamique est là : on va demander le nombre d'amis à l'utilisateur,
puis on fera une allocation dynamique pour créer un tableau ayant exactement la taille
nécessaire (ni trop petit, ni trop grand). Si l'utilisateur a 15 amis, on créera un tableau de
15int; s'il en a 28, on créera un tableau de 28int, etc.
L'avantage de l'allocation dynamique, c'est qu'elle nous permet de créer un tableau qui a
exactement la taille de la variable nombreDAmis, et cela grâce à un code qui fonctionnera
partout !
On va demander au malloc de nous réserver nombreDAmis * sizeof(int)octets en
mémoire :
amis = malloc(nombreDAmis * sizeof(int));

Ce code permet de créer un tableau de type int qui a une taille correspondant exactement au
nombre d'amis !
Voici ce que fait le programme dans l'ordre :
1. demander à l'utilisateur combien il a d'amis ;
2. créer un tableau de int ayant une taille égale à son nombre d'amis (via malloc) ;
3. demander l'âge de chacun de ses amis un à un, qu'on stocke dans le tableau ;
4. afficher l'âge des amis pour montrer qu'on a bien mémorisé tout cela ;
5. à la fin, puisqu'on n'a plus besoin du tableau contenant l'âge des amis, le libérer avec la
fonction free.
int main( )
{
int nombreDAmis = 0, i = 0;
int* ageAmis = NULL; // Ce pointeur va servir de tableau après l'appel du malloc
// On demande le nombre d'amis à l'utilisateur
printf("Combien d'amis avez-vous ? ");
scanf("%d", &nombreDAmis);
if (nombreDAmis > 0) // Il faut qu'il ait au moins un ami
{
ageAmis = malloc(nombreDAmis * sizeof(int)); // On alloue de la mémoire pour le tableau
if (ageAmis == NULL) // On vérifie si l'allocation a marché ou non
{
exit(0); // On arrête tout
}
// On demande l'âge des amis un à un
for (i = 0 ; i < nombreDAmis ; i++)
{
printf("Quel age a l'ami numero %d ? ", i + 1);
scanf("%d", &ageAmis[i]);
}
// On affiche les âges stockés un à un
printf("\n\nVos amis ont les ages suivants :\n");
for (i = 0 ; i < nombreDAmis ; i++)
{
printf("%d ans\n", ageAmis[i]);
}
// On libère la mémoire allouée avec malloc, on n'en a plus besoin
free(ageAmis);
}
return 0;
}
s
En résumé
 Une variable occupe plus ou moins d'espace en mémoire en fonction de son type.
 On peut connaître le nombre d'octets occupés par un type à l'aide de l'opérateur
sizeof().
 L'allocation dynamique consiste à réserver manuellement de l'espace en mémoire pour
une variable ou un tableau.
 L'allocation est effectuée avec malloc()et il ne faut surtout pas oublier de libérer la
mémoire avec free()dès qu'on n'en a plus besoin.
 L'allocation dynamique permet notamment de créer un tableau dont la taille est
déterminée par une variable au moment de l'exécution.

Das könnte Ihnen auch gefallen