← HUB ✦ Holberton School — C Programming ✦

MALLOC
& FREE

Prends le contrôle du Heap. Libère tout.

ALLOCATION AUTO vs DYNAMIQUE
⚔️ LES DEUX MODES D'ALLOCATION
✅ AUTOMATIQUE (stack)
/* Le compilateur gère tout */
int  n = 5;
char s[] = "Hello";
int  ar[3];

/* ✅ Taille connue à la compilation
   ✅ Libération automatique
   ❌ Taille FIXE — pas de variable */
💡 DYNAMIQUE (heap)
/* Tu gères toi-même */
int  n = 5;
char *s = malloc(n * sizeof(char));

/* ✅ Taille décidée à l'exécution
   ✅ Survit à la fin de la fonction
   ❌ DOIT être libérée avec free() */
🕹️ ANALOGIE GAMING

L'allocation automatique, c'est ton inventaire de départ dans un RPG : 5 emplacements fixes, toujours disponibles, vidés automatiquement en fin de niveau. L'allocation dynamique, c'est un coffre que tu loues dans la ville (le heap) : tu choisis la taille, tu peux y stocker autant que tu veux — mais tu dois rendre les clés (appeler free()) quand tu n'en as plus besoin, sinon le coffre reste loué pour rien.

LA MÉMOIRE VIRTUELLE — OÙ VIT MALLOC ?
🗺️ CARTE DE LA MÉMOIRE VIRTUELLE

malloc() alloue dans le segment HEAP. Le heap grandit vers le haut, la stack vers le bas.

haute ↑
Kernel Space (inaccessible)
↓ variable
STACK Variables locales, args, adresses de retour — libération AUTO
↕ grandit ↑
HEAP ⬅ malloc() alloue ici — libération MANUELLE avec free()
fixe
BSS / DATA Variables globales et statiques
basse ↓
TEXT Code compilé (fonctions, instructions)
Stack = rapide, taille limitée (~8MB). Heap = lent, taille limitée par la RAM dispo. Ne jamais retourner un pointeur vers une variable locale (stack) — elle est détruite à la fin de la fonction !
MALLOC — ALLOUER DE LA MÉMOIRE
📦 PROTOTYPE & COMPORTEMENT
#include <stdlib.h>   /* OBLIGATOIRE */

void *malloc(size_t size);
/* ↑ retourne void* (cast implicite en C)
   ↑ retourne NULL si échec
   ↑ mémoire NON initialisée (contient des données aléatoires !) */
1

Allouer un tableau de n entiers

int *arr;
arr = malloc(n * sizeof(int));
/* n * 4 bytes réservés sur le heap */
2

Toujours vérifier le retour (JAMAIS TRUST MALLOC)

if (arr == NULL)
{
    fprintf(stderr, "Not enough memory!\n");
    return (NULL);  /* ou return (1) dans main */
}
3

Utiliser normalement, puis libérer

arr[0] = 42;     /* utilise comme un tableau normal */
arr[1] = 7;
free(arr);        /* libère la mémoire */
arr = NULL;       /* bonne pratique : évite le dangling pointer */
La mémoire allouée par malloc contient des données indéfinies (garbage). Si tu lis avant d'écrire → comportement indéfini. Utilise calloc si tu veux une initialisation à zéro.
FREE — LIBÉRER LA MÉMOIRE
🗑️ PROTOTYPE & RÈGLES
void free(void *ptr);
/* ptr doit avoir été retourné par malloc / calloc / realloc
   free(NULL) est safe — ne fait rien
   NE PAS free deux fois le même pointeur ! */
🕹️ ANALOGIE GAMING

free() c'est rendre les clés du coffre au gestionnaire de la ville. La mémoire n'est pas effacée immédiatement — elle est juste marquée comme disponible pour d'autres. Si tu gardes une copie des clés et tu essaies de rouvrir le coffre après (dangling pointer), tu risques de tomber sur quelqu'un d'autre qui l'occupe déjà : segfault garanti.

