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;
}
Suivre le programme : Allocation, créer puis libérer un tableau
Instruction ou étapeExplication
Vérification de tailleÉvite une multiplication non représentable avant malloc.
malloc puis testLe programme ne déréférence pas un résultat NULL.
Première boucleInitialise 1,2,3,4 avant tout calcul de somme.
Deuxième boucleAdditionne les quatre éléments : 10.
freeLibè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 = 10

Allocation : 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;
}
Suivre le programme : Allocation, agrandir avec realloc
Instruction ou étapeExplication
Deux élémentsLes valeurs initiales sont écrites avant l’agrandissement.
temporaire=realloc(...)L’ancien propriétaire p est gardé si l’appel échoue.
p=temporaireAprès succès, seul le nouveau pointeur est utilisé.
p[2]=30Le 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 30

S’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

Aucune question. Soyez le premier à poser une question.
★ ★ ★ ★ ★
2 votes. Moyenne 5 sur 5.

Ajouter un commentaire

Anti-spam