Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial
  • Lezione 16 di 43
  • livello principiante
Indice lezioni

I tipi (parte terza)

Conclusione della rassegna dei tipi di dati: Puntatori e tipi pointer, tipi procedurali, tipi variant.
Conclusione della rassegna dei tipi di dati: Puntatori e tipi pointer, tipi procedurali, tipi variant.
Link copiato negli appunti

Puntatori e tipi pointer

Probabilmente saprete già cosa sono i puntatori. La programmazione ad oggetti, tipica
oramai di tutti i più recenti linguaggi ad alto livello, fa grande uso dei puntatori. Un
puntatore è una variabile che indica un indirizzo di memoria. Si dice che un puntatore
punta ad una variabile quando contiene l'indirizzo in memoria della variabile stessa.
Conseguentemente il puntatore fa riferimento alla memoria ed ai dati di quella variabile.
Per i tipi strutturati, come array, record ecc., un puntatore ad una variabile di questo
tipo punta all'indirizzo del primo elemento della struttura.

Anche i puntatori hanno un tipo che serve ad indicare il tipo di dati a fanno
riferimento. Esiste comunque un tipo generico di puntatore, il tipo pointer che
può puntare a qualsiasi tipo di dato. La memoria occupata dai puntatori equivale a 4
byte.

Per definire un puntatore ad un tipo si utilizza la sintassi

Type NomeTipoPuntatore : ^Tipo;

per esempio

Type PInteger : ^Integer;

definisce un tipo puntatore puntatore ad un tipo integer.

L'operatore "^" può essere utilizzato in due differenti modi: uno è quello
che abbiamo già visto, l'altro è utilizzarlo per dereferenziare il puntatore, cioè per
accedere al valore contenuto all'indirizzo di memoria indicato dal puntatore stesso.

Per esempio, avendo una variabile I di tipo PInteger che punta ad un indirizzo
contenente il valore 2, I^ restituisce 2.

Esiste anche un altro operatore ed è "@" che viene utilizzato per ottenere
l'indirizzo di una variabile.

Sono definite anche alcune funzioni standard dell'Object Pascal per operare con i
puntatori e sono: New, GetMem, Addr, Ptr. Le prime due
funzioni assegnano ad un puntatore esistente un indirizzo di memoria (vengono utilizzate
per creare dinamicamente delle variabili), le altre due restituiscono puntatori
rispettivamente ad un indirizzo o ad una variabile.

La costante speciale Nil, già vista in precedenza, serve ad indicare
che un puntatore non fa riferimento a nulla e può essere assegnata a qualsiasi tipo di
puntatore.


Tipi procedurali

I tipi procedurali vengono utilizzati per trattare le funzioni e le procedure
come valori che possono essere assegnati alle variabili o ad altre procedure o funzioni.

Per dichiarare un tipo procedurale, per un certo tipo di funzione o procedura,
basta prendere la dichiarazione della procedura o funzione ed eliminarne l'identificatore
dopo la parola procedure o function.

Ad esempio, avendo una funzione dichiarata come

Function Bisestile(Anno : Integer) : Boolean;

potremmo dichiarare un tipo

Type TBisestileFunc = Function (Anno : Integer) :
Boolean;

dichiarare una variabile

Var FBsisestile : TBisestileFunc;

ed assegnarle la funzione Bisestile come segue

FBisestile := Bisestile;

Quelli visti possono essere definiti tipi procedurali semplici ma esiste un altro tipo
procedurale, il tipo puntatore a metodo. Vengono utilizzati per fare riferimento
a metodi di classi e per la loro definizione occorre aggiungere le parole chiave "of
object
" dopo la dichiarazione del tipo.

Type TNotifyEvent = Procedure (Sender : TObject) of object;

In questo esempio si è definito un tipo TNotifyEvent (tipo già esistente in Delphi ed
utilizzato dai componenti della VCL) come puntatore ad un metodo. Ritroveremo i tipi
puntatore a metodo più avanti quando tratteremo la stesura di componenti.

Nell'esempio precedente abbiamo visto come assegnare ad una variabile di tipo
procedurale l'indirizzo di una procedura. Approfondiamo il comportamento dei tipi
procedurali nelle assegnazioni e nelle espressioni.

  1. Nelle assegnazioni, l'interpretazione viene determinata dal tipo della variabile
    presente a sinistra dell'operatore ":=". Se la variabile è di tipo procedurale
    e, ovviamente, a destra abbiamo una funzione, avremo l'assegnamento alla variabile
    procedurale dell'indirizzo della funzione specificata a destra come già visto. Se la
    variabile a sinistra non è una variabile procedurale allora, se il tipo della variabile
    corrisponde al tipo restituito dalla funzione a cui fa riferimento la variabile
    specificata alla destra dell'operatore ":=" allora avremo l'assegnazione alla
    prima variabile del valore restituito dalla funzione riferita dalla seconda.
  2. Nelle espressioni, viene sempre eseguita la funzione e viene poi valutata l'espressione
    in base al valore restituito dalla funzione stessa. Quindi confrontando in una
    espressione, ad esempio in una istruzione If, due variabili procedurali puntanti a
    funzioni, verranno confrontati i valori restituiti dalle rispettive funzioni e non le
    variabili stesse.
  3. Per confrontare il contenuto di due variabili procedurali, occorre ricorrere
    all'operatore "@". Per ottenere l'indirizzo di memoria di una variabile
    procedurale, occorre utilizzare un doppio operatore "@".

Vediamo alcuni esempi

FBisestile := Bisestile; // Assegna a FBisestile l'indirizzo
della funzione Bisestile

Var B : Boolean;

F : TBisestileFunc;

...

F := Bisestile;

B := F; // Assegna a B il valore restituito dalla
funzione Bisestile riferita da F

If F1 = F2 then... // In questo caso vengono
confrontati i risultati delle
funzioni riferite dalle due variabili

If @F1 = @F2 then... // Vengono confrontati i valori
contenuti nelle variabili F1 ed F2

If @@F1 = @@F2 then... // Vengono confrontati gli indirizzi
di memoria delle variabili F1 ed F2

Come i tipi pointer una variabile procedurale può contenere il valore nil
indicando pioè che la non punta a niente. Effettuare una chiamata ad una variabile
procedurale puntante a nil provoca un errore. Per verificare se un
variabile procedurale non contiene il valore nil si utilizza la funzione Assigned
che restituisce false qualora la variabile contenga il valore nil.

Il costrutto

If Assigned(NomeEvento) then NomeEvento(Parametri)

viene molto utilizzato durante la stesura dei componenti per testare se ad una
proprietà evento è stata assegnata un procedura per la gestione dell'evento stesso ed
evitare quindi di generare errori.


Tipi variant

I tipi variant, come dice il nome stesso, sono tipi che possono variare ovvero
una variabile di tipo variant può assumere valori appartenenti a tipi diversi.
Vengono utilizzati quando non si può determinare in fase di compilazione il tipo che
verrà assegnato alla variabile. Questi tipi offrono una grande flessibilità ma occupano
più memoria e rallentano l'elaborazione. Si ha anche maggiore probabilità di provocare
errori runtime poiché non è possibile individuarli al momento della compilazione proprio
perché non hanno un tipo specifico.

Vengo utilizzate soprattutto negli oggetti COM, OLE. Il valore speciale Null
indica valori sconosciuti o mancanti. I tipi variant occupano 16 byte di memoria e al
momento della loro creazione vengono inizializzati al valore Unassigned.

Per una trattazione approfondita di questo tipo si rimanda ai manuali del linguaggio.

Ti consigliamo anche