Nel linguaggio C, il programmatore gestisce direttamente la memoria: allocazione, liberazione e utilizzo di array, strutture e puntatori è controllato manualmente. Il meccanismo di allocazione dinamica della memoria esiste in C sin dagli anni '70 ed è implementato tramite funzioni standard della libreria (malloc, calloc, realloc, free). Questo approccio offre prestazioni e flessibilità, ma richiede attenzione.
Un errore nella gestione della memoria può portare a perdite, danneggiamento di altri dati, crash del programma o vulnerabilità di sicurezza. Spesso, l'errore si verifica a causa di chiamate free dimenticate, accesso oltre i limiti dell'array, casting errato dei puntatori o doppia liberazione della memoria. Per le strutture, la situazione è simile, ma si aggiunge il rischio di dimenticare di liberare i campi dinamici annidati.
Per ridurre al minimo gli errori, è consigliato sviluppare codice rigorosamente strutturato, tracciare tutte le allocazioni e le liberazioni di memoria, non utilizzare puntatori liberati e tenere traccia delle dimensioni degli array allocati. È importante utilizzare strumenti di analisi statica, controlli finali (valgrind, sanitizers), e seguire convenzioni: chi ha chiamato malloc deve anche liberare la memoria.
Esempio di codice:
#include <stdio.h> #include <stdlib.h> typedef struct { int *arr; size_t size; } ArrayWrapper; ArrayWrapper *create(size_t n) { ArrayWrapper *aw = malloc(sizeof(ArrayWrapper)); if (!aw) return NULL; aw->arr = malloc(sizeof(int) * n); if (!aw->arr) { free(aw); return NULL; } aw->size = n; return aw; } void destroy(ArrayWrapper *aw) { if (aw) { free(aw->arr); free(aw); } }
Caratteristiche chiave:
Cosa succede liberando la memoria su un puntatore nullo (free(NULL))?
Secondo lo standard C, chiamare free su un puntatore nullo è sicuro: non succede nulla, non si verifica errore. Questo è comodo per trasferire la responsabilità della liberazione della memoria.
È possibile usare la memoria dopo aver chiamato free?
No, l'uso della memoria dopo che è stata liberata (use-after-free) è un errore classico. I dati potrebbero cambiare o l'area essere assegnata ad un altro processo. Si dovrebbe sempre azzerare il puntatore dopo free.
int *ptr = malloc(10); free(ptr); ptr = NULL; // Sicuro
È necessario liberare tutta la memoria allocata prima dell'uscita dal programma?
Tecnicamente non è necessario: alla fine del programma, il sistema operativo libera tutte le risorse. Tuttavia, ignorare la liberazione della memoria è un antipattern, rende difficile il debug e porta a errori in programmi grandi e a lungo termine.
Uno sviluppatore crea array dinamici all'interno della funzione, dimenticando di liberarli:
Pro:
Contro:
Tutto il codice viene verificato da un analizzatore statico, tutti i puntatori sono azzerati dopo free, ogni malloc è associato a free:
Pro:
Contro: