# Allocazione dinamica delle variabili ## Limiti dell'allocazione statica In C++, l'allocazione statica è un approccio utile per riservare memoria, ma presenta alcune limitazioni che possono rendere complesso lo sviluppo di programmi flessibili: - ### Dimensione predefinita e immutabile - Le strutture dati come gli array devono avere una dimensione specificata al momento della dichiarazione. - Anche se in C++ è possibile dichiarare array con una dimensione determinata in fase di esecuzione, la loro dimensione rimane immutabile una volta allocati. **Esempio:** ```cpp int N; cin >> N; int array[N]; // Dimensione scelta in esecuzione, ma non modificabile successivamente ``` - ### Durata limitata nello stack - Le variabili allocate staticamente risiedono nello **stack**, il che significa che vengono automaticamente deallocate quando il programma esce dal contesto in cui sono state dichiarate (ad esempio, alla fine di una funzione). - Questo rende difficile gestire dati che devono persistere per tutta la durata del programma. Per esempio non è possibile creare un array all'intenro di una funzione e restituirlo, in quanto questo array essendo creato all'interno dello stack della funzione, al termine della funzione verrà deallocato. In C++, oltre alle variabili create automaticamente nello **stack**, possiamo allocare memoria dinamicamente nello **heap**. Questo è utile per creare oggetti o strutture dati di dimensioni sconosciute a priori e poterle creare durante l'esecuzione del programma. ## Allocazione dinamica ### Vantaggi dell'allocazione dinamica - **Dimensione variabile:** La memoria può essere allocata in base alle necessità effettive durante l'esecuzione del programma. - **Persistenza dei dati:** I dati nello heap rimangono allocati finché non vengono **esplicitamente deallocati**. - **Adattabilità:** Permette di gestire meglio scenari in cui i requisiti di memoria non sono noti a priori. ### Quando utilizzare l'allocazione dinamica - Quando non conosci a priori la dimensione delle strutture dati. - Quando i dati devono persistere oltre il contesto in cui sono stati creati. - Per evitare sprechi di memoria allocando solo lo spazio necessario. ### Come utilizzare l'allocazione dinamica in C++ #### L'operatore `new` L'operatore `new` è utilizzato per allocare dinamicamente memoria nello heap. Restituisce un puntatore all'area di memoria allocata. Ha bisogno di sapere quanto spazio riservare nella heap e quindi necessita di sapere che tipo di dato sarà salvato all'interno del nuovo spazio, quindi alla sua destra dobbiamo dire il tipo di datoche desideriamo immagazzinare nella nuova area di memoria. **Sintassi:** ```cpp int *ptr = new int; // Allocazione di un intero *ptr = 42; // Assegno il valore 42 alla memoria allocata ``` **Allocazione di array dinamici:** ```cpp int *array = new int[10]; // Allocazione di un array di 10 interi ``` #### L'operatore `delete` L'operatore `delete` è utilizzato per liberare la memoria allocata dinamicamente. Questo è necessario per evitare perdite di memoria (**memory leak**). **Sintassi:** ```cpp delete ptr; // Dealloca un singolo oggetto ``` **Deallocazione di array dinamici:** ```cpp delete[] array; // Dealloca un array ``` <im> Non utilizzare `delete` per la memoria allocata nello stack o per variabili non allocate dinamicamente. </im> <ac> **Creare un Array in una Funzione e Restituirlo** L'obiettivo di questa attività è costruire una funzione che crea un array di dimensione *N* e lo restituisce al chiamante. Iniziamo creando una funzione che alloca un array nello stack e lo restituisce tramite un puntatore. ```cpp #include <iostream> using namespace std; int* creaArray(int n) { int array[n]; // Allocazione dell'array nello stack for (int i = 0; i < n; i++) { array[i] = i + 1; // Popolamento dell'array } return array; // Tentativo di restituire l'array } int main() { int n = 5; int* array = creaArray(n); // Restituzione del puntatore all'array // Accesso all'array restituito cout << "Elementi dell'array: "; for (int i = 0; i < n; i++) { cout << array[i] << " "; // Potrebbe causare comportamento indefinito } cout << endl; return 0; } ``` **Cosa succede:** 1. La funzione `creaArray` crea un array locale nello stack. 2. L'array viene popolato e un puntatore al suo primo elemento viene restituito al chiamante. 3. Dopo che la funzione termina, la memoria dell'array viene deallocata automaticamente perché appartiene allo stack. 4. Quando il chiamante accede al puntatore restituito, questo punta a un'area di memoria non più utilizzata dalla funzione per memorizzare l'array di valori, causando comportamento indefinito. **Osservazione durante l'esecuzione:** Il programma potrebbe funzionare apparentemente, ma il comportamento è instabile e dipende dallo stato della memoria. **Capiamo il problema** Spieghiamo chiaramente perché il puntatore restituito è pericoloso: - Gli array creati nello stack esistono solo finché la funzione non termina. - Restituire un puntatore a un array locale significa accedere a una zona di memoria che non è più valida. - Questo causa **comportamento indefinito**: il programma potrebbe funzionare, ma è altamente soggetto a errori. Per risolvere il problema, usiamo l'allocazione dinamica nello heap. Creiamo l'array con il comando `new` all'interno della funzione. ```cpp #include <iostream> using namespace std; int* creaArray(int n) { int* array = new int[n]; // Allocazione dinamica nello heap for (int i = 0; i < n; i++) { array[i] = i + 1; // Popolamento dell'array } return array; // Restituzione del puntatore all'array } int main() { int n = 5; int* array = creaArray(n); // Restituzione del puntatore all'array // Accesso all'array restituito cout << "Elementi dell'array: "; for (int i = 0; i < n; i++) { cout << array[i] << " "; } cout << endl; delete[] array; // Deallocazione della memoria return 0; } ``` In questo caso l'allocazione nello heap garantisce che i dati rimangano validi **anche dopo l'uscita dalla funzione**. </ac>