Nelle sezioni precedenti del corso abbiamo visto, in particolare nelle sezioni che
spiegavano gli operatori, cose come Integer, String, Boolean. Si
tratta dei tipi di dati. Un tipo è essenzialmente un nome che identifica un particolare
genere di dati. Un tipo determina anche il range di valori validi per quel tipo. Quando si
definisce una variabile si deve specificarne il tipo.
L'Object Pascal è un linguaggio fortemente tipizzato. Ciò significa che distingue un
gran numero di dati. Solitamente, questa caratteristica permette al compilatore di
trattare i dati in maniera intelligente e di poter rilevare degli errori in fase di
compilazione che altrimenti sarebbero difficili da diagnosticare.
Esistono diversi tipi di dati in Object Pascal; questi sono organizzati in tipi
fondamentali (definiti direttamente dal linguaggio), generici, predefiniti e definiti
dall'utente.
I tipi fondamentali sono implementati direttamente nel linguaggio Object Pascal; i loro
formati e intervalli di validità sono gli stessi in tutte le implementazioni e sono
indipendenti dalla CPU e dal sistema operativo. I tipi generici invece, possono variare da
un'implementazione all'altra e sono specifici della piattaforma. È consigliabile
l'utilizzo dei tipi generici in quanto facilitano il porting tra piattaforme differenti.
I tipi predefiniti sono automaticamente riconosciuti dal compilatore e non hanno
necessità di essere dichiarati.
I tipi Object Pascal possono essere classificati come segue: simple, string,
structured, pointer, procedural, variant.
I tipi Simple
I tipi simple definiscono insiemi ordinati di valori. Appartengono a questa
categoria di tipi, i tipi ordinal e real.
I tipi ordinal
I tipi ordinal definiscono insiemi ordinati di valori in cui tutti i valori
hanno un predecessore ed un successore univoci ad esclusione del primo valore e
dell'ultimo. Sono tipi ordinal i tipi integer, boolean, character,
subrange.
Ogni valore è caratterizzato da una ordinalità che ne determina
l'ordinamento nel tipo. Solamente per il tipo integer l'ordinalità corrisponde
con il valore stesso. In tutti gli altri tipi, ad esclusione del subrange, il
primo valore ha ordinalità 0, successivo 1 e così a seguire. Delphi, o meglio l'Object
Pascal, mettono a disposizione diverse funzioni predefinite che permettono di lavorare
sulla ordinalità di questa categoria di tipi.
Le più importanti tra queste sono le seguenti:
Funzione | Valore restituito |
---|---|
Ord
|
restituisce l'ordinalità di un valore |
Pred
|
restituisce il predecessore del valore specificato |
Succ
|
restituisce il successore del valore specificato |
High
|
restituisce il valore più alto nel tipo |
Low
|
restituisce il valore più basso nel tipo |
Vediamo alcuni esempi: High(Byte) restituisce 255 in quanto il valore più alto del
tipo è 255; Succ(4) è 5 poiché 5 è il successore di 4.
Esistono due funzioni deifnite in Object Pascal che che si usano per incrementare e
decrementare il valore di una variabile ordinal e sono Inc e Dec. La
prima incrementa il valore ed equivale al Succ del valore stesso, la seconda lo
decrementa ed equivale al Pred del valore indicato.
I tipi Integer
I tipi Integer sono un sottoinsieme dei numeri interi. Esistono vari tipi di
interi in Object Pascal che si differenziano per intervallo di valori rappresentabili e
per formato. Per quanto riguarda i tipi interi generici abbiamo il tipo Integer e
Cardinal. I tipi integer fondamentali sono: Shortint, Smallint,
Longint, Int64, Byte, Word, Longword. Nella tabella
seguente sono riportate le differenze tra i tipi menzionati.
Tipo | Intervallo | Formato |
---|---|---|
Integer | -2147483648..2147483647 | 32 bit + segno |
Cardinal | 0..4294967295 | 32 bit |
Shortint | -128..127 | 8 bit + segno |
Smallint | -32768..32767 | 16 bit + segno |
Longint | -2147483648..2147483647 | 32 bit + segno |
Int64 | -263..263-1 | 64 bit + segno |
Byte | 0.255 | 8 bit |
Word | 0..65535 | 16 bit |
Longword | 0..4294967295 | 32 bit |
Incrementando l'ultimo valore o decrementando il primo valore di un tipo integer
il risultato sconfina all'inizio od alla fine dell'intervallo valido per quel tipo. Ad
esempio, nel caso del tipo Byte che ha come intervallo 0..255 l'esecuzione del
codice
var I : Byte;
...
I := High(Byte);
I := I + 1;
assegna ad I il valore 0. Da notare che se è stato attivato il controllo sugli
intervalli, il codice precedente genererà un errore di runtime.
I tipi character
Fanno parte dei tipi ordinal anche i tipi character i cui tipi
fondamentali sono AnsiChar e WideChar. Gli AnsiChar sono
caratteri che occupano un byte (8 bit) mentre i tipi WideChar sono caratteri che
occupano due byte ovvero una word (16 bit). Il rpimo tipo è ordinato secondo il set di
caratteri esteso ANSI mentre il secondo è ordinato secondo il set UNICODE i cui primi 256
caratteri corrispondono ai caratteri ANSI.
Il tipi generico è Char che nell'implementazione corrente equivale ad AnsiChar.
Esistono anche per questo tipo delle funzioni predefinite dell'Object Pascal come la Chr
che restituisce il carattere associato ad un intero compreso nell'intervallo AnsiChar
o WideChar. Ad esempio Chr(65) restituisce 'A'. Come per tutti i tipi ordinali,
è possibile applicare al tipo character la funzione predefinita Ord che
restituisce l'ordinale del valore character. Ad esempio, riprendendo l'esempio precedente,
Ord('A') restituisce 65 che è appunto l'ordinalità del carattere 'A' all'interno di AnsiChar
o WideChar.
Valgono anche per i tipi character gli sconfinamenti come visto per i tipi integer.
I tipi Boolean
I tipi boolean predefiniti sono quattro e sono: Boolean, ByteBool, WordBool
e LongBool. Si utilizza preferibilmente il tipo boolean mentre gli altri
esistono per compatibilità con altri linguaggi e con l'ambiente Windows.
Una variabile boolean equivale a ByteBool ed occupa un byte (8 bit) mentre una WordBool
occupa due byte (16 bit) ed una LongBool due word (32 bit). L'intervallo di valori per
questo tipo è composto da due costanti predefinite che sono true e False.
Un valore boolean viene considerato true quando la sua ordinalità è diversa
da zero. Da notare che se questo valore compare in un contesto in cui è atteso un valore
boolean, questo viene automaticamente convertito dal compilatore in true la dove
la sua ordinalità sia diversa da 0.
I tipi Enumerated
Il tipo enumerated serve a definire insiemi ordinati di valori. Da notare che
l'ordinalità di questi valori segue l'ordine con cui sono stati elencati i valori al
momento della dichiarazione del tipo.
Per dichiarare un tipo enumerated si utilizza la seguente sintassi
Type EnumeratedTypeName = (val1, val2, ..., valn);
Un esempio pratico potrebbe essere la dichiarazione di un tipo enumersto
"Seme" in un gioco di carte
Type Seme = (Cuori, Quadri, Fiori, Picche);
Nel caso del nostro esempio, i valori validi per il tipi "Seme" da noi
dichiarato sono: Cuori, Quadri, Fiori, Picche. In pratica quando si dichiara un tipo
enumerated si dichiarano delle costanti (val) del tipo enumerated dichiarato
(EnumeratedTypeName). Sempre facendo riferimento al nostro esempio, Cuori, Quadri, Fiori,
Picche sono costanti di tipo Seme.
Bisogna fare attenzione, al momento della dichiarazione del tipo, a non
utilizzare, per gli identificatori dei valori, nome che possano andare in conflitto con
altri nomi dichiarati nello stesso campo d'azione. Ad esempio, dichiarando un tipo
Type TSound = (Click, Clack, Clock);
ed utilizzando il valore Click come nel codice seguente, si verificherà un conflitto
di nome; questo perché Click è anche il nome di un metodo della classe TControl e di
tutti i sui discendenti (nel senso degli oggetti che discendono da esso!).
Procedure TForm1.Edit1Exit(Sender : TObject);
Var Snd : TSound;
Begin
...
Snd := Click;
...
End;
Si può aggirare questo tipo di inconveniente facendo riferimento al valore Click
qualificandolo con il nome della unit in cui è stato dichiarato. Ammettiamo che il tipo
TSound sia dichiarato nella unit SoundFuncs, si potrebbe utilizzare il seguente codice
Snd := SoundFuncs.Click;
Comunque, benché sia possibile utilizzare il codice precedente, si consiglia di
cercare di definire, per i valori dei tipi eneumerated, dei nomi che non entrino in
conflitto con altri identificatori. Una buona pratica è quella di anteporre al nome del
valore enumerated le iniziali del nome del tipo enumerated. Nell'esempio precedente
abbiamo dichiarato un tipo TSound; possiamo ridefinire i nomi dei suoi valori anteponendo
"ts" al loro nome in questo modo
Type TSound = (tsClick, tsClack, tsClock);
Come anche
Type TMyType = (mtVal1, mtVal2, mtVal3);
I tipi Subrange
Come dice il nome stesso, questi tipi identificano dei sottoinsiemi di valori
appartenenti ad un altro tipo ordinale che viene definito tipo base. Per definire
un tipo subrange, si utilizza la seguente sintassi
Type SubrangeTypeName = Low..High;
dove Low ed High appartengono allo stesso tipo ordinal e Low
è minore di High. Questo costrutto definisce quel sottoinsieme di valori
compresi tra Low ed High.
Per esempio
Type PrimiDieciNumeri = 1..10;
definisce un tipo subrange di nome PrimiDieciNumeri che conterrà i valori 1, 2, 3, 4,
5, 6, 7, 8, 9, 10.
Non valgono per questo tipo gli sconfinamenti visti per i tipi precedenti.