Tous les tableaux que vous avez écrits jusqu'ici avaient leur taille figée à la compilation : int scores[10]; réserve dix cases que vous en ayez besoin de trois ou de trois cents. Cela fonctionne jusqu'à ce que la taille ne soit connue qu'à l'exécution - combien de lignes a un fichier, combien d'enregistrements l'utilisateur saisit. La mémoire dynamique règle cela : vous demandez les octets dont vous avez besoin, au moment où vous en avez besoin, et vous les rendez quand vous avez fini.
Les outils sont dans <stdlib.h> :
#include <stdlib.h>
Pourquoi le tas existe
Le C donne à votre programme deux grandes régions de mémoire. Les variables locales vivent dans le stockage automatique (la pile) : le compilateur décide de leur taille, et elles disparaissent au retour de leur fonction. Les allocations dynamiques vivent dans le tas : vous décidez de la taille à l'exécution, et le bloc reste vivant jusqu'à ce que vous le libériez explicitement.
Cette seconde propriété est la vraie raison d'utiliser malloc. Une fonction peut allouer un bloc, renvoyer le pointeur, et la mémoire est toujours valide chez l'appelant - ce qu'un tableau local ne peut jamais faire. Les deux régions sont comparées en détail dans pile et tas.
malloc : demander des octets
malloc prend un nombre d'octets et renvoie un void * pointant sur ce nombre d'octets de mémoire non initialisée, ou NULL si la demande échoue.
Quatre choses dans ce court programme constituent toute la discipline :
- L'expression de taille est
n * sizeof *scores. Lisez-la « n fois ce quescorespointe ». - Le résultat est vérifié contre
NULLavant tout usage. - Le bloc s'utilise exactement comme un tableau -
scores[i]fonctionne parce que l'indexation est de l'arithmétique de pointeurs. - Il est libéré une fois, et le pointeur est ensuite mis à
NULL.
Notez qu'il n'y a pas de cast sur la valeur de retour. En C, void * se convertit automatiquement vers tout pointeur d'objet, et écrire (int *)malloc(...) ajoute du bruit tout en masquant un <stdlib.h> manquant. (Le C++ exige le cast ; le C non.)
L'idiome sizeof
Pourquoi sizeof *scores plutôt que sizeof(int) ? Parce que cela ne peut pas devenir périmé.
int *a = malloc(n * sizeof *a); /* n int, quel que soit int ici */
long *b = malloc(n * sizeof *b); /* meme forme de ligne, bonne taille */
/* la forme fragile */
long *c = malloc(n * sizeof(int)); /* compile, alloue trop peu */
La dernière ligne est un vrai bug qu'aucun compilateur ne signale : c est un long *, l'allocation est dimensionnée pour des int, et toute écriture au-delà de la première moitié part hors du bloc. Avec sizeof *c, la taille suit automatiquement la déclaration.
sizeof *p ne déréférence pas p - sizeof est évalué à la compilation d'après le type seul, il est donc sûr même quand p n'est pas initialisé ou vaut NULL.
De la mémoire non initialisée
malloc n'efface pas ce qu'il vous donne. Les octets contiennent ce qui s'y trouvait avant.
Quand vous voulez que le bloc soit mis à zéro pour vous, calloc le fait en une étape - voir calloc et realloc.
free : le rendre
free(p) rend le bloc à l'allocateur. Trois règles :
- Passez le pointeur exact renvoyé par
malloc, pas un pointeur avancé.free(p + 1)est un comportement indéfini. - Libérez chaque bloc exactement une fois. Deux fois, c'est une double libération, qui corrompt la comptabilité interne de l'allocateur.
- Après la libération, le pointeur est périmé. L'utiliser est une utilisation après libération.
int *p = malloc(sizeof *p);
free(p);
*p = 5; /* utilisation apres liberation - comportement indefini */
free(p); /* double liberation - comportement indefini */
Aucune de ces lignes n'est tenue de planter immédiatement, ce qui les rend dangereuses : le programme peut tourner des minutes puis échouer à un endroit sans rapport. La défense bon marché est de vider le pointeur :
free(p);
p = NULL;
free(NULL) est explicitement défini comme ne faisant rien, donc un second free(p) après cela est inoffensif, et *p devient un plantage immédiat au vrai point fautif au lieu d'une corruption discrète.
Un tableau dynamique, de bout en bout
Voici la forme d'une vraie allocation : lire un nombre à l'exécution, dimensionner le bloc dessus, l'utiliser, le libérer.
Le cast en size_t sur le compte compte dès que les tailles deviennent grandes : n * sizeof *values avec un n de type int peut déborder avant même que la multiplication n'atteigne malloc, produisant un bloc bien plus petit que prévu. Multiplier en size_t évite cela.
Allouer dans une fonction
La règle de durée de vie du tas est ce qui rend ceci légal - et ce qui rend le renvoi d'un tableau local illégal :
len + 1 laisse la place pour le terminateur nul - la même règle de dimensionnement que partout dans les chaînes. Et remarquez le commentaire au-dessus de la fonction : quand une fonction renvoie de la mémoire allouée, qui la libère fait partie de son contrat. Écrire cela n'est pas de la paperasse ; c'est la seule chose qui empêche une fuite.
Des structures sur le tas
Le même idiome fonctionne pour n'importe quel type :
Notez que la seconde branche d'échec libère p avant de retourner. Dès qu'une fonction détient plus d'une allocation, chaque chemin d'erreur doit rendre celles déjà prises - la source la plus fréquente des fuites décrites dans fuites de mémoire.
Aide-mémoire
- Vérifiez toujours
malloccontreNULLavant le premier usage. - Dimensionnez avec
n * sizeof *p, en multipliant ensize_t. - Pas de cast sur la valeur de retour en C.
- La mémoire de
mallocn'est pas initialisée ; écrivez avant de lire. - Libérez exactement une fois, avec le pointeur d'origine, puis mettez-le à
NULL. - Chaque chemin d'erreur après une allocation doit libérer ce qu'il détient déjà.
Questions fréquentes
Que fait malloc en C ?
malloc(n) demande n octets de mémoire au tas et renvoie un pointeur sur le début de ce bloc, ou NULL si la demande ne peut pas être satisfaite. Le bloc vit jusqu'à ce que vous passiez ce même pointeur à free - contrairement à une variable locale, il n'est pas libéré au retour de la fonction.
Comment utilise-t-on malloc et free en C ?
Allouez avec int *p = malloc(n * sizeof *p);, vérifiez if (p == NULL) avant d'y toucher, utilisez-le comme un tableau, puis free(p); exactement une fois quand vous avez fini. Mettre p = NULL ensuite transforme une utilisation accidentelle ultérieure en plantage net plutôt qu'en corruption silencieuse.
Pourquoi malloc(n * sizeof *p) vaut-il mieux que malloc(n * sizeof(int)) ?
Parce que sizeof *p suit automatiquement le type du pointeur. Si p devient plus tard un long * ou un struct Point *, la taille d'allocation se met à jour toute seule ; écrire sizeof(int) laisse une taille désormais fausse que le compilateur ne signalera pas.
Que se passe-t-il si on ne libère pas la mémoire en C ?
Le bloc reste alloué pendant toute la vie du processus - une fuite de mémoire. Un programme court s'en tire parce que le système d'exploitation récupère tout à la sortie, mais un programme de longue durée fuit un peu à chaque passage et finit par épuiser la mémoire. Voir fuites de mémoire.