Skip to content
This repository has been archived by the owner on Jun 16, 2023. It is now read-only.

Commit

Permalink
fine lezione 20
Browse files Browse the repository at this point in the history
  • Loading branch information
mc-cat-tty committed Dec 7, 2021
1 parent 274d8dd commit 413ff20
Show file tree
Hide file tree
Showing 5 changed files with 256 additions and 0 deletions.
155 changes: 155 additions & 0 deletions 20_memoria_dinamica.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
# Memoria dinamica
Spesso non si conosce quanti dati memorizzare a tempo di scrittura del programma
=> vogliamo allocare memoria a tempo di esecuzione -> allocazione dinamica della memoria

Quando lancio un processo il SO riserva al programma una certa quantità di memoria. Questo spazio allocato in modo contiguo è suddiviso in segmenti.
Uno di questi segmenti è chiamato _heap_ (oppure memoria livera o memoria dinamica). All'interno di questo segmento posso allocare memoria in modo arbitrario a tempo di esecuzione, pur rispettando la dimensione fornita dal SO.

I: l'indirizzo di memoria del primo byte dell'heap è costante durante tutta l'esecuzione del programma

## Oggetti dinamici
Utilizzeremo array dinamici -> possono cambiare dimensione a tempo di esecuzione.

In C standard si usa `malloc()`

In Cpp useremo `new`:
```
new <nome_tipo> [<num_elementi>];
```
<num_elementi> può essere una espressione qualsiasi
W: il vettore sopra dichiarato non ha nome (identificatore)

`new` alloca solamente un certo spazio in memoria (heap)

Return value: indirizzo di memoria del primo elemento -> indirizzo dell'area di memoria riservata

## Puntatore
Dobbiamo memorizzare questo indirizzo in una variabile per poter accedere all'area di memoria allocata mediante un identificatore

Def: un oggetto di tipo puntatore è una variabile che ha per valore un indirizzo di memria (numero naturale)

### Dichiarazione
```
[const] <tipo_oggetto_puntato> * [const] <identificatore> [= <indirizzo>];
```

Il primo _const_ va inserito se il puntatore fa riferimento ad un oggetto non modificabile
Il secondo _const_ va inserito se il valore del puntatore non deve essere modificato dopo l'inizializzazione

Esempio: allocaizone di un array di N elementi
```
int *p = new int[N];
```

### Accesso agli elementi
=> identico a quello degli array -> operatore parentesi quadri

W: non vengono effettuati controlli sulla dimensione e l'accesso in memoria come visto fin'ora

I: `htop` o `top` per visualizzare l'utilizzo di memoria di un processo

### Valori, operazioni, tempo di vita
Il puntatore nullo (NULL) è un puntatore che contiene 0 come valore.

Prevede che le operazioni siano correlate al tipo di dato a cui si punta.

I puntatori seguono le stesse regole su visibilità e tempo di vita di tutte le altre variabili (ma non l'area di memoria allocata dall'operatore new)

### Tempo di vita di un array dinamico
Non confondere un puntatore con l'array ad esso associare.

Una volta allocato un array dinamico esiste fino alla fine del programma (anche quando scompaiono tutti i riferimenti ad esso, compreso il puntatore) oppure fino a che non viene _deallocata_ l'area di memoria.

### Dealocazione area di memoria
=> per liberare un area di memoria

```
delete[] <indirizzo_da_deallocare>
```

W: perdendo qualsiasi riferimento ad un array dinamico la memoria non può essere deallocata, quindi rimane occupata ma non utilizzabile fino alla terminazione del procesos

I: l'operatore `delete` può essere applocato anche al puntatore nullo

W: un errore di gestione della memoria si verifica quando viene deallocata un'area di memoria mai allocata nell'heap oppure già allocata

Hint: utilizza sempre il delete alla fine di una funzione/del main per evitare _memory leak_ -> errore difficile da trovare a meno che non si controlli in continuazione la memory footprint del programma.

### Passaggio per riferimento alle funzioni
```
[const] <nometipo>< <identificatore> []
// oppure
[const] <nometipo> * <identificatore>
```

Gli array sono sempre passati per riferimento

Il parametro formale contiene una copia dell'indirizzo dell'array (quindi questo tipo di passaggio è per valore)

Il nome dell'array è equivalente al nome del primo elemento

### Ritorno da parte di funzioni
Posso allocare un array dinamico dentro ad una funzione e farmi ritornare il puntatore ad esso, dove sono memorizzati i valori di ritorno. Dovrò ricordarmi di deallocare l'area di memoria allocata nella funzione.

