Allocazione di array

  1. a cosa serve (es. la dimensione dell'array è un dato di input)
  2. come si fa: allocazione+la variabile puntatore è come un array
  3. cosa succede in memoria
  4. esempio: leggere la dimensione e poi gli elementi di un array

Nella pagina sulla allocazione di memoria si è visto come si fa a creare una zona di memoria durante la esecuzione di un programma. Allocare la zona di memoria per una singola variabile non è molto utile: si può infatti creare una variabile ottenendo lo stesso risultato. In effetti, la allocazione di memoria non viene quasi mai fatta, in pratica, per una singola variabile. Capita invece molto spesso di voler allocare la memoria per un intero vettore.

Supponiamo infatti di voler ricevere in input un vettore. Il primo numero che viene dato è la dimensione di questo vettore, mentre i numeri successivi sono gli elementi. Dal momento che la dimensione del vettore viene determinata soltanto in fase di esecuzione del programma, non possiamo usare un array statico, dal momento che la dimensione di un array statico va decisa in fase di scrittura del programma, e non può venire alterata durante l'esecuzione.

La funzione malloc, d'altra parte, ha come argomento il numero di byte da allocare. È quindi possibile chiamare la funzione malloc passando come argomento un valore che viene determinato soltanto in fase di esecuzione, per esempio il valore di una variabile che è stato letto da input. Allocando invece un vettore con la malloc, si puè quindi decidere in esecuzione quanti elementi questo vettore deve contenere.

Per allocare un vettore dinamico, si effettuano i seguenti passi.

  1. se il vettore è di tipo int, si definisce una variabile puntatore a int, per esempio int *a;
  2. si determina la dimensione del vettore (nel nostro caso, si prende la dimensione come input);
  3. si allocano un numero appropriato di byte: se il vettore deve contenere x elementi di tipo t, allora il numero di byte è dato da x * sizeof(t);
  4. si passa il numero di byte da allocare alla funzione malloc, e si mette il risultato nel puntatore;
  5. da questo momento in poi, si può trattare la variabile puntatore come se fosse una variabile di tipo vettore.

La cosa nuova di questo procedimento è il fatto che, una volta che il puntatore contiene l'indirizzo della zona di memoria allocata, si può usare il puntatore stesso come se fosse un vettore statico.

La figura qui accanto mostra lo stato iniziale della memoria. Si noti che esiste una sola variabile puntatore x, e che quindi il programma ha a disposizione solo 4 byte di memoria.

La funzione malloc mette a disposizione del programma una zona di memoria grande quanto il numero che riceve come argomento. In questo caso, la zona di memoria è larga 10*sizeof(int), ossia quanto basta per contenere dieci interi.

Il valore di ritorno della funzione malloc è l'indirizzo iniziale della zona di memoria che è stata riservata per il programma. Nell'esempio, questa zona di memoria inizia dalla locazione AF880 (questo è un numero scritto in esadecimale). Questo numero viene messo nella variabile a.

La freccia è semplicemente una notazione comoda per far vedere quale è l'indirizzo che sta scritto in una variabile puntatore. Occorre però sempre ricordare che si tratta di una semplice notazione, e che quello che effettivamente sta scritto nella variabile puntatore è un numero.

Da questo momento in poi, si può usare a come se fosse un array statico. Per esempio, si possono usare a[0], a[1] ecc. come se a fosse un array statico. In particolare, a[0] è memorizzata nei primi quattro byte della memoria allocata da malloc, a[1] nei successivi quattro, ecc.

Il seguente programma dinamico.c legge un numero in input e alloca un vettore che contiene un numero di elementi pari al numero letto da input. Il vantaggio di questo modo di creare i vettori è che si può decidere la dimensione del vettore durante l'esecuzione del programma. In questo caso, per esempio, è l'utente a scegliere il numero di elementi del vettore, cosa che è impossibile da stabilire prima che il programma venga eseguito.

/*
  Allocazione di un array dinamico.
*/

#include<stdlib.h>

int main() {
  int *a;
  int d;

  printf("Quanti elementi? ");
  scanf("%d", &d);

  a=malloc(d*sizeof(int));

  a[0]=12;
  a[1]=a[0]+40;

  return 0;
}

Si noti che, una volta creata una zona di memoria di dimensione opportuna con la funzione malloc, si può usare il puntatore a come se fosse un vettore di interi. Per esempio, si può memorizzare un valore in a[0], oppure usare il valore di a[4] in una espressione, ecc.

Il programma leggivettore.c è un esempio più completo, in cui si legge da input un numero, si alloca un vettore grande come questo numero, e poi si leggono tutti i suoi elementi.

/*
  Legge un vettore il cui numero di elementi
viene dato da input.
*/

#include<stdlib.h>

int main() {
  int *a;
  int d;
  int i;

			/* chiede il numero di elementi */
  printf("Quanti elementi? ");
  scanf("%d", &d);

			/* alloca il vettore */
  a=malloc(d*sizeof(int));

			/* legge gli elementi */
  for(i=0; i<=d-1; i++) {
    printf("Dammi l'elemento a[%d]: ", i);
    scanf("%d", &(a[i]));
  }

			/* stampa gli elementi del vettore */
  for(i=0; i<=d-1; i++)
    printf("a[%d] vale %d\n", i, a[i]);

  return 0;
}