Array a Lunghezza Variabile in C99

Un Array a Lunghezza Variabile (VLA) è un costrutto introdotto a partire dallo standard C99 nel linguaggio C.

Gli array a lunghezza variabile permettono di definire array la cui dimensione è determinata a tempo di esecuzione e non a tempo di compilazione. Tuttavia, la dimensione di un VLA non può essere modificata dopo la sua dichiarazione.

In questa lezione vedremo come definire e utilizzare array a lunghezza variabile in C99. Vedremo quali espressioni sono ammissibili per la lunghezza di un VLA, come definire array multidimensionali e quali sono i limiti di utilizzo.

Il problema della lunghezza fissa di un array

Abbiamo visto nella lezione sugli array monodimensionali statici, ed anche in quella sugli array multidimensionali statici, che la dimensione di un array deve essere nota a tempo di compilazione.

Per indicare la dimensione abbiamo visto che esistono due modi:

  • Indicandola esplicitamente in fase di dichiarazione:

    /* Crea un array di 10 elementi */
    int a[10];
    
  • Ricavandola dal numero di elementi della lista di inizializzazione:

    /* Crea un array di 3 elementi */
    int a[] = {1, 2, 3};
    

In entrambe i casi, però, la lunghezza dell'array viene determinata da un'espressione costante.

Il problema di avere una lunghezza nota a tempo di compilazione è che bisogna fare delle assunzioni sulla dimensione dell'array, che potrebbero non essere sempre verificate. In altri termini, lo sviluppatore con questo approccio deve prevedere quale sia la necessaria lunghezza massima dell'array.

Ma, così facendo, lo sviluppatore potrebbe scegliere una lunghezza troppo piccola, causando un buffer overflow, oppure troppo grande, sprecando memoria.

Per risolvere questo problema esistono due approcci:

  • Il primo è quello di adoperare l'allocazione dinamica della memoria. Questo argomento, però, merita un capitolo a parte e lo studieremo nelle prossime lezioni. Anche perché, per comprenderlo a pieno, è necessario studiare dapprima i puntatori.
  • Il secondo approccio è quello di adoperare array a lunghezza variabile definiti a partire dallo standard C99. Essi sono l'argomento di questa lezione.

Array a lunghezza variabile in C99

A partire dallo standard C99 non è più necessario che la lunghezza o le dimensioni di un array siano note a tempo di compilazione.

Invece, è possibile adoperare, in alcuni casi, delle espressioni non costanti per definire la lunghezza di un array.

Vediamo un esempio. Supponiamo di voler realizzare un programma che calcola la somma di n numeri interi inseriti dall'utente. In questo caso, n è un valore che non è noto a tempo di compilazione ma deve essere chiesto all'utente.

Possiamo realizzare il programma in questo modo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdio.h>

int main(void) {
    int n;

    printf("Inserisci il numero di elementi: ");
    scanf("%d", &n);

    /* Crea un array di n elementi */
    int a[n];

    for (int i = 0; i < n; i++) {
        printf("Inserisci il %d° numero: ", i + 1);
        scanf("%d", &a[i]);
    }

    int somma = 0;

    for (int i = 0; i < n; i++) {
        somma += a[i];
    }

    printf("La somma dei numeri inseriti è: %d\n", somma);

    return 0;
}

Il punto chiave è posto nelle righe 9 e 10. Qui stiamo definendo l'array a come un array di n elementi int. Ma il valore della variabile n non è noto a tempo di compilazione.

Il valore di n sarà impostato soltanto dopo che l'utente avrà inserito il numero di elementi.

Array di questo tipo prendono il nome di Array a lunghezza variabile o Variable Length Array (VLA).

La dimensione di un VLA è determinata a tempo di esecuzione, non a tempo di compilazione. L'importante è che il valore sia noto al momento in cui viene dichiarato l'array.

Il vantaggio principale di adoperare i VLA è che si può risparmiare memoria, in quanto si può allocare esattamente la quantità di memoria necessaria. Non bisogna fare assunzioni sulla dimensione massima dell'array.

Definizione

Array a Lunghezza Variabile in C99

Gli Array a Lunghezza Variabile, chiamati anche Variable Length Array (VLA), sono array la cui dimensione è determinata a tempo di esecuzione e non a tempo di compilazione.

Essi sono stati introdotti nello standard C99.

Il vincolo per il loro utilizzo è che, al momento della loro dichiarazione, l'espressione che definisce la loro lunghezza sia nota.

Espressioni ammissibili per la lunghezza di un VLA

Per definire un VLA non è necessario adoperare una variabile soltanto.

Si possono, infatti, adoperare espressioni più complesse, purché il valore sia noto al momento della dichiarazione dell'array.

Ad esempio, sono ammissibili le seguenti espressioni:

int a[m + n];
int b[2 * m];
int c[(m + n) * 2];

Nelle espressioni di sopra è però necessario che siano rispettate le seguenti condizioni:

  1. Le variabili m ed n devono essere definite e inizializzate prima della dichiarazione dell'array;
  2. Il risultato delle espressioni sia un valore positivo maggiore di zero.
Definizione

Espressioni ammissibili per la lunghezza di un VLA

Per definire un VLA si usa la sintassi seguente:

tipo array[espressione];

