L’allocation dynamique en C permet de gérer la mémoire de manière flexible, en réservant et en libérant de l’espace à la demande pendant l’exécution d’un programme. Contrairement à l’allocation statique (où la taille des variables est définie à la compilation), l’allocation dynamique utilise des fonctions de la bibliothèque standard pour réserver et libérer de la mémoire.
NULL
si l’allocation échoue.void* malloc(size_t size);
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p = malloc(5 * sizeof(int)); // Alloue un tableau de 5 entiers
if (p == NULL) {
printf("Échec de l'allocation mémoire\n");
return 1;
}
for (int i = 0; i < 5; i++) {
p[i] = i * 2;
printf("%d ", p[i]); // Affiche : 0 2 4 6 8
}
printf("\n");
free(p); // Libère la mémoire
return 0;
}
NULL
si l’allocation échoue.void* calloc(size_t num, size_t size);
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p = calloc(5, sizeof(int)); // Alloue un tableau de 5 entiers, initialisés à 0
if (p == NULL) {
printf("Échec de l'allocation mémoire\n");
return 1;
}
for (int i = 0; i < 5; i++) {
printf("%d ", p[i]); // Affiche : 0 0 0 0 0
}
printf("\n");
free(p); // Libère la mémoire
return 0;
}
void* realloc(void* ptr, size_t new_size);
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p = malloc(3 * sizeof(int));
if (p == NULL) {
printf("Échec de l'allocation mémoire\n");
return 1;
}
for (int i = 0; i < 3; i++) {
p[i] = i + 1;
printf("%d ", p[i]); // Affiche : 1 2 3
}
printf("\n");
// Agrandir la mémoire pour 5 entiers
p = realloc(p, 5 * sizeof(int));
if (p == NULL) {
printf("Échec du réallouage mémoire\n");
return 1;
}
p[3] = 4;
p[4] = 5;
for (int i = 0; i < 5; i++) {
printf("%d ", p[i]); // Affiche : 1 2 3 4 5
}
printf("\n");
free(p); // Libère la mémoire
return 0;
}
free
, le pointeur devient invalide et ne doit pas être utilisé.void free(void* ptr);
#include <stdlib.h>
int main() {
int *p = malloc(10 * sizeof(int));
if (p == NULL) {
return 1;
}
free(p); // Libère la mémoire allouée
p = NULL; // Bonne pratique : éviter les pointeurs "dangling"
return 0;
}
Les tableaux peuvent être créés avec une taille déterminée à l’exécution.
#include <stdio.h>
#include <stdlib.h>
int main() {
int n;
printf("Entrez la taille du tableau : ");
scanf("%d", &n);
int *arr = malloc(n * sizeof(int));
if (arr == NULL) {
printf("Échec de l'allocation mémoire\n");
return 1;
}
for (int i = 0; i < n; i++) {
arr[i] = i + 1;
printf("%d ", arr[i]);
}
printf("\n");
free(arr); // Libère la mémoire
return 0;
}
Permet de manipuler des chaînes dont la taille est déterminée dynamiquement.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char *str = malloc(20 * sizeof(char)); // Alloue 20 caractères
if (str == NULL) {
printf("Échec de l'allocation mémoire\n");
return 1;
}
strcpy(str, "Allocation Dynamique");
printf("Chaîne : %s\n", str);
free(str); // Libère la mémoire
return 0;
}
Créez des structures complexes comme des listes chaînées.
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int data;
struct Node *next;
} Node;
Node* creerNoeud(int valeur) {
Node *nouveau = malloc(sizeof(Node));
if (nouveau != NULL) {
nouveau->data = valeur;
nouveau->next = NULL;
}
return nouveau;
}
int main() {
Node *head = creerNoeud(1);
head->next = creerNoeud(2);
head->next->next = creerNoeud(3);
Node *temp = head;
while (temp != NULL) {
printf("%d -> ", temp->data);
temp = temp->next;
}
printf("NULL\n");
// Libération
while (head != NULL) {
Node *next = head->next;
free(head);
head = next;
}
return 0;
}
malloc
, calloc
, ou realloc
retournent une valeur non nulle.free
une fois qu’elle n’est plus nécessaire.NULL
pour éviter les accès invalides.malloc
ou calloc
a un free
correspondant.L’allocation dynamique en C est une fonctionnalité puissante mais peut entraîner des problèmes subtils si elle est mal gérée. Voici une analyse des cas particuliers et des pièges courants rencontrés avec l’allocation dynamique, ainsi que des solutions pour les éviter.
malloc
ou calloc
sans VérificationProblème : Si l’allocation échoue (retourne NULL
), accéder à la mémoire non allouée provoque un comportement indéfini.
#include <stdlib.h>
#include <stdio.h>
int main() {
int *arr = malloc(10 * sizeof(int)); // Allocation sans vérification
arr[0] = 42; // Comportement indéfini si malloc retourne NULL
printf("%d\n", arr[0]);
return 0;
}
Vérifiez toujours le pointeur retourné par malloc
ou calloc
.
int *arr = malloc(10 * sizeof(int));
if (arr == NULL) {
printf("Échec de l'allocation mémoire\n");
return 1;
}
realloc
Problème : Si realloc
échoue, l’ancienne mémoire allouée est perdue si vous n’utilisez pas un pointeur temporaire.
#include <stdlib.h>
#include <stdio.h>
int main() {
int *arr = malloc(5 * sizeof(int));
if (arr == NULL) return 1;
arr = realloc(arr, 10 * sizeof(int)); // Si realloc échoue, l'ancienne mémoire est perdue
if (arr == NULL) {
printf("Réallocation échouée\n");
return 1;
}
return 0;
}
Utilisez un pointeur temporaire pour sécuriser la réallocation.
int *temp = realloc(arr, 10 * sizeof(int));
if (temp == NULL) {
printf("Réallocation échouée\n");
free(arr); // Libérez la mémoire existante si nécessaire
return 1;
}
arr = temp; // Assignez seulement si la réallocation réussit
Problème : Ne pas libérer la mémoire allouée ou libérer un pointeur non alloué peut provoquer des fuites mémoire ou un comportement indéfini.
#include <stdlib.h>
int main() {
int *arr = malloc(5 * sizeof(int));
free(arr); // Libération correcte
free(arr); // Double libération, comportement indéfini
return 0;
}
Libérez une seule fois et réinitialisez le pointeur après la libération.
free(arr);
arr = NULL; // Évite les accès ou libérations multiples
Lors de l’allocation de structures contenant des pointeurs, chaque niveau doit être correctement alloué et libéré.
Si chaque nœud de la liste contient un pointeur vers le nœud suivant, oubliez de libérer chaque nœud.
#include <stdlib.h>
typedef struct Node {
int data;
struct Node *next;
} Node;
int main() {
Node *head = malloc(sizeof(Node));
head->next = malloc(sizeof(Node)); // Allocation d’un deuxième nœud
free(head); // Fuite mémoire pour head->next
return 0;
}
Libérez chaque nœud avant de libérer la liste.
Node *temp;
while (head != NULL) {
temp = head->next;
free(head);
head = temp;
}
Problème : L’utilisation de mémoire en dehors de la plage allouée provoque un comportement indéfini.
#include <stdlib.h>
#include <stdio.h>
int main() {
int *arr = malloc(5 * sizeof(int));
arr[5] = 10; // Dépassement : arr a 5 éléments (indices 0 à 4)
printf("%d\n", arr[5]); // Comportement indéfini
free(arr);
return 0;
}
Les tableaux 2D dynamiques nécessitent des allocations et libérations correctes pour chaque ligne.
int main() {
int **matrix = malloc(3 * sizeof(int *));
for (int i = 0; i < 3; i++) {
matrix[i] = malloc(4 * sizeof(int));
}
free(matrix); // Libère uniquement le tableau des pointeurs, fuite mémoire pour les lignes
return 0;
}
Libérez chaque ligne individuellement avant de libérer le tableau de pointeurs.
for (int i = 0; i < 3; i++) {
free(matrix[i]);
}
free(matrix);
Si une structure alloue un bloc mémoire contigu pour plusieurs parties, libérer une partie de ce bloc entraîne un comportement indéfini.
#include <stdlib.h>
#include <stdio.h>
int main() {
int *arr = malloc(10 * sizeof(int));
free(arr + 5); // Comportement indéfini
return 0;
}
Libérez toujours la mémoire allouée à l’adresse exacte retournée par malloc
, calloc
, ou realloc
.
free(arr);
Un pointeur sauvage est un pointeur non initialisé ou un pointeur qui continue de pointer vers une zone mémoire libérée.
#include <stdlib.h>
#include <stdio.h>
int main() {
int *arr = malloc(5 * sizeof(int));
free(arr);
printf("%d\n", arr[0]); // Pointeur sauvage, comportement indéfini
return 0;
}
NULL
après libération.free
.malloc
et free
Une utilisation excessive de l’allocation dynamique peut entraîner des performances médiocres en raison de la fragmentation mémoire.
for (int i = 0; i < 1000; i++) {
int *temp = malloc(sizeof(int));
*temp = i;
free(temp); // Alloue et libère constamment
}
Réutilisez la mémoire autant que possible.
int *temp = malloc(sizeof(int));
for (int i = 0; i < 1000; i++) {
*temp = i; // Réutilise le même bloc mémoire
}
free(temp);
calloc
Problème : Supposer que calloc
initialise à zéro tous les types, même les pointeurs.
#include <stdlib.h>
#include <stdio.h>
typedef struct {
int *data;
} Node;
int main() {
Node *node = calloc(1, sizeof(Node));
*node->data = 42; // Comportement indéfini, node->data n'est pas initialisé
free(node);
return 0;
}
Initialisez explicitement les pointeurs internes.
node->data = NULL; // Initialisation explicite
Cas Particulier | Solution |
---|---|
Allocation échouée | Vérifiez toujours le retour de malloc ou calloc . |
Mauvaise réallocation | Utilisez un pointeur temporaire pour sécuriser. |
Fuites mémoire | Libérez toute mémoire allouée et utilisez des outils comme Valgrind. |
Accès hors limites | Vérifiez les indices avant d’accéder à un tableau. |
Tableaux 2D dynamiques | Libérez chaque ligne avant le tableau des pointeurs. |
Pointeurs sauvages | Réinitialisez les pointeurs à NULL après libération. |
Fragmentation mémoire | Réutilisez la mémoire allouée autant que possible. |
Mauvaise utilisation de calloc | Initialisez explicitement les pointeurs internes des structures. |
Les fuites mémoire se produisent lorsqu’un programme alloue de la mémoire dynamique sans la libérer, ce qui peut entraîner une consommation excessive de mémoire et réduire les performances du système. Voici un guide pratique pour identifier, éviter et gérer les fuites mémoire en C.
Une fuite mémoire survient lorsque la mémoire allouée dynamiquement n’est pas correctement libérée après usage. Elle reste inaccessible, mais le programme conserve sa réservation, ce qui réduit la mémoire disponible pour d’autres processus.
La mémoire est allouée mais jamais libérée avec free
.
int *arr = malloc(5 * sizeof(int)); // Mémoire allouée
// Oubli de free(arr); fuite mémoire
Lors de la réallocation, si l’ancienne adresse n’est pas sauvegardée, elle est perdue.
int *arr = malloc(5 * sizeof(int));
arr = realloc(arr, 10 * sizeof(int)); // L'adresse initiale est perdue si realloc échoue
Si le pointeur vers la mémoire est écrasé avant d’être libéré, cette mémoire devient inaccessible.
int *arr = malloc(5 * sizeof(int));
arr = NULL; // Adresse initiale perdue
Valgrind est un outil efficace pour détecter les fuites mémoire. Il analyse l’exécution du programme et signale toute mémoire allouée non libérée.
valgrind --leak-check=full ./programme
==12345== 20 bytes in 1 blocks are definitely lost in loss record 1 of 1
==12345== at 0x4C2C1E3: malloc (vg_replace_malloc.c:309)
==12345== by 0x4005A2: main (example.c:5)
Ajoutez des logs pour suivre les allocations et libérations de mémoire.
#include <stdio.h>
#include <stdlib.h>
void *monMalloc(size_t size) {
void *ptr = malloc(size);
if (ptr != NULL) {
printf("Mémoire allouée : %p\n", ptr);
}
return ptr;
}
void monFree(void *ptr) {
printf("Mémoire libérée : %p\n", ptr);
free(ptr);
}
Assurez-vous que chaque appel à malloc
ou calloc
a un appel correspondant à free
.
int *arr = malloc(5 * sizeof(int));
free(arr); // Toujours libérer après utilisation
Réinitialiser un pointeur à NULL
après un free
évite les pointeurs sauvages.
free(arr);
arr = NULL;
Vérifiez toujours si malloc
, calloc
, ou realloc
ont réussi.
int *arr = malloc(5 * sizeof(int));
if (arr == NULL) {
printf("Échec de l'allocation mémoire\n");
exit(1);
}
Libérez chaque allocation dynamique dans des structures imbriquées.
int **matrix = malloc(3 * sizeof(int *));
for (int i = 0; i < 3; i++) {
matrix[i] = malloc(4 * sizeof(int));
}
// Libération complète
for (int i = 0; i < 3; i++) {
free(matrix[i]);
}
free(matrix);
Sauvegardez l’adresse initiale avant d’effectuer une réallocation.
int *temp = realloc(arr, 10 * sizeof(int));
if (temp != NULL) {
arr = temp;
} else {
free(arr); // Libérez l'ancienne mémoire en cas d'échec
}
Créez des fonctions dédiées pour gérer l’allocation et la libération, afin d’uniformiser le code.
void libérerMémoire(int **matrix, int rows) {
for (int i = 0; i < rows; i++) {
free(matrix[i]);
}
free(matrix);
}
Définissez des macros pour suivre les allocations et libérations.
#include <stdlib.h>
#include <stdio.h>
#define MALLOC(size) monMalloc(size, __FILE__, __LINE__)
#define FREE(ptr) monFree(ptr, __FILE__, __LINE__)
void *monMalloc(size_t size, const char *file, int line) {
void *ptr = malloc(size);
printf("Mémoire allouée à %p (Fichier : %s, Ligne : %d)\n", ptr, file, line);
return ptr;
}
void monFree(void *ptr, const char *file, int line) {
printf("Mémoire libérée à %p (Fichier : %s, Ligne : %d)\n", ptr, file, line);
free(ptr);
}
En plus de Valgrind, vous pouvez utiliser des outils comme :
Les allocations dynamiques dans des boucles sans libération causent des fuites.
for (int i = 0; i < 10; i++) {
int *temp = malloc(sizeof(int));
// Oubli de free(temp);
}
Solution : Libérez chaque allocation avant de réitérer.
for (int i = 0; i < 10; i++) {
int *temp = malloc(sizeof(int));
free(temp);
}
Les structures contenant des pointeurs nécessitent une libération explicite pour chaque membre dynamique.
typedef struct {
int *data;
} Node;
Node *node = malloc(sizeof(Node));
node->data = malloc(10 * sizeof(int));
// Libération complète
free(node->data);
free(node);
Dans les programmes longs ou les serveurs, accumuler des fuites peut épuiser la mémoire.
Solution :
Problème | Solution |
---|---|
Oubli de free | Toujours libérer la mémoire allouée. |
Pointeurs sauvages | Réinitialisez les pointeurs à NULL après libération. |
Échec de malloc | Vérifiez si le pointeur est NULL après une allocation. |
Réallocation échouée | Sauvegardez l’adresse initiale avant de réassigner le pointeur. |
Structures imbriquées | Libérez tous les éléments imbriqués (pointeurs dans les structures). |
Détection des fuites | Utilisez des outils comme Valgrind ou AddressSanitizer. |
La réussite d’un projet repose sur une coordination efficace des tâches, une allocation équilibrée des…
Dans le cadre de la mise en œuvre d'un Système de Management de la Qualité…
Le plan de charge constitue un outil stratégique permettant d'anticiper, répartir et ajuster la charge…
Le calcul du stock de sécurité est un incontournable de la gestion logistique. Que ce…
La gestion du stock de sécurité est un levier essentiel pour assurer la continuité des…
La méthode HACCP (Hazard Analysis Critical Control Point), fondée sur la prévention des risques sanitaires,…
This website uses cookies.