Logo

Marco Cantù
Essential Pascal


Tradotto dall'inglese da: Paolo Rossi

Capitolo 8
La memoria

Nota dell'autore: Questo capitolo parlera' della gestione della memoria, delle varie aree di memoria e introdurra' gli array dinamici. Provvisoriamente solo quest'ultima parte e' diponibile.

Gli Array Dinamici in Delphi

Tradizionalmente, il linguaggio Pascal ha sempre avuto array a dimensione fissa. Quando si dichiara un tipo di dato usando la dichiarazione di array, bisogna specificare il numero di elementi dell'array. Come sanno i programmatori esperti, ci sono alcune tecniche che si possono usare per gli array dinamici, tipicamente usando puntatori allocando e liberando manualmente la memoria richiesta.

Delphi 4 introduce un'implementazione molto semplice degli array dinamici, sul modello delle long string appena descritte. Come le long string, gli array dinamici sono allocati dinamicamente e reference counted, ma non offrono la tecnica copy-on-write. Questo non e' un grosso problema, visto che si puo' deallocare un array ponendo la sua vriabile a nil.

Ora si puo' semplicemente dichiarare un array senza specificare il numero degli elementi e quindi allocarlo con una data dimensione usando la procedura SetLength. La stessa procedura puo' anche essere usata per ridimensionare un array senza perdere il suo contenuto. Ci sono anche altre procedure di gestione delle stringhe, come la procedura Copy, che si possono usare sugli array.

Di seguito c'e' un frammento di codice, che sottolinea il fato che bisogna dichiarare e allocare memoria per l'array prima di poterlo usare:

procedure TForm1.Button1Click(Sender: TObject);
var
  Array1: array of Integer;
begin
  Array1 [1] := 100; // error
  SetLength (Array1, 100);
  Array1 [99] := 100; // OK
  ...
end;

Se si dichiara soltanto il numero di elementi dell'array, l'indice partira' sempre da zero. Gli array generici in Pascal permettono indici non-interi e limiti inferiori non-zero, due caratteristiche che sono precluse agli array dinamici. Per sapere lo status di un array dinamico, si possono usare le funzioni Length, High e Low, come per ogni altro array. Per gli array dinamici, tuttavia, la funzione Low ritorna sempre zero e la funzione High ritorna sempre la lunghezza meno 1. Questo fatto implica che per un array vuoto High ritorna -1 (che e' uno strano valore, piu' piccolo del valore ritornato da Low!).

Figura 8.1: Il Form dell'esempio DynArr

Dopo questa sorta di introduzione, posso mostrare un semplice esempio, chiamato DynArr mostrato in Figura 8.1. E' davvero molto semplice siccome non c'e' nulla di veramente complesso sugli array dinamici. Usero' questo esempio anche per mostrare alcuni piccoli errori che i programmatori possono commettere. Il programma dichiara due array globali e inizializza il primo nel gestore d'evento OnCreate:

var
  Array1, Array2: array of Integer;

procedure TForm1.FormCreate(Sender: TObject);
begin
  // allocate
  SetLength (Array1, 100);
end;

Questo mette tutti i valori a zero. Questo codice d'inizializzazione rende possibile la lettura e la scrittura dei valori dell'array immediatamente, senza paura di errori di memoria. (Assumendo che non si tenta di accedere ad elementi oltre al limite superiore dell'array). Per una migliore inizializzazione, il programma ha un pulsante che scrive in ogni cella dell'array:

procedure TForm1.btnFillClick(Sender: TObject);
var
  I: Integer;
begin
  for I := Low (Array1) to High (Array1) do
    Array1 [I] := I;
end;

Il pulsante Grow, permette di modificare la dimensione della'rray senza perderne il contenuto. Si puo' verificare cio' testando il valore ritornato dal pulsante Get dopo aver premuto il pulsante Grow:

procedure TForm1.btnGrowClick(Sender: TObject);
begin
  // grow keeping existing values
  SetLength (Array1, 200);
end;

procedure TForm1.btnGetClick(Sender: TObject);
begin
  // extract
  Caption := IntToStr (Array1 [99]);
end;

Il solo codice leggermente piu' complesso e' nell'evento OnClick del pulsante Alias. Il programma copia un array in un altro con l'operatore := che in effetti crea un alias (una nuova variabile che punta allo stesso array in memoria), A questo punto, tuttavia, se si modifica uno degli array, anche l'altro risulta modificato, siccome entrambi puntano alla stessa area di memoria:

procedure TForm1.btnAliasClick(Sender: TObject);
begin
  // alias
  Array2 := Array1;
  // change one (both change)
  Array2 [99] := 1000;
  // show the other
  Caption := IntToStr (Array1 [99]);

Il metodo btnAliasClick compie due altre operazioni. La prima e' un test di uguaglianza sugli array. Questo non controlla gli elementi reali della struttura, piuttosto l'area di memoria a cui puntano gli array, controllando se le variabili sono due alias allo stesso array in memoria:

procedure TForm1.btnAliasClick(Sender: TObject);
begin
  ...
  if Array1 = Array2 then
    Beep;
  // truncate first array
  Array1 := Copy (Array2, 0, 10);
end;

La seconda e' una chiamata alla funzione Copy, la quale non solo sposta i dati da un array all'altro, ma sostituisce anche il primo array con uno nuovo creato dalla funzione. L'effetto e' che la variabile Array1 adesso punta ad un array di 11 elementi, cosi' che premendo il pulsante Get o il pulsante Set si avra' un errore di memoria e quindi verra' generata un'accezione (a meno che non si abbia il range-checking messo ad off, nel qual caso l'errore rimane, ma l'eccezione non e' visualizzata). Il codice del pulsante Fill continua a lavorare bene anche dopo questo cambiamento, siccome gli elementi da modificare sono determinati usando i limiti attuali dell'array.

Conclusioni

Questo capitolo temporaneamente copre solo gli array dinamici, certamente un elemento importante per la gestione della memoria, ma solo una porzione dell'intera gestione. Seguira' altro materiale in futuro.

La struttura della memoria descritta in questo capitolo e' tipica della programmazione Windows, un argomento che verra' introdotto nel prossimo capitolo.

Prossimo Capitolo: La programmazione Windows

© Copyright Marco Cantù, Wintech Italia Srl 1995-2000