💀 PIÈGES MORTELS
/* Double free — UNDEFINED BEHAVIOR */
free(ptr);
free(ptr);  /* 💀 crash ou corruption */

/* Dangling pointer */
free(ptr);
ptr[0] = 42; /* 💀 ptr pointe vers rien */

/* Free sur stack — INTERDIT */
int x = 5;
free(&x);   /* 💀 x est sur la stack ! */
✅ BONNES PRATIQUES
/* Mettre à NULL après free */
free(ptr);
ptr = NULL;  /* ✅ évite double free */

/* Vérifier NULL avant free */
if (ptr != NULL)
    free(ptr);

/* free(NULL) est safe */
free(NULL);  /* ✅ ne fait rien */
CALLOC — ALLOUER + INITIALISER À ZÉRO
🧊 PROTOTYPE & DIFFÉRENCE AVEC MALLOC
#include <stdlib.h>

void *calloc(size_t nmemb, size_t size);
/* nmemb = nombre d'éléments
   size   = taille de chaque élément
   → alloue nmemb * size bytes
   → initialise TOUT à 0 (contrairement à malloc)
   → retourne NULL si échec */
MALLOC — garbage dedans
int *a = malloc(5 * sizeof(int));
/* a[0] = ??? (garbage)
   a[1] = ???
   ... */
CALLOC — tout à zéro
int *a = calloc(5, sizeof(int));
/* a[0] = 0
   a[1] = 0
   ... tout initialisé ! */
free(a); /* même free() ensuite */
calloc est plus lent que malloc car il initialise la mémoire. Utilise-le quand tu as besoin de zéros garantis (tableaux, chaînes vides, structs initialisées).
REALLOC — REDIMENSIONNER UNE ALLOCATION
📐 PROTOTYPE & COMPORTEMENT
#include <stdlib.h>

void *realloc(void *ptr, size_t size);
/* ptr  = pointeur vers un bloc malloc/calloc existant
   size = nouvelle taille en bytes
   → peut déplacer le bloc en mémoire (nouvelle adresse !)
   → copie les anciennes données dans le nouveau bloc
   → retourne NULL si échec (l'ancien bloc est TOUJOURS valide)
   → realloc(NULL, size)  ≡ malloc(size)
   → realloc(ptr, 0)      ≡ free(ptr) */
/* Pattern sécurisé — NE PAS faire ptr = realloc(ptr, ...) */
int *arr = malloc(5 * sizeof(int));
int *tmp;

tmp = realloc(arr, 10 * sizeof(int));
if (tmp == NULL)
{
    /* arr est toujours valide ici ! */
    free(arr);
    return (NULL);
}
arr = tmp;  /* seulement si succès */
/* arr pointe maintenant vers un bloc de 10 ints
   Les 5 premières valeurs sont conservées */
Ne JAMAIS faire ptr = realloc(ptr, size) directement ! Si realloc retourne NULL, tu perds l'ancienne adresse → memory leak impossible à free.
LES 4 FONCTIONS — TABLEAU RÉCAP
📊 CHEAT TABLE COMPLÈTE
FonctionPrototypeInit ?Usage
malloc void *malloc(size_t size) ❌ Garbage Allocation générale
calloc void *calloc(size_t n, size_t size) ✅ Zéros Tableaux, structs à zéro
realloc void *realloc(void *ptr, size_t size) ↕ Copie Redimensionner un bloc
free void free(void *ptr) — Libérer un bloc malloc/calloc/realloc
LES 3 BOSS — EXEMPLES PROGRESSIFS
🐣 BOSS 1 — CRÉER UNE STRING DYNAMIQUE
⚔️ LVL 1 — FACILE
#include <stdlib.h>
#include <stdio.h>

char *create_string(int len, char c)
{
    char *s;
    int  i;

    s = malloc((len + 1) * sizeof(char)); /* +1 pour le \0 */
    if (s == NULL)
        return (NULL);
    i = 0;
    while (i < len)
    {
        s[i] = c;
        i++;
    }
    s[i] = '\0';
    return (s);  /* caller doit free() */
}

