Marco Cantù
|
Capitolo 8
|
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.
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.
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.
© Copyright Marco Cantù, Wintech Italia Srl 1995-2000