next up previous
Next: Operazioni su matrici Up: Memorizzazione di una matrice Previous: Memorizzazione di una matrice

Vettore di puntatori

La soluzione più conveniente è invece quella di memorizzare la matrice ``a fette'', allocando un blocco di memoria per ciascuna riga. In questo modo ci serve un vettore di puntatori: ciascun puntatore punta al blocco contenente una certa riga. Gli svantaggi sono che l'allocazione diventa più complicata, e per accedere ad un elemento sono necessarie due dereferenziazioni.

All'inizio del programma si dichiara che a è un vettore di puntatori. Poiché non sappiamo quanto è lungo questo vettore, in realtà si dichiara a come

    double **a;
e nel momento in cui l'ordine n diventa disponibile si alloca la memoria. a è un vettore, e ciascun elemento a[i] punta ad una zona di memoria dove si memorizza (sotto forma di vettore) la i-esima riga della matrice.

Appena, nel corso del programma, si conosce l'ordine n della matrice, si può procedere con l'effettiva allocazione della memoria. Si comincia con allocare la memoria per il vettore di puntatori:

    a = calloc(n, sizeof *a);
dopodiché si alloca la memoria per ciascuna riga:
    for (i=0; i<n; i++)
        a[i] = calloc(n, sizeof **a);
In un codice reale, bisogna controllare se le allocazioni hanno avuto successo (la calloc() restituisce 0 se non trova la memoria).

Adesso si può accedere all'elemento (i+1,j+1) della matrice con la notazione naturale a[i][j]. Essa significa: il j-esimo elemento del vettore a[i], ovvero del vettore puntato dall'i-esimo elemento del vettore a. La scrittura a[i][j] è quindi equivalente a *(*(a+i)+j) (la traduzione viene fatta dal compilatore - notare che un accesso alla matrice richiede adesso due dereferenziazioni, indicate dall'operatore unario `*'). Ricordarsi che l'elemento in altro a sinistra è a[0][0] e quello in basso a destra a[n-1][n-1].

Si osservi che se la matrice è simmetrica, oppure triangolare, si può facilmente allocare metà memoria, memorizzando ad esempio solo la metà inferiore della matrice. A tale scopo basta richiedere i posizioni di memoria, anziché n, quando si inizializza ciascun a[i].

Quando la matrice non serve più, la memoria va rilasciata attraverso la funzione free(). La liberazione va fatta in modo simmetrico all'allocazione.

Un frammento completo di programma è dunque il seguente

    #include <stdlib.h>              /* Per calloc() e free()  */
    typedef double **matrice;        /* (abbreviazione)        */
    matrice a;                       /* Dichiara la matrice    */

    ...                /* Trova il valore di n */

    a = calloc(n, sizeof *a);        /* Alloca vettore di puntatori */
    if ( !a ) { ... }
    for (i=0: i<n: i++) {
        a[i] = calloc(n, sizeof *a)       /* Alloca i-esima riga */
        if ( !a[i] ) { ... }
    }

    ...                /* Usa la matrice */

    for (i=0; i<n; i++)              /* Libera la memoria */
        free(a[i]);
    free(a);

In un codice reale bisogna fare molta attenzione alle condizioni di errore: ad esempio, se fallisce l'allocazione della i-esima riga (la funzione calloc restituisce 0), si deve liberare la memoria già allocata per le righe precedenti.

Si può evitare una gestione accurata delle free solo per programmi molto brevi: alla fine dell'esecuzione infatti tutta la memoria allocata viene automaticamente rilasciata.


next up previous
Next: Operazioni su matrici Up: Memorizzazione di una matrice Previous: Memorizzazione di una matrice
Daniele Finocchiaro
1998-11-13