# C/Gestione della memoria

C
Copertina Tutti i moduli · Sviluppo
* LinguaggioC/Linguaggio
** PanoramicaC/Linguaggio/Panoramica
** Struttura del linguaggioC/Linguaggio/Struttura del linguaggio
* Variabili, operatori e costantiC/Variabili, operatori e costanti
** VariabiliC/Variabili, operatori e costanti/Variabili
** Operatori aritmeticiC/Variabili, operatori e costanti/Operatori aritmetici
** Operatori sui bitC/Variabili, operatori e costanti/Operatori sui bit
** CostantiC/Variabili, operatori e costanti/Costanti
* Blocchi e funzioniC/Blocchi e funzioni
** Blocchi if e switchC/Blocchi e funzioni/Blocchi if e switch
** Operatori logiciC/Blocchi e funzioni/Operatori logici
** CicliC/Blocchi e funzioni/Cicli
** FunzioniC/Blocchi e funzioni/Funzioni
** mainC/Blocchi e funzioni/main
** LibrerieC/Blocchi e funzioni/Librerie
** RicorsivitàC/Blocchi e funzioni/Ricorsività
* Vettori e puntatoriC/Vettori e puntatori
** VettoriC/Vettori e puntatori/Vettori
** PuntatoriC/Vettori e puntatori/Puntatori
** Interscambiabilità tra puntatori e vettoriC/Vettori e puntatori/Interscambiabilità tra puntatori e vettori
* StringheC/Variabili, operatori e costanti/Stringhe
* Enumerazioni, strutture e unioniC/Enumerazioni, strutture e unioni
** EnumerazioniC/Enumerazioni, strutture e unioni/Enumerazioni
** StruttureC/Enumerazioni, strutture e unioni/Strutture
** UnioniC/Enumerazioni, strutture e unioni/Unioni
* Conversioni di tipoC/Conversioni di tipo
* Lettura e scrittura su fileC/Lettura e scrittura su file
** File di TestoC/Lettura e scrittura su file/File di Testo
** File BinariC/Lettura e scrittura su file/File Binari
* Compilatore e precompilatoreC/Compilatore e precompilatore
** CompilatoreC/Compilatore e precompilatore/Compilatore
** DirettiveC/Compilatore e precompilatore/Direttive
** HeaderC/Compilatore e precompilatore/Header
* VisibilitàC/Visibilità
* Gestione della memoriaC/Gestione della memoria
* Le applicazioni CGIC/Le applicazioni CGI
* Appendice
** Librerie standardC/Appendice/Librerie standard
*** assert.hC/Appendice/Librerie standard/assert.h
*** ctype.hC/Appendice/Librerie standard/ctype.h
*** errno.hC/Appendice/Librerie standard/errno.h
*** float.hC/Appendice/Librerie standard/float.h
*** limits.hC/Appendice/Librerie standard/limits.h
*** locale.hC/Appendice/Librerie standard/locale.h
*** math.hC/Appendice/Librerie standard/math.h
*** setjmp.hC/Appendice/Librerie standard/setjmp.h
*** signal.hC/Appendice/Librerie standard/signal.h
*** stdarg.hC/Appendice/Librerie standard/stdarg.h
*** stddef.hC/Appendice/Librerie standard/stddef.h
*** stdio.hC/Appendice/Librerie standard/stdio.h
*** stdlib.hC/Appendice/Librerie standard/stdlib.h
*** string.hC/Appendice/Librerie standard/string.h
*** time.hC/Appendice/Librerie standard/time.h
** StrumentiC/Appendice/Strumenti
*** InstallerC/Appendice/Strumenti/Installer
** Librerie esterneC/Appendice/Librerie esterne
* Bibliografia
    IndiceC/Indice
Modifica il sommario
Il linguaggio C, come il lettore ha avuto modo di constatare leggendo il testo, è caratterizzato dal dare al programmatore il totale controllo sullo sviluppo della sua applicazione, non mascherando niente, anche a costo di una iniziale difficoltà tutt'altro che scontata.
Pertanto, mentre in linguaggi, come Python, Ruby, Java, la memoria è gestita dal Garbage collector, nel C è compito del programmatore provvedere ad allocare la memoria e soprattutto a deallocarla.
Ovviamente, per comprendere i meccanismi di gestione della memoria, è necessario conoscere l'organizzazione della stessa.

