Array Multidimensionali statici in linguaggio C

Gli array multidimensionali statici in C sono una forma di array bidimensionali o più dimensioni, che possono essere utilizzati per rappresentare dati in una forma tabellare, come ad esempio una matrice di numeri.

Essendo statici, per gli array multidimensionali è necessario che le dimensioni siano note a tempo di compilazione e non possono cambiare durante l'esecuzione del programma.

In questa lezione, esploreremo come dichiarare e utilizzare gli array multidimensionali statici in C, fornendo esempi pratici per aiutare i lettori a comprendere meglio questa funzionalità.

Array Multidimensionali statici

Nella lezione precedente abbiamo studiato gli array statici ad una singola dimensione. Abbiamo visto che la dimensione non può essere modificata durante l'esecuzione di un programma ossia in maniera dinamica.

In questa lezione ci concentreremo sugli array statici multidimensionali.

Un array può avere più di una dimensione. Ad esempio la dichiarazione che segue crea un array a due dimensioni:

int x[4][6];

Da un punto di vista matematico, un array di questo tipo può essere visto come una matrice. In questo caso abbiamo dichiarato un array di interi di 4 righe e 6 colonne.

Come per il caso monodimensionale, anche per il caso multidimensionale l'indicizzazione degli elementi delle righe e delle colonne parte da zero. Per chiarire meglio possiamo osservare la figura che segue, che rappresenta un array con n righe e m colonne:

Array Multidimensionale Statico
Figura 1: Array Multidimensionale Statico

Per accedere ad un elemento sito alla riga i e alla colonna i dobbiamo usare la sintassi seguente:

x[i][j];

Nell'espressione di sopra, x[i] indica l'intera riga i. Per cui l'espressione x[i][j] seleziona l'elemento j nella riga i.

Nota

Un errore comune nell'utilizzo degli array multidimensionali in C è quello di scrivere la seguente espressione per accedere ad un elemento:

/* Errore: */
x[i, j];

Anche se questa espressione è corretta sintatticamente e, infatti, il compilatore non segnala alcun errore, nella realtà stiamo usando l'operatore virgola.

Per cui l'espressione di sopra viene interpretata come:

x[j];

Quindi stiamo selezionando l'intera riga j!

Gli array in C possono avere anche più di due dimensioni. Ad esempio, possiamo creare array a 3 dimensioni o 4 dimensioni come nell'esempio che segue:

/* Array a 3 dimensioni */
int x[3][3][4];

/* Array a 4 dimensioni */
int y[2][2][3][4];
Definizione

Array Multidimensionali statici

Un Array Multidimensionale statico è un array a più dimensioni il cui numero di elementi non può variare durante l'esecuzione del programma.

La sintassi per dichiarare un array di questo tipo è la seguente:

tipo nome[dimensione_1][dimensione_2][dimensione_3];

Disposizione in memoria

Sebbene possiamo visualizzare un array multidimensionale come una tabella o matrice, in realtà in memoria gli array multidimensionali non sono memorizzati in questo modo.

Infatti, il linguaggio C memorizza gli array per riga. Ciò significa che un array sarà sempre memorizzato come una sequenza di valori in memoria dove vi sarà prima la riga 0, poi la riga 1 e così via.

Ad esempio, prendiamo il seguente array:

int x[3][2];

L'array x ha 3 righe e 2 colonne. Per cui la sua disposizione in memoria sarà:

Disposizione dell'array multidimensionale dell'esempio
Figura 2: Disposizione dell'array multidimensionale dell'esempio

Utilizzo di cicli

Analogamente al caso degli array monodimensionali, anche per gli array multidimensionali i cicli for sono utilizzati per processare gli elementi contenuti in essi. In particolare, si utilizzano cicli for innestati.

Prendiamo un semplice esempio matematico. Vogliamo inizializzare un array multidimensionale per poterlo utilizzare come una matrice identità. Una matrice identità è una matrice per cui tutti gli elementi sono uguali a zero tranne quelli sulla diagonale principale che invece valgono 1.

Per poter inizializzare un array in questo modo e trasformarlo in una matrice identità dobbiamo visitare tutti gli elementi dell'array e impostarli a zero se il numero di riga è diverso dal numero di colonna, o impostarli a uno in caso contrario.

Per far questo possiamo usare due cicli for innestati: un primo ciclo esterno che itera sulle righe ed un secondo ciclo interno che itera sulle colonne.

Possiamo scrivere il codice in questo modo:

/* Costante che rappresentano il numero
   di righe e colonne */
const int N = 10;

/* La matrice da inizializzare */
double M[N][N];

/* Indici di riga e colonna */
int row;
int col;

/* Ciclo esterno per le righe */
for (row = 0; row < N; ++row) {
    /* Ciclo interno per le colonne */
    for (col = 0; col < N; ++col) {
        if (row == col) {
            M[row][col] = 1.0;
        }
        else {
            M[row][col] = 0.0;
        }
    }
}