int main(void)
{
    char *str = create_string(5, 'H');

    if (!str)
        return (1);
    printf("%s\n", str);  /* "HHHHH" */
    free(str);
    str = NULL;
    return (0);
}
⚔️ BOSS 2 — TABLEAU DYNAMIQUE DEPUIS ARGV
🔥 LVL 2 — INTERMÉDIAIRE
#include <stdlib.h>
#include <stdio.h>

void print_int_array(int *a, int size)
{
    int i = 0;
    while (i < size)
        printf("%d\n", a[i++]);
}

int main(int ac, char **av)
{
    int *a;
    int asize;
    int i = 0;

    if (ac < 2)
    {
        fprintf(stderr, "Usage: %s number [NUMBER]\n", av[0]);
        return (1);
    }
    asize = ac - 1;
    a = malloc(asize * sizeof(int)); /* taille connue qu'au runtime */
    if (a == NULL)
        return (1);
    while (i < asize)
    {
        a[i] = atoi(av[i + 1]);
        i++;
    }
    print_int_array(a, asize);
    free(a);
    return (0);
}
💀 BOSS FINAL — TABLEAU DYNAMIQUE AVEC REALLOC
💀 LVL 3 — HOLBERTON STYLE
#include <stdlib.h>
#include <stdio.h>

/* Ajoute un élément à un tableau en le redimensionnant */
int *push(int *arr, int *size, int value)
{
    int *tmp;

    tmp = realloc(arr, (*size + 1) * sizeof(int));
    if (tmp == NULL)
    {
        free(arr);         /* libère l'ancien bloc */
        return (NULL);
    }
    tmp[*size] = value;
    (*size)++;
    return (tmp);          /* peut être une nouvelle adresse ! */
}

int main(void)
{
    int *arr = NULL;
    int size = 0;
    int i;

    arr = push(arr, &size, 10);
    arr = push(arr, &size, 20);
    arr = push(arr, &size, 30);
    if (!arr)
        return (1);
    i = 0;
    while (i < size)
        printf("%d\n", arr[i++]);  /* 10, 20, 30 */
    free(arr);
    return (0);
}
VALGRIND — DÉBOGUER LES MEMORY LEAKS
🔍 COMMANDES VALGRIND
# Compiler TOUJOURS avec -g pour valgrind
gcc -Wall -Werror -Wextra -pedantic -std=gnu89 -g main.c -o prog

# Vérification basique des leaks
valgrind ./prog

# Vérification complète (recommandée Holberton)
valgrind --leak-check=full ./prog

# Vérification ultra détaillée (affiche chaque allocation)
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes ./prog

Lire la sortie Valgrind :

==12345== HEAP SUMMARY:
==12345== in use at exit: 0 bytes in 0 blocks
==12345== total heap usage: 3 allocs, 3 frees, 72 bytes allocated

==12345== All heap blocks were freed -- no leaks are possible
==12345==
==12345== ERROR SUMMARY: 0 errors from 0 contexts

⚠️ En cas de leak :

==12345== LEAK SUMMARY:
==12345== definitely lost: 40 bytes in 1 blocks
==12345== indirectly lost: 0 bytes in 0 blocks
==12345==
==12345== 40 bytes in 1 blocks are definitely lost
==12345== at 0x...: malloc (in /usr/lib/valgrind/...)
==12345== by 0x...: main (main.c:12) ← ligne du malloc non libéré
definitely lost = tu as oublié un free(). still reachable = bloc accessible mais non libéré en fin de programme (moins grave). Holberton exige 0 definitely lost.
ERREURS CLASSIQUES — ❌ vs ✅
💀 LES CRIMES MÉMOIRE
/* 1. Oublier le +1 pour \0 */
char *s = malloc(strlen(str));
/* ❌ → buffer overflow sur \0 */

/* 2. Ne pas vérifier malloc */
int *p = malloc(100);
p[0] = 42; /* ❌ si NULL → segfault */

