Allocation dynamique
Approfondir : Allocation dynamique
Un tableau automatique a une durée de vie liée à sa portée. L’allocation dynamique permet de réserver une zone dont la durée de vie est gérée explicitement. Cela donne de la souplesse, mais impose une responsabilité : suivre la capacité, vérifier les échecs et libérer exactement ce qui doit l’être.
malloc et la taille demandée
malloc reçoit une taille en octets et renvoie un pointeur ou NULL en cas d’échec. Les octets réservés ne sont pas initialisés ; il faut écrire les éléments avant de les lire. Pour n entiers, utiliser n * sizeof *valeurs, après avoir vérifié que le produit est représentable. sizeof *valeurs reste cohérent si le type pointé change.
Séparer allocation et propriété
Le pointeur qui possède l’allocation doit être conservé jusqu’à sa libération. Une autre variable peut désigner la même zone sans en être un second propriétaire indépendant. Appeler free sur les deux provoquerait une double libération. Une fonction doit préciser si elle prête un pointeur ou transfère la responsabilité de libérer.
free et durée de vie
free termine la durée de vie de l’allocation. Après cet appel, aucune lecture ni écriture à travers une ancienne adresse de cette zone n’est valide. Mettre le pointeur propriétaire à NULL peut éviter sa réutilisation accidentelle, mais ne met pas à NULL les autres copies de l’adresse. free(NULL) est permis ; free d’une variable automatique ne l’est pas.
realloc sans perdre le pointeur
realloc peut déplacer la zone et échouer. Passer le résultat dans une variable temporaire : si elle est nulle, l’ancienne zone reste disponible pour le traitement d’erreur ; si elle est non nulle, on remplace le propriétaire. En cas d’agrandissement, les éléments ajoutés ne sont pas initialisés. Les anciennes copies d’adresses de la zone ne doivent plus être utilisées après un realloc réussi.
Zéro, dépassements et ressources
Définir explicitement le comportement pour n=0 plutôt que dépendre des variantes de traitement d’une allocation de taille zéro. Vérifier la multiplication de la taille avant l’appel. calloc initialise les octets à zéro, mais cela ne dispense pas de comprendre le type stocké et la capacité. Un programme correct libère aussi les allocations sur ses chemins d’erreur.
Allocation : créer puis libérer un tableau
#include <stdio.h>
#include <stdlib.h>
int main(void) {
size_t n = 4;
int *valeurs;
if (n == 0 || n > (size_t)-1 / sizeof *valeurs) {
return 1;
}
valeurs = malloc(n * sizeof *valeurs);
if (valeurs == NULL) { puts("Allocation impossible."); return 1; }
for (size_t i = 0; i < n; ++i) { valeurs[i] = (int)i + 1; }
int somme = 0;
for (size_t i = 0; i < n; ++i) { somme += valeurs[i]; }
printf("Somme = %d\n", somme);
free(valeurs);
valeurs = NULL;
return 0;
}| Instruction ou étape | Explication |
|---|---|
| Vérification de taille | Évite une multiplication non représentable avant malloc. |
| malloc puis test | Le programme ne déréférence pas un résultat NULL. |
| Première boucle | Initialise 1,2,3,4 avant tout calcul de somme. |
| Deuxième boucle | Additionne les quatre éléments : 10. |
| free | Libère une seule fois l’allocation ; plus aucun accès n’est réalisé ensuite. |
Sortie écran : essai 1
Exécution normale lorsque l’allocation réussit.
Somme = 10Allocation : agrandir avec realloc
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *p = malloc(2 * sizeof *p);
if (p == NULL) { return 1; }
p[0] = 10; p[1] = 20;
int *temporaire = realloc(p, 3 * sizeof *p);
if (temporaire == NULL) { free(p); return 1; }
p = temporaire;
p[2] = 30;
printf("%d %d %d\n", p[0], p[1], p[2]);
free(p);
return 0;
}| Instruction ou étape | Explication |
|---|---|
| Deux éléments | Les valeurs initiales sont écrites avant l’agrandissement. |
| temporaire=realloc(...) | L’ancien propriétaire p est gardé si l’appel échoue. |
| p=temporaire | Après succès, seul le nouveau pointeur est utilisé. |
| p[2]=30 | Le nouvel élément doit être initialisé ; son ancienne valeur n’avait pas à être lue. |
Sortie écran : essai 1
Sous réserve du succès des deux allocations.
10 20 30S’entraîner et vérifier
Exercice 1. Pourquoi éviter p=realloc(p,nouvelle_taille); directement ?
Voir la correctionMasquer la correction
Si realloc échoue, NULL écrase l’unique copie de l’ancien pointeur. La zone initiale n’est pas libérée et peut devenir inaccessible. Une variable temporaire permet de libérer ou conserver cette zone selon la stratégie choisie.
Exercice 2. Mettre p à NULL après free protège-t-il un autre pointeur q qui avait reçu p ?
Voir la correctionMasquer la correction
Non. q garde sa propre copie de l’ancienne adresse et devient un pointeur non utilisable vers l’objet libéré. Il faut suivre tous les alias et leur durée d’utilisation.
Pratiquer avec le TD/TP 1 corrigé · Tous les exercices C.
Repère de lecture : les codes proposés utilisent des constructions disponibles en C11. Les règles du langage et de sa bibliothèque sont consultables dans les projets publics du WG14 (C11) et du WG14 (C23). Les exemples n’utilisent pas les nouveautés propres à C23.
Questions / Réponses
Ajouter un commentaire