Puntatori ed allocazione dinamica della memoria

Puntatori
Dichiarazione di variabili puntatore
Assegnazione: operatore unitario prefisso &
Dereferenziazione: operatore unitario prefisso *
Relazione tra puntatori ed array
Allocazione dinamica della memoria (scalari)
Allocazione dinamica della memoria (array)
Allocazione dinamica della memoria (matrici)
 
 


Puntatori
Dato un tipo di variabile, ad es. double, il tipo double * si chiama puntatore (a double, nell'esempio), ed e' una variabile che assume, come valore, l'indirizzo di memoria in cui e' contenuta una variabile del tipo corrispondente (double, nell'esempio), cioe' e' una variabile il cui valore punta ad una variabile.
Nota: per definire una variabile puntatore bisogna definire il tipo di variabile a cui esso punta; un puntatore a double ed un puntatore ad int, ad esempio, sono variabili di tipo differente, come double e' un tipo differente da int.


Dichiarazione di variabili puntatori
Le variabili puntatore si dichiarano utilizzando il simbolo *, nel seguente modo:

    double *dp;                        // puntatore a double
    unsigned short int *usip;     // puntatore a unsigned short int
    string *sp;                         // puntatore a variabile di tipo string


Assegnazione: operatore unitario prefisso &
L'operatore unitario & posto davanti al nome di una variabile serve ad indicare l'indirizzo occupato dalla variabile in questione. In questo modo e' possibile assegnare un valore ai puntatori:

    double d;
    double * pd = &d;        // il puntatore a double pd assume come valore l'indirizzo della variabile d

E' possibile assegnare ad un puntatore il valore di un altro puntatore:

    int i;
    int * ip1 = &i;            // il puntatore ip1 punta alla variabile i
    int * ip2 = ip1;          // il puntatore ip2 punta anch'esso alla variabile i

Quando si dichiara un puntatore senza contemporaneamente inizializzare il suo valore, e' opportuno inizializzarlo ad un valore invalido, che per i puntatori si chiama NULL:

    char * cp = NULL;


Dereferenziazione: operatore unitario prefisso *
L'accesso ad una variabile tramite un puntatore si chiama dereferenziazione. Per fare cio' si utilizza l'operatore unitario prefisso *:

    double d;
    double * dp = &d;        // dp punta alla double d

    *dp = 12.0;                  // questo e' equivalente a scrivere d = 12.0, ma uso il puntatore
    cout << *d << endl;    // stampo su terminale il valore di d, usando il puntatore

a tutti gli effetti, *dp equivale a scrivere d


Relazione tra puntatore ed array
Il nome della variabile array (senza le []) puo' essere trattato come un puntatore al primo elemento dell'array.

    int v[3] = {2, 4, 6};
    cout << *v << endl;    // v punta al primo elemento, quindi *v e' il primo elemento

In modo simmetrico, un puntatore a cui sia assegnato l'indirizzo del primo elemento di un array, puo' essere utilizzato come una variabile array:

    int *p = NULL;
    p = v;                    // equivalente a p = & v[0]; (indirizzo del primo elemento)
    p[2] = 3.0;           // accesso al terzo elemento dell'array tramite un puntatore


Allocazione dinamica della memoria (scalari)
Per allocare dinamicamente la memoria, per contenere ad esempio variabili o array non dichiarati,
si utilizza l'istruzione new. Questa istruzione assegna ad un puntatore l'indirizzo della memoria allocata.
Assieme a new, va specificato il tipo di oggetto che si vuole allocare.

    double * p = NULL;
    p = new double;                // p assume il valore della zona di memoria allocata per una double
    *p = 23.4;                         // assegno un valore alla zona di memoria allocata

Per deallocare la memoria allocata dinamicamente si usa l'istruzione delete:

    delete p;            // libero la memoria allocata precedentemente con new


Allocazione dinamica della memoria (array)
Per allocare dinamicamente un array, va utilizzata l'istruzione new specificando il tipo e la dimensione dell'array. L'istruzione assegna ad un puntatore l'indirizzo del primo elemento allocato. La dimensione dell'array puo' essere una variabile, e anche una operazione algebrica tra variabili.

    int n=3, m=4;
    double * p = new double[n+m];    // alloco un array di n+m elementi

Per deallocare un array, si usa l'istruzione delete[]:

    delete[] p;

Nota: l'istruzione delete p dealloca solo la memoria occupata dal primo elemento dell'array!


Allocazione dinamica della memoria (matrici)
L'allocazione dinamica di una matrice nXm si attua in due parti: prima si alloca un array di n puntatori, e per ciascuno di questi si alloca un array di m variabili (il contenuto di ciascuna riga della matrice):

    int nrow, ncol;
    double **matrix;

    matrix = new double*[nrow];            // alloco l'array di puntatori a double
    for(int i=0; i<nrow; ++i)
        matrix[i] = new double[ncol];        // per ogni riga alloco l'array di double per gli elementi della riga

La deallocazione di una matrice va fatta secondo l'ordine inverso rispetto alla allocazione:

    for(int i=0; i<nrow; ++i)
        delete[] matrix[i];
    delete[] matrix