/* 3. Double free */
free(p); free(p); /* ❌ undefined behavior */

/* 4. realloc direct sans tmp */
p = realloc(p, 200);
/* ❌ si NULL → perd l'ancien p */

/* 5. Free partiel de tableau */
int *arr = malloc(5 * sizeof(int));
free(arr + 2); /* ❌ pas l'adresse d'origine ! */
✅ LES BONNES PRATIQUES
/* 1. +1 pour \0 */
char *s = malloc(strlen(str) + 1);
/* ✅ */

/* 2. Toujours vérifier */
int *p = malloc(100);
if (!p) return (NULL);

/* 3. NULL après free */
free(p); p = NULL; /* ✅ */

/* 4. tmp pour realloc */
tmp = realloc(p, 200);
if (!tmp) { free(p); return (NULL); }
p = tmp;  /* ✅ */

/* 5. Toujours free l'adresse de base */
int *arr = malloc(5 * sizeof(int));
free(arr);  /* ✅ adresse d'origine */
TIPS SPÉCIAUX HOLBERTON
🎓 LES 6 COMMANDEMENTS DE LA MÉMOIRE
Never trust malloc — toujours vérifier NULL

Holberton est strict là-dessus. Chaque malloc, calloc, realloc doit être suivi d'un if (ptr == NULL).

Toujours utiliser sizeof avec malloc

Ne jamais hardcoder les tailles. malloc(4) ❌ → malloc(sizeof(int)) ✅. Portabilité garantie.

0 leaks Valgrind = passage du projet

Chaque allocation doit avoir son free. Inclure les free dans les chemins d'erreur !

/* Libérer en cascade si erreur */
if (!(node->name = malloc(len)))
{
    free(node);   /* libère ce qu'on a déjà alloué */
    return (NULL);
}
calloc pour les structs et tableaux à zéro

Utilise calloc quand tu veux une struct initialisée à zéro (pointeurs NULL, ints à 0). Évite des bugs difficiles à trouver.

Mettre le pointeur à NULL après free

free(ptr); ptr = NULL; — évite les double-free accidentels et les dangling pointers. Habitude à prendre dès maintenant.

Compiler avec -g avant de lancer Valgrind

Sans -g, Valgrind ne peut pas te donner les numéros de lignes. Toujours compiler avec -g pour le debug.

RÉSUMÉ — TOUT EN UN COUP D'ŒIL
🗺️ LA CARTE MENTALE COMPLÈTE
/*
 *  FONCTION   INIT    USAGE                      FREE
 *  ─────────────────────────────────────────────────────────────
 *  malloc(n)  ❌      allocation générale         free(ptr)
 *  calloc(n,s)✅ 0    tableau/struct à zéro       free(ptr)
 *  realloc(p,n) copie redimensionner un bloc      free(ptr)
 *  free(ptr)   —      libérer malloc/calloc/realloc
 *
 *  LE PATTERN SÉCURISÉ :
 *  ─────────────────────────────────────────────────────────────
 *  ptr = malloc(n * sizeof(type));   // allouer
 *  if (ptr == NULL)                  // TOUJOURS vérifier
 *      return (NULL);
 *  [... utiliser ptr ...]
 *  free(ptr);                        // libérer
 *  ptr = NULL;                       // éviter dangling pointer
 *
 *  VALGRIND :
 *  ─────────────────────────────────────────────────────────────
 *  gcc -g main.c -o prog
 *  valgrind --leak-check=full ./prog
 *  → "0 bytes in 0 blocks" + "0 errors" = ✅ parfait
 *
 *  RÈGLES D'OR :
 *  ─────────────────────────────────────────────────────────────
 *  1. 1 malloc = 1 free (pas plus, pas moins)
 *  2. Toujours free l'adresse d'ORIGINE (pas ptr+2 !)
 *  3. Ne jamais free une variable locale (stack)
 *  4. realloc → utiliser un tmp, ne jamais écraser directement
 *  5. +1 pour le \0 des strings
 */