## La memoria: lo stack e lo heap

Possiamo immaginare la memoria divisa in due settori (detti segmenti): lo stack e lo heap. Lo stack è la memoria "fissa", che non cambia nel corso dell'esecuzione del programma, al contrario del segmento heap, che è la memoria cosiddetta "dinamica", le cui dimensioni possono variare durante l'esecuzione dell'applicazione.
Per meglio comprendere il concetto, ricorriamo ad un esempio pratico. Vogliamo creare un software per la somma di due numeri: si dichiarano due variabili con le quali gestire il processo logico suddetto. In questo caso noi conosciamo il numero di valori con cui dobbiamo operare (le due variabili). Ma si possono verificare situazioni nelle quali non si conosce a priori il numero dei valori con i quali il programma dovrà operare.
In questo caso si usano le liste, particolari strutture dati le cui dimensioni possono variare (aumentando o diminuendo) durante l'esecuzione del programma. Immaginiamo quindi che il nostro programma debba memorizzare un numero n di utenti, che non conosciamo a priori e che può variare nel corso dell'esecuzione. Come ovviare a questa situazione?

## Funzioni di allocazione dinamica

L'allocazione dinamica della memoria avviene generalmente attraverso la funzione malloc, oppure calloc, definite nella libreria standard stdlib.h. Se queste riescono a eseguire l'operazione, restituiscono il puntatore alla memoria allocata, altrimenti restituiscono il valore NULL.
void *malloc (size_t dimensione);
void *calloc (size_t numero, size_t dimensione);
La differenza tra le due funzioni sta nel fatto che la prima, malloc, viene utilizzata per allocare un'area di memoria di una certa dimensione (espressa generalmente in byte), mentre la seconda, calloc, permette di indicare il numero di elementi e si presta per l'allocazione di array (che, proprio per questa caratteristica di essere a dimensione variabile, si dicono array dinamici)
Dovendo utilizzare queste funzioni per allocare della memoria, è necessario conoscere la dimensione dei tipi primitivi di dati, ma per evitare incompatibilità conviene farsi aiutare dall'operatore sizeof.
Il valore restituito da queste funzioni è di tipo void * cioè una specie di puntatore neutro, indipendente dal tipo di dati da utilizzare. Per questo, in linea di principio, prima di assegnare a un puntatore il risultato dell'esecuzione di queste funzioni di allocazione, è opportuno eseguire un cast (conversione) al tipo di dato desiderato.
Come si può osservare dall'esempio, il cast viene eseguito con la notazione (int *) che esegue la conversione esplicita in un puntatore a int. Lo standard C non richiede l'utilizzo di questo cast, quindi l'esempio si può ridurre al modo seguente:
La memoria allocata dinamicamente deve essere liberata in modo esplicito quando non serve più. Per questo si utilizza la funzione free che richiede semplicemente il puntatore alla memoria precedentemente allocata e non restituisce alcunché.
È necessario evitare di deallocare più di una volta la stessa area di memoria, perché ciò potrebbe provocare effetti imprevedibili.

### realloc

Lo standard prevede una funzione ulteriore, per la riallocazione di memoria: realloc. Questa funzione si usa per ridefinire l'area di memoria con una dimensione differente (proprio in virtù del fatto che stiamo usando la memoria "dinamica):
In pratica, la riallocazione deve rendere disponibili gli stessi contenuti già utilizzati, salvo la possibilità che questi siano stati ridotti nella parte terminale. Se invece la dimensione richiesta nella riallocazione è maggiore di quella precedente, lo spazio aggiunto può contenere dati casuali. Il funzionamento di realloc non è garantito, pertanto occorre verificare nuovamente, dopo il suo utilizzo, che il puntatore ottenuto sia ancora valido.