Salta al contenuto
Note per Studenti Gestione della memoria in C

Gestione della memoria in C

In questa pagina 5

Organizzazione della memoria di un processo

Segmento Contenuto Gestione Durata
Codice (text) istruzioni macchina caricato dal sistema operativo intero programma
Dati statici variabili globali e static, letterali stringa allocati all'avvio, inizializzati a zero se non indicato intero programma
Stack record di attivazione: parametri, variabili locali, indirizzo di ritorno automatica: cresce a ogni chiamata, si libera al ritorno fino alla fine della funzione/blocco
Heap memoria allocata dinamicamente manuale: malloc / free finché non si chiama free

Lo stack è piccolo (tipicamente qualche MB): array locali molto grandi o ricorsione troppo profonda causano stack overflow. L'heap è grande quanto la memoria disponibile.

Perché l'allocazione dinamica

Serve quando:

malloc, calloc, realloc, free

Funzioni di <stdlib.h>:

c
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    int n;
    if (scanf("%d", &n) != 1 || n <= 0) return 1;

    int *v = malloc(n * sizeof(int));      /* n interi, contenuto indeterminato */
    if (v == NULL) {                       /* memoria esaurita */
        fprintf(stderr, "allocazione fallita\n");
        return 1;
    }
    for (int i = 0; i < n; i++)
        v[i] = i * i;                      /* si usa come un array */

    free(v);                               /* restituisce la memoria */
    v = NULL;                              /* evita usi successivi per errore */
    return 0;
}
Funzione Effetto
malloc(byte) alloca byte byte non inizializzati; restituisce void * o NULL
calloc(n, dim) alloca n elementi di dim byte, azzerati
realloc(p, nuovi_byte) ridimensiona il blocco; può spostarlo e restituire un nuovo indirizzo (copiando il contenuto); NULL se fallisce, nel qual caso il vecchio blocco resta valido
free(p) libera un blocco ottenuto da malloc/calloc/realloc; free(NULL) non fa niente
  • Calcolare sempre la dimensione con sizeof: malloc(n * sizeof *v) resta corretto anche se cambia il tipo di v.
  • Ogni malloc riuscita deve avere esattamente un free corrispondente.

Array che cresce (come list.append)

c
int cap = 4, n = 0;
int *v = malloc(cap * sizeof *v);
int x;
while (scanf("%d", &x) == 1) {
    if (n == cap) {
        cap *= 2;                                   /* raddoppio: append O(1) ammortizzato */
        int *tmp = realloc(v, cap * sizeof *v);
        if (tmp == NULL) { free(v); return 1; }
        v = tmp;
    }
    v[n++] = x;
}
free(v);

Raddoppiando la capacità, il costo totale delle copie su n inserimenti è O(n)O(n): è lo stesso meccanismo delle Liste in Pythonlist come sequenza mutabile (array dinamico); indici, slicing e assegnamento a fette; metodi e loro costo; aliasing, copia superficiale e profonda; list comprehension; liste annidate e matrici.Liste in Python →.

Restituire memoria da una funzione

c
int *crea_sbagliato(void) {
    int locale[100];
    return locale;            /* SBAGLIATO: locale sparisce al ritorno (dangling pointer) */
}

int *crea_array(int n) {
    int *v = malloc(n * sizeof *v);   /* corretto: vive nell'heap */
    return v;                 /* il chiamante dovrà fare free */
}

char *duplica(const char *s) {
    char *c = malloc(strlen(s) + 1);  /* +1 per '\0' */
    if (c != NULL)
        strcpy(c, s);
    return c;
}

Va documentato chi è responsabile del free (di solito il chiamante).

Errori classici

Errore Che cosa succede
Memory leak si perde l'ultimo puntatore a un blocco senza free: memoria occupata fino alla fine del processo
Dangling pointer uso di un puntatore a memoria già liberata o a una variabile locale di una funzione terminata
Double free free due volte sullo stesso blocco: corruzione dell'heap
Buffer overflow scrittura oltre la fine del blocco allocato
Memoria non inizializzata lettura di un blocco da malloc prima di averlo scritto
free di memoria non dinamica free su un array locale o su un puntatore spostato a metà blocco
p = realloc(p, ...) se realloc fallisce p diventa NULL e il blocco originale è perso (leak)

Nessuno di questi errori è segnalato dal compilatore e spesso il programma sembra funzionare. Strumenti: valgrind --leak-check=full ./prog (Linux), opzione -fsanitize=address di gcc/clang (vedi Debugging e testingTipi di errore, metodo per localizzare un bug, lettura del traceback, stampe di controllo, debugger (VS Code, pdb, gdb), assert e strumenti per la memoria in C; test di unità, casi limite e partizioni, doctest e unittest.Debugging e testing →).

Confronto con Python

In Python ogni oggetto vive nell'heap e il garbage collector lo libera quando non è più raggiungibile (Variabili, oggetti e riferimentiIn Python una variabile è un nome legato a un oggetto; tipo, identità e valore; assegnamento, aliasing, oggetti mutabili e immutabili, None.Variabili, oggetti e riferimenti →): leak "classici", dangling pointer e double free non possono capitare. Il prezzo è il controllo e un costo in tempo e memoria.

Teoria collegata