La fonction « malloc »
Nous vous rappelons qu'il est impossible de déclarer une variable de type « void » et qu'une fonction de type « void » ne retourne aucune valeur.
Par contre, « void * » correspond à une donnée de type pointeur vers octet. Nous pouvons donc déclarer une variable de type « void * ».
Si une fonction est de type « void * » elle retournera une valeur de type « adresse d'un octet » (voir chapitre 3).
L'en-tête de la fonction « malloc » est le suivant : void *malloc(size_t taille) ;
La fonction malloc sert à faire une allocation dynamique de mémoire dans la zone du « tas ». Le paramètre « taille » est de type « size_t » (type entier), c'est la taille de l'espace-mémoire que l'on veut obtenir en nombre d'octets.
Elle retourne une adresse de type pointeur vers « void » (pointeur vers octet), qui nécessite l'utilisation de la conversion explicite de type (cast voir chapitre 4).
Prenons l'exemple suivant :
void main()
{
char * s;
// une variable de type pointeur sur caractères
int * t;
// une variable de type pointeur sur entier
float * td;
// une variable de type pointeur sur flottant
int i;
s = (char *) malloc(10 * sizeof(char));
t = (int *) malloc(12 * sizeof(int));
td = (float *) malloc(51 * sizeof(float));
scanf("%s",s);
for (i = 0;i<10;i++)
scanf("%d",&(t[i]));
for (i = 0;i<10;i++)
scanf("%f",&(td[i]));
}
L'utilisation de la fonction « sizeof » permet de retourner la taille du réceptacle (en octets) associé à un type (voir chapitre 3). Si on multiplie ensuite par une valeur positive entière, nous avons une valeur entière qui correspond à un nombre d'octets. La fonction « malloc » demande alors au gestionnaire (fictif) de la mémoire centrale de lui réserver le nombre d'octets consécutifs demandés en mémoire.
Si cette allocation est impossible par manque de place, « malloc » retourne la valeur de pointeur « NULL » (Voir chapitre 4), sinon elle retourne l'adresse du premier octet de la zone allouée dans le tas. Toutes les valeurs retournées par la fonction « malloc » sont de type « void * ».
Pour que le compilateur puisse considérer autre chose qu'une zone d'octets, il faut effectuer une conversion explicite de type (un cast). La conversion est réalisée ici grâce aux (char *), (int *), (float *) qui transforment la valeur retournée par « malloc » en « une adresse vers » respectivement : un char, un int et un float.
Après les trois premières instructions qui utilisent "malloc", "s" est une variable qui contient l'adresse d'une zone de caractères, "t" est une variable qui contient l'adresse d'une zone d'entiers et enfin "td" est une variable qui contient l'adresse d'une zone de float.
En conséquence, nous pouvons accéder aux éléments comme avec des tableaux.
En effet, nous vous rappelons que :
-
s[2] est syntaxiquement la même chose que *(s+2)
-
t[4] est syntaxiquement la même chose que *(t+4)
-
td[11] est syntaxiquement la même chose que *(td+11)
Sauf que cette fois-ci "s", "t" et "td" sont des variables de type "pointeur vers".
Nous pouvons donc les manipuler comme un tableau, la seule différence étant que "s", "t", et "td" sont des variables et non des constantes (voir chapitre 6). Nous les utilisons ensuite comme des tableaux dans les instructions qui suivent.
L'allocation dynamique est très utile en programmation pour créer des réceptacles temporaires pour des données.
Cet exemple permet de réserver en mémoire la place pour un tableau d'élèves dont on demande la taille à l'utilisateur.
#define MAX 50
struct eleve
{
char nom[MAX],
prenom[MAX],
float note_moyenne;
};
typedef struct eleve T_eleve;
int * allouer_tableau_eleve(int nb)
{
T_eleve * inter;
// variable locale de la fonction allouer_tableau_eleve
// qui contient une valeur qui est l'adresse d'un tableau alloué dynamiquement
inter = (T_eleve *)malloc(nb*sizeof(T_eleve));
//allocation de « nb » cases, chaque case étant de
// type « T_eleve ».
// La fonction « malloc » retourne un « void* », il ne faut
// donc pas oublier l'opération de « cast ».
return inter;
// on retourne la valeur du pointeur : elle n'est pas perdue
// bien que la variable locale soit détruite après l'appel de la fonction.
}
int main(void)
{
T_eleve * tab_eleve;
int nombre;
prinf("\n entrer le nombre d'eleves : ");
scanf("%d",&nombre);
tab_eleve = allouer_tableau_eleve(nombre);
// appel de la fonction « allouer_tableau_eleve » qui va
//dynamiquement créer
// un tableau de « nombre » cases. Chaque case étant
//une variable de type « T_eleve »
if (tab_eleve == NULL)
prinf("\n probleme d'allocation dynamique");
else
{
// la suite de votre code
}
}
Ce n'est pas un tableau de taille variable, vous avez alloué un nombre fixe de cases.
Si vous ne souhaitez pas utiliser l'ensemble des cases, il vaudrait mieux utiliser la technique algorithmique vue au chapitre 6.