Prends le contrôle du Heap. Libère tout.
/* 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 */
/* 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() */
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.
malloc() alloue dans le segment HEAP. Le heap grandit vers le haut, la stack vers le bas.
#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 !) */
int *arr;
arr = malloc(n * sizeof(int));
/* n * 4 bytes réservés sur le heap */
if (arr == NULL)
{
fprintf(stderr, "Not enough memory!\n");
return (NULL); /* ou return (1) dans main */
}
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 */
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.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 ! */
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.
/* 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 ! */
/* 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 */
#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 */
int *a = malloc(5 * sizeof(int));
/* a[0] = ??? (garbage)
a[1] = ???
... */
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).#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 */
ptr = realloc(ptr, size) directement ! Si realloc retourne NULL, tu perds l'ancienne adresse → memory leak impossible à free.| Fonction | Prototype | Init ? | 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 |
#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);
}
#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);
}
#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);
}
# 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 :
⚠️ En cas de leak :
free(). still reachable = bloc accessible mais non libéré en fin de programme (moins grave). Holberton exige 0 definitely lost./* 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 ! */
/* 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 */
Holberton est strict là-dessus. Chaque malloc, calloc, realloc doit être suivi d'un if (ptr == NULL).
Ne jamais hardcoder les tailles. malloc(4) ❌ → malloc(sizeof(int)) ✅. Portabilité garantie.
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);
}
Utilise calloc quand tu veux une struct initialisée à zéro (pointeurs NULL, ints à 0). Évite des bugs difficiles à trouver.
free(ptr); ptr = NULL; — évite les double-free accidentels et les dangling pointers. Habitude à prendre dès maintenant.
Sans -g, Valgrind ne peut pas te donner les numéros de lignes. Toujours compiler avec -g pour le debug.
/*
* 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
*/