NULL
les pointeurs après un free() ?NULL
?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
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.
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.
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.
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.
Cela signifie que vous avez oublié d'inclure le fichier
stdlib.h
.
Voir à ce sujet la question 12.1.
Il est assez facile de corrompre les structures de données internes de malloc(). Les sources les plus plausibles du problème sont :
malloc(strlen(s))
au lieu de
malloc(strlen(s)+1)
.Il y en a d'autres...
Voir aussi les questions 12.2 et 12.8.
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.
Oui, car tous les systèmes ne le font pas d'eux-mêmes.
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 */ }