Inizializzazione

Anche per gli array multidimensionali possiamo usare le liste di inizializzazione. In questo caso, tuttavia, dobbiamo innestare più liste di inizializzazione l'una dentro l'altra.

Ad esempio, volendo creare una matrice identità di 4 righe e 4 colonne possiamo scrivere il seguente codice:

double M[4][4] = {{1.0, 0.0, 0.0, 0.0},
                  {0.0, 1.0, 0.0, 0.0},
                  {0.0, 0.0, 1.0, 0.0},
                  {0.0, 0.0, 0.0, 1.0}};

Ciascuna lista di inizializzazione interna fornisce i valori per una riga dell'array.

Analogamente al caso monodimensionale, il linguaggio C fornisce vari modi per abbreviare la lunghezza delle liste di inizializzazione.

Se la lista di inizializzazione contiene meno righe tutti le righe rimanenti avranno gli elementi inizializzati a zero. Ad esempio:

/* L'array è 3x3
   La lista di inizializzazione è 2x3 */
int x[3][3] = {{1, 2, 3},
               {4, 5, 6}};

In questo caso l'array conterrà i seguenti elementi:

\begin{array}{ccc} 1 &amp; 2 &amp; 3 \\ 4 &amp; 5 &amp; 6 \\ 0 &amp; 0 &amp; 0 \end{array}

Se una lista interna ha meno elementi tutti gli elementi di quella riga saranno inizializzati a zero. Ad esempio:

/* L'array è 3x3 */
int x[3][3] = {{1, 2, 3}, /* Riga 1: 3 elementi */
               {4, 5},    /* Riga 2: Solo 2 elementi */
               {6}};      /* Riga 3: Solo 1 elemento */

In questo caso l'array conterrà i seguenti elementi:

\begin{array}{ccc} 1 &amp; 2 &amp; 3 \\ 4 &amp; 5 &amp; 0 \\ 6 &amp; 0 &amp; 0 \end{array}

Infine, una lista di inizializzazione può anche omettere le liste interne. In altre parole, la lista di inizializzazione può anche essere una lista semplice. Il compilatore provvederà a riempire una riga con gli elementi a disposizione. Terminata una riga, riempirà le altre con gli elementi rimanenti.

Ad esempio, prendiamo un array di 3 righe e 3 colonne. In totale avrà 9 elementi, per cui possiamo inizializzarlo in questo modo:

int x[3][3] = {1, 2, 3,
               4, 5, 6,
               7, 8, 9};

In questo caso abbiamo usato una lista di inizializzazione semplice che è del tutto equivalente a scrivere il codice seguente:

int x[3][3] = {{1, 2, 3},
               {4, 5, 6},
               {7, 8, 9}};

Anche in questo caso valgono le stesse regole per il caso in cui vi siano meno elementi. Ad esempio, se per l'array 3x3 usiamo una lista con 7 elementi:

int x[3][3] = {1, 2, 3,
               4, 5, 6,
               7};

L'array conterrà i seguenti elementi:

\begin{array}{ccc} 1 &amp; 2 &amp; 3 \\ 4 &amp; 5 &amp; 6 \\ 7 &amp; 0 &amp; 0 \end{array}
Nota

Evitare liste di inizializzazione monodimensionali con Array Multidimensionali statici

Conviene sempre evitare di usare liste di inizializzazione monodimensionali con array multidimensionali. Infatti, l'inserimento di elementi in più o in meno per svista o errore può comportare lo slittamento di tutti gli altri valori di inizializzazione.

Per questo motivo, alcuni compilatori producono un messaggio di warning in questa situazione.

Inizializzatori designati in C99

Lo standard del linguaggio C99 permette di usare gli inizializzatori designati anche per gli array multidimensionali.

Utilizzando gli inizializzatori designati possiamo semplificare la dichiarazione della matrice identità dell'esempio di sopra in questo modo:

/* Creazione di una matrice identità 4x4
   con gli inizializzatori designati */
double M[4][4] = {[0][0] = 1.0,
                  [1][1] = 1.0,
                  [2][2] = 1.0,
                  [3][3] = 1.0};

In Sintesi

In questa lezione abbiamo studiato gli array multidimensionali, ossia a più di una dimensione in linguaggio C. Ci siamo concentrati su quelli statici per cui le dimensioni non possono variare durante l'esecuzione del programma.

Abbiamo visto che è possibile utilizzare le liste di inizializzazione innestate per inizializzarli e abbiamo visto i cicli for innestati per poter accedere agli elementi.

Nella pratica comune, tuttavia, gli array multidimensionali statici non sono molto utilizzati in linguaggio C perché sorge spesso l'esigenza di poter modificare a tempo di esecuzione la dimensione di tali array. Per poter lavorare con array dinamici, tuttavia, bisogna comprendere bene come funzionano i puntatori che studieremo nelle lezioni successive.