Alla fine della funzione viene deallocato solo il *puntatore* e non la memoria da esso puntata.

I: con "dinamico" non si intende che la dimensione dell'array viene conosciuta solamente a tempo di esecuzione del programma, non che è possibile modificarne la dimensione dinamicamente.

Quando ritorno un vettore ho anche bisogno della sua dimensione. I metodi sono:
- struct con 2 campi
- passo dim per riferimento e ritorno il puntatore al vettore
- passo il puntatore al vettore per riferimento e ritorno la dimensione

### Riferimento ad un puntatore
```
[const] <nometipo>* & <identificatore> = <nomeoggetto>
```

La variabile così definita è un riferimento a puntatore e può tornare utile per modificare un oggetto di tipo puntatore.

Esempio:
```
void creaVett(int* &v, int &num) { ... }
```

## Dangling reference
Oppure pending pointer

Quando si ha un puntatore che punta ad un'area di memoria che non è stata allocata oppure che è stata deallocata. Il comportamento del programma diventa non deterministico (in base alla disposizione e quantità della memoria il programma funziona oppure no)

## Esaurimento memoria
In assenza di memoria libera disponibile l'operatore new fallisce e viene generata un'eccezione

### Memory leak
=> problema derivante dalla mancata deallocazione degli oggetti dinamici, che comporta un esaurimento della RAM/swap totale

Il memory leak può verificarsi anche all'interno di una funzione

## Tipo di array dinamici
### Strighe dinamiche
```
char * const str = new char[11];
```

### Array dinamico di strutture
```
struct persona { ... };
persona* const t = new persona[dim];
```

### Matrici dinamiche
=> array dinamico di array (statico, anche se in realtà funziona anche con gli array din di array din)

```
int (*p) [10] = new int[n][10];
```
Passaggio a funzione:
```
[const] <nometipo> (*<identificatore>) [<espr_costante>];
```
### Esercizi
// TODO: percorso_semplificato.cpp
// TODO: percorso.cpp
// TODO: pila.cpp

24 changes: 24 additions & 0 deletions esercitazioni/crea_copia_arra.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#include <iostream>

using namespace std;

void crea_copia_array(const int v1[], const int dim, int* &v2) {
v2 = new int[dim];
for (int i=0; i<dim; i++)
v2[i] = v1[i];
}

int main() {
const int N = 3;
int vettore[N] = {10, 20, 30};
int *dest;

crea_copia_array(vettore, N, dest);

for (int i=0; i<N; i++)
cout << dest[i] << " ";

delete[] dest;

return 0;
}
51 changes: 51 additions & 0 deletions esercitazioni/crea_riempi_distruggi_array.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#include <iostream>
#include <cstring>

using namespace std;

int main() {
int n; // dimensione array dinamico scelta dall'utente
cout << "Dimensione array: ";
cin >> n;

int *a = new int[n]; // allocazione

// riempimento
for (int i=0; i<n; i++) {
cout << "Elemento numero "<<i+1<<" : ";
cin >> a[i];
}

// stampa
cout << endl << "STAMPA" << endl;
for (int i=0; i<n; i++) {
cout << "Elemento numero "<<i+1<<" : ";
cout << a[i] << endl;
}

int n2; // nuova dimensione
do {
cout << "Quanti elementi vuoi conservare? ";
cin >> n2;
} while (!(n2>=0 && n2<=n));

int *a2 = new int[n2];

// copia
memcpy(a2, a, sizeof(int)*n2);

// deallocazione vecchio array
delete[] a;

// stampa
cout << endl << "STAMPA" << endl;
for (int i=0; i<n2; i++) {
cout << "Elemento numero "<<i+1<<" : ";
cout << a2[i] << endl;
}

// deallocazione array nuovo
delete[] a2;

return 0;
}
Empty file.
26 changes: 26 additions & 0 deletions esercitazioni/primo_arr_dyn.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#include <iostream>

using namespace std;

int main() {
int n;
cout << "Dimensione del vettore: ";
cin >> n;

int *a = new int[n];

for (int i=0; i<n; i++) {
cout << "Elemento "<<i+1<<" : ";
cin >> a[i];
}

for (int i=0; i<n; i++) {
cout << "Elemento "<<i+1<<" : ";
cout << a[i] << endl;
}

// Utilizza htop per visualizzare
// l'utilizzo della memoria

return 0;
}

0 comments on commit 413ff20

Please sign in to comment.