Dove espressione è un'espressione tale che:

  1. Le variabili utilizzate nell'espressione siano definite e inizializzate prima della dichiarazione dell'array;
  2. Il risultato dell'espressione sia un valore positivo maggiore di zero.

Array a Lunghezza Variabile Multidimensionali

Gli array a lunghezza variabile possono essere anche multidimensionali.

Ad esempio, possiamo definire una matrice di dimensioni m \times n in questo modo:

double matrice[m][n];

Ovviamente, purché m e n siano variabili definite e inizializzate prima della dichiarazione dell'array.

Inoltre, le dimensioni di un VLA multidimensionale possono essere anche maggiori di due.

Definizione

Array a Lunghezza Variabile Multidimensionali

Un Array a Lunghezza Variabile in C99 può essere anche multidimensionale.

La sintassi per dichiarare un array a lunghezza variabile multidimensionale è la seguente:

tipo array[espressione1][espressione2];

Dove per le due espressioni espressione1 ed espressione2 valgono le stesse regole viste per gli array monodimensionali.

Note sulla Variabilità della Lunghezza

Abbiamo detto che la lunghezza di un VLA deve essere nota al momento della dichiarazione dell'array.

Dobbiamo, però, approfondire alcune questioni legate alla variabilità della lunghezza di un VLA.

La dicitura lunghezza variabile di un array può risultare fuorviante. Può, infatti, portare a pensare che le dimensioni di un array possano essere modificate in qualunque momento dopo che esso sia stato definito. Ma ciò non è vero.

In particolare, la dimensione di un VLA non può essere modificata dopo la sua dichiarazione, anche se essa viene calcolata a tempo di esecuzione.

Per cui, ad esempio, se abbiamo un array a definito come:

int a[n];

e quando raggiungiamo questa definizione n vale 5, la lunghezza di a varrà 5. Anche se in seguito n dovesse cambiare, la dimensione di a rimarrà sempre 5.

Quindi, un nome più adatto per i VLA sarebbe Array a Lunghezza Definita a Tempo di Esecuzione.

Tuttavia, ciò non ne limita l'impiego. Essi sono molto adoperati all'interno di funzioni.

Non abbiamo ancora studiato le funzioni definite dall'utente e le regole di visibilità delle variabili, quindi dobbiamo anticipare alcuni concetti.

Quando si definisce un VLA all'interno di una funzione, la sua dimensione è valida soltanto all'interno di quella funzione. Quando la funzione termina, la memoria allocata per l'array viene liberata.

Per cui, ogni volta che viene invocata quella funzione, l'array cambia dimensione.

Ad esempio:

int esempio(int n) {
    /* Ogni volta che viene chiamata la funzione 'esempio'
     * l'array a cambia dimensione
     */
    int a[n];

    /* Resto del codice */
}

In questo esempio, ogni volta che viene chiamata la funzione esempio, l'array a cambia dimensione in base al valore di n.

In realtà, a voler essere corretti, non è che a cambia dimensione; ciò che accade è che viene creato un nuovo array che si chiama sempre a ma con dimensione diversa.

In ogni caso, studieremo approfonditamente funzioni e visibilità delle variabili nelle prossime lezioni.

Definizione

Variabilità della Lunghezza di un VLA

La dimensione di un VLA è definita al momento della sua dichiarazione e non può essere modificata in seguito.

Limiti degli Array a Lunghezza Variabile

Gli array a lunghezza variabile sono stati introdotti nello standard C99 per risolvere il problema della lunghezza fissa degli array.

Ma hanno alcune limitazioni nel loro impiego.

  • Il primo limite è che non possono essere impiegati come variabili globali.

    Studieremo le variabili globali nelle prossime lezioni. Per ora, sappiamo che le variabili globali sono definite al di fuori di tutte le funzioni e sono visibili in tutto il programma.

    Finora, le variabili che abbiamo adoperato sono variabili locali perché definite all'interno della funzione main.

  • Il secondo è che non possono essere dichiarati come static.

    Non abbiamo ancora studiato le variabili static. Le studieremo nelle prossime lezioni.

  • Non si possono adoperare le liste di inizializzazione.

    Gli array a lunghezza variabile non possono essere inizializzati con una lista di valori.

    Ad esempio, la seguente dichiarazione è errata:

    /* ERRORE */
    int a[n] = {1, 2, 3};
    

    Questo perché la lista di inizializzazione potrebbe avere una dimensione inferiore a quella dell'array.

  • Non si può aggirare la definizione di un VLA con l'istruzione goto:

    /* ERRORE */
    goto fine;
    int a[n];
    fine:
    

    Questo perché, istruzioni successive al punto di destinazione di un goto potrebbero accedere ad a che non è ancora stata definito e quindi allocato.

Conclusioni

Gli array a lunghezza variabile sono una caratteristica introdotta nello standard C99 per risolvere il problema della lunghezza fissa degli array.

Essi permettono di definire array la cui dimensione è determinata a tempo di esecuzione e non a tempo di compilazione.

I VLA sono molto utili all'interno di funzioni, in quanto la loro dimensione è valida soltanto all'interno della funzione stessa.

Tuttavia, essi hanno alcune limitazioni, come il fatto che non possono essere dichiarati come variabili globali, non possono essere dichiarati come static, non possono essere inizializzati con una lista di valori e non possono essere aggirati con l'istruzione goto.

Inoltre, la dimensione di un VLA non può essere modificata dopo la sua dichiarazione.