Nous allons voir quelques exemples, car si pour un tableau d'entiers ou une chaîne de caractères l'utilisation de sizeof()
est triviale, ce n'est pas forcément le cas pour d'autres structures de données.
Lorsque nous souhaitons allouer de la mémoire pour un objet lui-même, cela est simple. Les objets concernés sont entre autres les types simples comme int, char. Entre autres aussi les structures, comme par exemple une cellule d'une liste chaînée liste_t déclarée ainsi :
typedef struct liste_t { void *data; struct liste_t *prev; struct liste_t *next; } liste_t;
Parmi les cas simples, citons encore les tableaux (mais pas leurs éléments). Cela donne des exemples comme ceci :
int *entier; int *tableau; float *tableau_f; char *string; liste_t *cellule; entier = malloc (sizeof (*entier)); tableau = malloc (N * sizeof (*tableau)); tableau_f = malloc (N * sizeof (*tableau_f)); string = malloc (TAILLE * sizeof (*string)); cellule = malloc (sizeof (*cellule));
Le premier cas que vous risquez de rencontrer est l'élément d'un tableau. Vous le trouvez dès que vous utilisez un tableau de chaînes de caractères. En effet, si vous déclarez le tableau et allouez la mémoire de manière simple comme ci-dessus, l'allocation de la mémoire de chaque chaîne change un peu. Vous pourriez penser à ceci :
char **tabstr; int i; tabstr = malloc (N * sizeof (*tabstr)); /* Pour simplifier, toutes les chaînes ont la même taille ici */ for (i = 0; i < N, i++) tabstr[i] = malloc (TAILLE * sizeof (*tabstr[i]));
Cependant, même si cela marche, pour éviter de mettre une référence à la variable i
, nous préférons remplacer la dernière ligne par ceci :
tabstr[i] = malloc (TAILLE * sizeof (**tabstr));
Tant que nous y sommes, si vous rencontrez réellement un tel cas, où les chaînes ont toutes la même taille, vous ne programmerez pas comme ci-dessus mais utiliserez un vecteur de chaînes. En d'autres termes, vous ne faites appel à malloc()
qu'une seule fois, et les chaînes se trouvent les unes à la suite des autres. Cela évite de fragmenter la mémoire disponible, et fait gagner quelques cycles d'horloge (un malloc()
en utilise 15 000 en moyenne d'après Nicolas Boulay, auteur entre autres de l'article Tests de l'AMD Athlon FX53 du Linuxmag n°67 et de benchmarks sur cet animal). L'exemple suivant réserve l'espace pour N chaînes et initialise tabstr
pour qu'il fasse référence aux chaînes comme dans l'exemple précédent.
char *vecstr; char **tabstr; vecstr = malloc (TAILLE * N * sizeof (*vecstr)); tabstr = malloc (N * sizeof (*tabstr)); for (i = 0; i < N, i++) tabstr[i] = tabvec + i * TAILLE;
Vous noterez ainsi deux manières d'accéder au cinquième octet de la troisième chaîne de caractères, contre une avec l'exemple d'avant. La manière commune est tabstr[2][4]
. Mais comme les chaînes sont consécutives, vous pouvez aussi maintenant utiliser vecstr[2*TAILLE+4]
.
Un autre cas complexe, plus complexe encore, consiste à allouer de la mémoire à un élément d'une structure contenue dans un tableau. Supposons que nous ayons une liste de n'importe quoi, stockée sous forme de structures, regroupées dans un tableau. L'allocation de la mémoire nécessaire au tableau est encore un cas simple. Mais pour un champ de la structure, voici ce à quoi vous pouvez penser :
typedef struct { char *nom; /* ... */ } nimporte_quoi_t; nimporte_quoi_t *nimp; nimp = malloc (N * sizeof (*nimp)); /* ... */ /* Initialisation du nom du n-ième champ : */ nimp[n].nom = malloc (TAILLE * sizeof (*nimp[n].nom));
Encore une fois, il faut éviter la référence à la variable n
, ce qui donne pour la dernière ligne :
nimp[n].nom = malloc (TAILLE * sizeof (*nimp->nom));
Ces deux cas complexes montrent qu'il faut éviter de faire référence à une variable compteur dans le sizeof()
d'une allocation mémoire, et que vous pouvez vous en sortir en mettant un pointeur vers l'élément comme s'il était seul, comme s'il n'y avait pas de tableau. C'est ainsi que *nimp[n].nom
devient *nimp->nom
car *nimp.nom
(ou nimp[0].nom
) et nimp->nom
pointent sur la même chose.
© 2005 Yves Mettier