précédent  index  suivant

12. Allocation dynamique


12.1 Doit-on ou ne doit-on pas caster malloc() ?

Cette question est probablement celle qui revient le plus souvent dans la discussion. Et à chaque fois, elle engendre une longue discussion.

Certains intervenants pensent que caster la valeur de retour de malloc() est inutile, voire dangereux. En effet, malloc() renvoie un void *. Or, en C, un pointeur void * est implicitement casté lors d'une affectation vers le type de la variable affectée. Bien sûr, expliciter le cast n'est pas interdit, et est parfois utile. Toutefois, caster le retour de malloc() risque de cacher au compilateur l'oubli du prototype de malloc(). Ce prototype se trouve dans le fichier d'en-tête <stdlib.h>. Sans lui, malloc() sera par défaut une fonction retournant un int et dont les paramètres seront du type des arguments passés, ce qui peut provoquer de sérieux bugs.

La véritable erreur est l'oubli du fichier d'en-tête <stdlib.h>, et non pas le cast de malloc() en lui même. Mais le cast de malloc() risque de cacher au compilateur cette erreur. À noter qu'il existe des outils de vérification de code et des options sur la plupart des compilateurs (pour GCC par exemple, l'option -Wall active -Wmissing-prototypes -Wmissing-declarations) qui permettent de détecter ce genre d'erreur.

D'autres intervenants jugent qu'il faille tout de même caster le retour de malloc(), afin de conserver une compatibilité avec d'anciens compilateurs pré-ANSI, ou pour intégrer plus facilement le code avec C++. Evidemment, les programmeurs avertis sauront dans quelles situations il est utile ou non de caster les void *.

Voir aussi la question 7.8

12.2 Comment allouer proprement une variable ?

Le plus portable et le plus simple est de faire ainsi :

	var_t * ma_var = malloc(N * sizeof *ma_var);
		

Si le type de la variable change, l'allocation est toujours valide. À noter que l'on ne caste pas le retour de malloc()

Voir la question 12.1 à ce sujet, ainsi que la question 12.10.

12.3 Pourquoi mettre à NULL les pointeurs après un free() ?

La fonction free() libère l'espace mémoire pointé par le pointeur en question. Mais la valeur de celui-ci ne peut-être changée, car en C les arguments sont passés par valeur aux fonctions.

La variable pointeur contient après le free() une adresse invalide. Son utilisation peut entraîner de sérieux embêtements. Pour éviter cela, une bonne solution consiste à affecter la valeur NULL au pointeur après l'appel à free().

Il existe aussi certaines implémentations de l'allocation dynamique qui fonctionnent en Garbage Collector, c'est-à-dire, que la mémoire n'est réellement libérée que lorsque le pointeur est mis à NULL.

Dans tous les cas, cela permet de tester facilement la validité des pointeurs.

12.4 Pourquoi free() ne met pas les pointeurs à NULL ?

Rappelons que les paramètres des fonctions sont passés par valeur (ou par copie). Ainsi, pour modifier la valeur du pointeur, il faudrait passer un pointeur sur le pointeur, ce qui compliquerait l'utilisation de free(). Mais ce n'est pas le cas, il faut donc le faire soi-même.

12.5 Quelle est la différence entre malloc() et calloc() ?

Pratiquement, calloc() est équivalent à :

	/* p = calloc(m, n); */
	p = malloc(m * n);
	memset(p, 0, m * n);
		

Chaque élément est initialisé à 0. Ce 0 est un « tout bit à zéro ». La valeur des éléments n'est pas forcément valide, suivant leur type.

Voir aussi la question 5.6.

12.6 Que signifie le message « assignment of pointer from integer » quand j'utilise malloc() ?

Cela signifie que vous avez oublié d'inclure le fichier stdlib.h.

Voir à ce sujet la question 12.1.

12.7 Mon programme plante à cause de malloc(), cette fonction est-elle buggée ?

Il est assez facile de corrompre les structures de données internes de malloc(). Les sources les plus plausibles du problème sont :

Il y en a d'autres...

Voir aussi les questions 12.2 et 12.8.

12.8 Que signifient les erreurs « segmentation fault » et « bus error » ?

Cela signifie que vous avez essayé d'accéder à une zone mémoire non autorisée. C'est souvent l'utilisation d'un pointeur non initialisé ou NULL qui en est la cause. Ce genre d'erreur peut aussi provenir d'une mauvaise allocation (cf. 12.7 et 12.2) ou de l'oubli du 0 en fin de chaîne.

12.9 Doit-on libérer explicitement la mémoire avant de quitter un programme ?

Oui, car tous les systèmes ne le font pas d'eux-mêmes.

12.10 Du bon usage de realloc()

La fonction realloc() permet de modifier la taille de l'espace mémoire alloué à une variable. Elle est souvent utilisée pour augmenter cette taille.

Rappelons que la mémoire allouée par malloc(), calloc() et realloc() est fournie sous la forme d'une zone continue (en un seul bloc). Or, il peut arriver que la nouvelle taille demandée dépasse l'espace disponible derrière la zone initiale. Dans ce cas, la fonction realloc() alloue une nouvelle zone ailleur, là ou il y a de la place, et y recopie les données initiales. L'ancienne zone est alors libérée.

C'est pourquoi realloc() renvoie un pointeur sur la nouvelle zone mémoire, même si l'augmentation de taille (ou la réduction) a pu se faire sur place. Bien sûr, comme malloc(), realloc() peut échouer.

Voici pour résumer une bonne manière d'utiliser realloc() :

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

	/* ... */

	int * var = NULL ;
	var = malloc(sizeof * var * 42) ;
	if (!var) {
		/* gestion des erreurs */
	}

	/* ... */

	int * tmp = NULL;
	tmp = realloc(var, 84);
	if (tmp) {
		var = tmp;
	} else {
		/* gestion de l'erreur */
	}
		

précédent  index  suivant