Sinossi
Descrizione
SizeOf può essere applicato a espressioni e nomi di tipi. Se l'argomento è un oggetto polimorfico,
viene restituita la dimensione del suo tipo effettivo.
Conforme a
Esempio
programma SizeOfDemo;
var
a: numero intero;
b: array [1 .. 8] di Char;
inizio
WriteLn (SizeOf (a)); {Dimensione di un `intero '; spesso 4 byte. }
WriteLn (SizeOf (Integer)); { Lo stesso. }
WriteLn (SizeOf (b)) {Size of eight `Char's; di solito 8 byte. }
fine.
La funzione di compilazione sizeOf restituisce la dimensione in byte di un dato tipo di nome di dati
o identificatore di variabile .
sizeOf può apparire in espressioni in fase di compilazione, anche all'interno delle direttive del
compilatore.
Indice
1 Storia
2 Caratteristiche
o 2.1 Compilatori gratuiti
3 Programmi di esempio
o 3.1 Hello, world!
o 3.2 Variabili
o 3.3 Puntatori
o 3.4 Array
o 3.5 Record
4 Input e output
5 Strutture di controllo
o 5.1 Alternative
o 5.2 Iterazione
6 Note
7 Voci correlate
8 Altri progetti
9 Collegamenti esterni
Storia
Pare che Wirth, docente di programmazione negli anni Sessanta, avvertì la mancanza di un
linguaggio di programmazione adatto all'insegnamento della propria materia e dotato di strutture
dati avanzate. Il BASIC, creato nel 1964, era facile da imparare ma non aveva strutture dati
avanzate e non incoraggiava abbastanza ad analizzare il problema prima di scrivere effettivamente
il codice. Nemmeno linguaggi come ALGOL e Fortran sembravano adatti per i suoi fini didattici.
Perciò, Wirth creò da zero il linguaggio Pascal, inserendovi il concetto di programmazione
strutturata[1].
La prima implementazione del linguaggio divenne operativa nel 1970, ma raggiunse una discreta
diffusione nel campo industriale a partire dal 1974, con la pubblicazione del libro "Pascal User
Manual and Report"[2], considerato il riferimento standard per il linguaggio. TeX e parte delle prime
versioni del sistema operativo del Macintosh[3] e di Microsoft Windows[4] furono scritte in Pascal.
Essendo un linguaggio pensato per studenti e utenti inesperti, i compilatori Pascal hanno un
atteggiamento protettivo nei confronti del programmatore (partendo dal presupposto che qualunque
irregolarità del codice scritto sia un errore), che spesso infastidisce un po' gli esperti. Per esempio i
dati e le funzioni sono verificati dal compilatore usando la cosiddetta tipizzazione forte (strong type
checking), ovvero uno stretto rigore riguardo alla definizione ed al modo di usare i tipi di dati, a
differenza di linguaggi a tipizzazione debole (per esempio JavaScript) che invece consentono ai
programmatori esperti una maggiore libertà di scrittura del codice al costo della possibilità di
rendere più arduo rilevare errori di struttura e sintassi.
Il Pascal è un linguaggio che impone sia l'uso di un buono stile di programmazione, sia di
analizzare a fondo il problema prima di cominciare a scrivere codice sorgente. Data la diffusione
negli ambienti scolastici e l'evoluzione della tecnologia e delle esigenze del mercato, ne sono state
create versioni ad oggetti, come per esempio il Turbo Pascal e l'Object Pascal (utilizzato
nell'ambiente di sviluppo Delphi di Borland, ora di proprietà di Embarcadero).
Caratteristiche
Le caratteristiche principali del Pascal sono una sintassi chiara e rigida con l'obbligo di dividere il
programma in sezioni ben definite (specifiche uses e implementation) e di dichiarare in anticipo
tutte le variabili usate nel programma. Anche la sequenza di definizione degli elementi nel codice
sorgente è rigidamente codificata e sequenziale, ovvero: etichette, costanti, tipi, variabili, procedure
e funzioni (label, const, type, var, procedure, function). Inoltre, permette l'uso di tipi di dati
complessi e definibili dal programmatore (record) tramite la specifica di sezionetype. Permette
anche l'uso dei puntatori e l'allocazione dinamica della memoria (specifiche new e dispose), in
modo comunque più controllato per esempio del linguaggio C tradizionale.
Compilatori gratuiti
Free Pascal[5], un compilatore con licenza GPL, con sintassi compatibile con Object Pascal;
Lazarus[6], nato come sottoprogetto di Free Pascal, un clone avanzato GPL di Borland
Delphi;
Dev-Pascal[7], IDE per Windows 9X, parzialmente compatibile con la serie NT/2K/XP.. e
non più sviluppato, basato sul compilatore Free Pascal.
Programmi di esempio
Hello, world!
program hello;
uses crt; <----non obbligatoria
begin
clrscr; <----non obbligatoria
writeln('Hello World');
readln;
end.
Note La prima riga introduce il programma con la parola chiave program, a cui segue il titolo del
programma. Non possono essere utilizzati caratteri speciali né spazi. La terza riga contiene
l'istruzione begin, usata per iniziare a scrivere il programma vero e proprio. La quinta riga contiene
l'istruzione writeln, usata per scrivere a video il testo ("Hello World") riportato tra parentesi, mentre
la sesta, con l'istruzione readln, pone il programma in attesa di un input da tastiera, in modo da non
far scomparire immediatamente la scritta. Quando verrà premuto il tasto invio, il programma
procederà eseguendo l'istruzione end, che pone fine alla sequenza. Tale comando è l'unico, in
ambiente Pascal, ad essere seguito da un punto anziché da un punto e virgola.
Variabili
Tipi interi, utilizzati per memorizzare valori numerici interi, con o senza segno e compresi
entro determinati intervalli numerici. In Pascal sono:
o integer: variabile di tipo intero a 16 bit con segno (numeri da -32.768 a 32.767)
o word: variabile di tipo intero a 16 bit senza segno, con valori compresi tra 0 e
65.535.
o byte: come già suggerisce il nome, questo tipo occupa un byte in memoria e
o longint: occupa 4 byte (ovvero 32 bit) e permette di gestire diversi valori con
segno, compresi fra -2147483648 e 2147483647.
o comp: è il tipo intero più grande. Occupa 8 byte (64 bit), pertanto permette di gestire
Puntatori
Si possono specificare puntatori a variabili usando nella dichiarazione un nome seguito dal simbolo
^ che precedete il tipo di variabile a cui deve puntare il puntatore. I puntatori funzionano come in
C/C++:
var
pointer : ^int;
number : int;
begin
number := 10;
pointer := @number;
end.
In questo modo pointer punterà a number. Mentre per assegnare un valore allo spazio di memoria
indirizzato da pointer, si userà ^ in coda al nome, ovvero come operatore di dereferenziazione:
pointer^ := 15;
Array
Gli array in Pascal sono una sequenza ordinata, in quantità prestabilita, di elementi dello stesso
tipo. Gli elementi possono essere composti da qualunque tipo di dati, nativo o definito dal
programmatore usando type.
Una caratteristica importante del linguaggio Pascal sta nel fatto che nel momento della
dichiarazione di un array, viene definito anche il valore iniziale dell'indice da utilizzare per la
scansione dei vari elementi:
Stringhe: Come in molti altri linguaggi le stringhe sono semplicemente degli array di
caratteri. La dichiarazione di una variabile stringa è quindi la dichiarazione di un array
composto da una quantità predefinita di caratteri. Nell'esempio seguente viene creato una
variabile stringa di 20 caratteri. La variabile dichiarata in questo modo può essere usata
come un array, cioè accedendo alle informazioni carattere per carattere oppure nel suo
insieme. Se si utilizza un'assegnazione di quest'ultimo tipo vengono interessati anche gli
elementi successivi alla lunghezza della stringa letterale assegnata. Quindi, seguendo
l'esempio, l'array riceve il nome «Paolo» nei suoi primi cinque elementi, mentre negli altri
viene comunque inserito uno spazio. Nelle più recenti implementazioni di Pascal è tuttavia
possibile usare il tipo String, che sostituisce la dichiarazione array [0..n] of char
Record
In Pascal è possibile definire un tipo personalizzato (custom), strutturato dal programmatore stesso.
La sintassi si basa sulla specifica type:
Input e output
L'input di dati da tastiera, viene effettuato tramite l'utilizzo del comando
readln(nome_variabile).
L'output invece usa il comando writeln(nome_variabile); la stampa a video usa lo stesso il
comando writeln, però il testo è tra singoli apici ' ' (writeln('ciao mondo');)
Esistono anche le due funzioni write() e read() che differiscono dalle precedenti per il fatto che
non scrivono un codice di "ritorno a capo" (carriage return) a fine riga.
Un esempio di I/O di numeri interi:
Nella stampa a video abbiamo usata un'alterazione del comando writeln, aggiungendo var:n:m
dove var è il nome della variabile da visualizzare, n è il numero di cifre complessive (compresa la
",") da visualizzare e m sono quelle dopo la virgola. Se ci sono più cifre da visualizzare di quelle
indicate, esse non vengono inviate sul dispositivo di uscita.
I/O caratteri: l'input e l'output di caratteri (numeri, lettere, simboli), è lo stesso per caratteri e numeri
interi:
I/O stringhe: le variabili stringhe come già detto sono array di char (vettori di caratteri).
Strutture di controllo
Alternative
Program alternativa;
var
n:integer;
begin
write('inserisci un numero: ');
readln(n);
if n > 0 {controlla se il valore è positivo}
then
write('il numero e'' positivo.')
else
write('il numero e'' negativo.');
readln;
end.
Iterazione
I seguenti frammenti di codice riportano un esempio dei cicli di iterazione in linguaggio Pascal.
Program ciclo_for;
var i, n, num:integer;
begin
write('quanti numeri vuoi inserire?');
readln(n);
for i:=1 to n do
begin
write('inserisci il numero: ');
readln(num);
end;
readln;
end.
Dopo la specifica for, occorre l'assegnazione di un valore a una variabile (in questo caso i:=1).
Questa variabile verrà incrementata automaticamente ad ogni ripetizione del ciclo, ovvero del
codice indicato dopo for tra do e end: una volta uguale al numero (o alla variabile) dopo il to, il
ciclo terminerà.
Il valore di una variabile può essere incrementato anche usando la stessa variabile come riferimento.
Ovviamente non bisogna fare confusione tra queste espressioni in Pascal, e in molti altri linguaggi,
con le convenzioni delle equazioni matematiche.
Il ciclo for...to...do ripete un blocco di istruzioni un numero determinato di volte, perciò sono vietati
valori reali decimali prima e dopo il to, e le variabili da utilizzare dovranno sempre essere state
dichiarate di tipo intero (integer).
Si può eseguire un ciclo for..to..do anche in ordine inverso, ossia dal numero più alto a quello più
basso, utilizzando la parola chiave downto al posto di to. In questo caso ad ogni ripetizione del
ciclo la variabile verrà decrementata invece che incrementata.
Program Esempio2;
Uses Crt,WinDos;
Var nome1,nome2,stringa:string;
file1,file2:text;
begin
clrscr;
write('Inserire il nome di un file: ');
readln(nome1);
write('Inserire il nome del file copia: ');
readln(nome2);
Assign(file1,nome1);
Assign(file2,nome2);
Reset(file1);
Rewrite(file2);
repeat
readln(file1,stringa);
writeln(file2,stringa);
until eof(file1);
Close(file1);
Close(file2);
writeln('Copia completata!');
readln
end.
Assign(file1,nome1): Questa specifica assegna alla variabile file 1 di tipo testo il nome del file
contenuto nella stringa nome1.
Occorre precisare come il linguaggio Pascal tradizionale utilizza i nomi dei file. Un nome può
essere composto al massimo da 8 caratteri, estensione esclusa. Se il nome supera gli 8 caratteri
viene troncato a 6 e si aggiunge un ~1 (il codice ASCII della tilde, ~, è 126). Perciò il nome
testouno.txt è corretto e viene mantenuto inalterato. Invece, testoquattro.txt è sbagliato e il
compilatore produrrà messaggio di errore (error 2: File not found); testoq~1.txt è la versione
corretta dell'esempio precedente: i caratteri vengono troncati a 6 e aggiunto un ~1.
Reset(file1); : l'istruzione reset(x), dove x è una variabile inizializzata con Assign e di tipo text o
file, serve per aprire il file x, in vista di operazioni di lettura/scrittura su di esso.
Rewrite(file2); : valgono le stesse regole di reset. Rewrite(x:text o file) è una procedura che crea
un nuovo file x (se non è specificata la directory nel suo nome, viene creato nel path corrente). Se
esiste già un file di nome x, lo sovrascrive.
repeat...until eof(file1); : ripete un ciclo di istruzioni finché l'espressione indicata dopo until è
vera. Nel nostro caso, il programma continua a leggere, riga per riga, ogni sequenza di caratteri
contenuta in file1 e la copia in file2, finché la variabile reimpostata eof (che significa End Of
File; supporta solo parametri di tipo text o file) non è vera, e quindi il file da cui leggere è al
termine.
Come si è ben visto, il ciclo repeat ... until serve per ripetere un'istruzione o un blocco di
istruzioni fino a che una condizione non è vera. Non serve racchiudere il blocco di istruzioni fra un
begin ed un end, poiché i confini del blocco sono già definiti da repeat e until.
Program esempio3;
Uses Crt;
Var x,y:word;
begin
clrscr;
write('Inserire due coordinate: ');
readln(x,y);
while (x<>0) and (y<>0) do
begin
read(x,y);
gotoxy(x,y);
write((,x,;,y,));
end;
readln;
end.
Questo programma legge da tastiera due coordinate, sposta il cursore a quelle date coordinate e
scrive in quel punto le ascisse e le ordinate tra parentesi, separate da un punto e virgola. Il ciclo si
ripete ogni volta che la condizione indicata è vera: in questo caso quando sia x che y sono diversi da
0.
La procedura gotoxy(x,y:word) sposta il cursore alle coordinare (x;y) sullo schermo.
Anche con while è opportuno prestare attenzione ai cicli infiniti e si noti che con while è
necessario includere il blocco di istruzioni fra begin ed end;
Le Variabili in Pascal
Una variabile è un’area della memoria del calcolatore (collocata nella memoria RAM e che
ovviamente si azzera al termine del programma) destinata a contenere un particolare dato (che
cambia nel corso del programma); viene distinta dalle altre aree per mezzo di un nome
(identificatore) che il programmatore stabilisce in modo univoco.
I nomi assegnati agli identificatori debbono:
1. non contenere spazi
2. non contenere segni di punteggiatura
3. non essere dei simboli o dei numeri
4. non essere una "parola riservata"
5. non contenere lettere accentate
Le variabili possono avere anche lo stesso nome del programma, e come quest'ultimo non vengono
lette dal compilatore, il quale legge solo i dati che esse contengono.
Per poter utilizzare una variabile bisogna effettuare due operazioni:
1. definirne il nome (l’identificatore);
2. definirne la natura (il tipo).
Solo successivamente è possibile usarla, cioè assegnarle un valore, leggerlo e modificarlo.
Il Pascal richiede che le variabili siano tutte dichiarate in una apposita sezione, all’inizio del
programma, che prende il nome di var. Se vi sono più variabili i vari identificatori vengono separati
virgole (,).
Esempio:
...
var identificatore,...,...,identificatore: tipo;
...
L’istruzione che ci permette di assegnare un valore ad una variabile prende il nome di
assegnazione. La sintassi è la seguente:
variabile1 := 10;
Il simbolo ":=" è chiamato operatore di assegnazione.
I tipi di dati elementari del linguaggio Pascal dipendono dal compilatore utilizzato e
dall’architettura dell’elaboratore sottostante. Alcuni forniscono delle estensioni dei dati nativi, per
ovviare ad esempio alla limitatezza degli integer: un esempio, disponibile in alcuni compilatori, è
il tipo di dato longint, che ha le stesse caratteristiche degli integer ma ammette numeri in un
intervallo molto maggiore.
I tipi di variabile possono essere suddivisi in tre gruppi: numerici, testo e booleane. I più diffusi
sono:
NUMERICI:
TESTO:
BOOLEANE:
o Boolean = Può assumere due valori true (vero, 1) o false (falso, 0); occupa 1 bit
o CBoolean = Può assumere due valori true (vero, 1) o false (falso, 0); occupa 1 bit
o ByteBool = Può assumere due valori true (vero, 1) o false (falso, 0); occupa 1 byte
o LongestBool = Può assumere due valori true (vero, 1) o false (falso, 0); occupa 8
bytes
o LongBool = Può assumere due valori true (vero, 1) o false (falso, 0); occupa 8 bytes
Essendo comunque il computer una macchina limitata non è possibile scrivere numeri fino
all'infinito. Quando si supera il limite massimo, la macchina segnala un errore di overflow.
E' importante utilizzare il tipo di variabile più adatto per ogni situazione. Ad esempio l'uso di
variabili intere anziché variabili reali, là dove è consentito, permette di scrivere programmi più
efficienti (più veloci in fase di esecuzione e meno voluminosi come area di memoria occupata),
anche se è più facile incorrere in un errore di overflow.
Se viene digitato in input un valore string, là dove è richiesto un valore numerico, verrà generato
un errore di run-time che terminerà l'esecuzione del programma. Ma questo non significa che
occorre utilizzare solo variabili string per l'input e poi convertirle; invece è opportuno utililizzare
la tipologia di variabile adatta ed attivare la gestione dell'errore con {SI-} per evitare errori di run-
time.
Operatore
Descrizione Operandi Risultato
algebrico
real o integer o
+ somma due valori real o integer o char
char
real o integer o
- calcola la differenza fra due valori real o integer o char
char
real o integer o
* moltiplica due valori real o integer o char
char
real o integer o
abs(x) calcola il valore assoluto di un numero real o integer o char
char
trunc(x) tronca un numero alla sua parte intera real o integer o char integer
L'unica operazione algebrica possibile con il tipo string è +, che concatena più stringhe in un'unica
stringa contenente tutte le altre, nell'ordine indicato.
Pascal inoltre mette a disposizione inoltre alcuni operatori di confronto che restituiscono sempre
un valore booleano e che possono essere usati su due variabili qualsiasi dello stesso tipo di dato:
< minore
= uguale
> maggiore
DATI STRUTTURATI
Quando i dati da trattare sono molti è comodo ordinarli in una tabella:
Posizione 1 2 3 4 5 6 7 8 9 10
Valori a bc de f ghi l
Nella nostra tabella esempio abbiamo gestito le prime dieci lettere dell'alfabeto. In informatica
questo tipo di struttura è chiamato vettore. Il vettore consente di identificare un gruppo di dati con
un nome collettivo e di poter individuare ogni singolo dato attraverso un indice racchiuso tra
parentesi quadre, il quale rappresenta la posizione occupata dal dato nella tabella.
In Pascal si possono strutturare i dati in maniera simile tramite gli array (anch'essi collocati nella
memoria RAM e quindi si azzerano al termine del programma), i suoi componenti devono
appartere allo stesso tipo.
La dichiarazione degli array viene effettuata in Pascal nel modo seguente:
var nome_array: array [indice] of tipo_variabile;
Valgono anche dichiarazioni come:
type
mese : (gen, feb, mar, apr, mag, giu, lug, ago, set, ott, nov, dic);
var
nomi_mesi: array[mese] of string;
Per accedere ai valori dell'array, è sufficiente comportarsi come con le variabili normali, usando
la notazione
nome_array[indice]
E’ possibile dichiarare anche un array a più dimensioni; ad esempio un array a due dimensioni
(detto matrice) si dichiara in questo modo:
var nome_array: array[1 .. n,1 .. m] of tipo_variabile;
Per leggere e scrivere i dati di una matrice di N righe e M colonne in Pascal si possono usare due
cicli for annidati.
Insieme alla nozione di tipo, una delle grandi idee introdotte dal linguaggio Pascal e' l'abilita' di
definire nuovi tipi di dati in un programma. I programmatori possono definire i loro tipi di dati per
mezzo dei type constructor, sono un esempio i tipi subrange, i tipi array, i tipi enumerativi, i tipi
puntatore ed i tipi set. Il piu' importante tipo di dato definito dall'utente e' la classe, la quale e' parte
delle estensione orientate agli oggetti dell'Object Pascal, non descritta in questo libro.
E' giusto pensare che i type contructor sono comuni a diversi linguaggi di programmazione, ma il
Pascal e' stato il primo linguaggio ad introdurre l'idea in modo formale e molto preciso. Esistono
solo pochi linguaggi con cosi' tanti meccanismi per definire nuovi tipi di dato.
type
// subrange definition
Uppercase = 'A'..'Z';
// array definition
Temperatures = array [1..24] of Integer;
// record definition
Date = record
Month: Byte;
Day: Byte;
Year: Integer;
end;
// set definition
Letters = set of Char;
Simili definizioni di tipo possono essere usate direttamente per definire una variabile senza un
esplicito nome di tipo, come nel seguente codice:
var
DecemberTemperature: array [1..31] of Byte;
ColorCode: array [Red..Violet] of Word;
Palette: set of Colors;
Nota: In generale, bisogna evitare di usare i tipi unnamed come nel codice sopra, visto che non si
puo' passarli come parametri alle routine o dichiarare altre variabili dello stesso tipo. Le regole di
compatibilita' di tipo del Pascal sono basate di fatto sui nomi del tipo, non sulla definizione corrente
del tipo. Due variabili di due identici tipi non sono ancora compatibili, a meno che i loro tipi
abbiano esattamente lo stesso nome, e ai tipi unnamed viene attribuito un nome interno dal
compilatore. Bisogna abituarsi a definire un tipo di dato ogni volta che si ha bisogno di una
variabile complessa, e sicuramente non ci si pentira' del tempo speso.
Ma cosa vogliono dire queste definizioni ? Daro' alcune definizioni per quelli che non hanno
familiarita' con le dichiarazioni di tipo del Pascal. Tentero' anche di sottolineare le differenze dagli
stessi costrutti negli in altri linguaggi di programmazione, cosi' la lettura di questa sezione sara'
interessante anche per chi ha familiarita' con le definizioni mostrate sopra. Finalmente mostrero'
alcuni esempi in Delphi ed introdurro' alcuni strumenti che permettaranno l'accesso alle
informazioni sul tipo in modo dinamico.
Tipi Subrange
Un tipo subrange definisce un intervallo di valori entro un intervallo di un altro tipo(da qui il nome
di subrange)). Si puo' definire un subrange del tipo Integer, da 1 a 10 o da 100 a 1000, oppuresi puo'
definireun subrange del tipo Char, come in:
type
Ten = 1..10;
OverHundred = 100..1000;
Uppercase = 'A'..'Z';
Nella definizione di un subange, non serve specificare il nome del tipo di base. Bisogna solo
specificare due costanti di questo tipo. Il tipo originale deve essere un tipo ordinale e il tipo
risultante deve assere ancora un tipo ordinale.
Una volta definito un subrange, si puo' legalmente assegnare ad esso un valore compreso in questo
intervallo. Il codice seguente pertanto risulta valido:
var
UppLetter: UpperCase;
begin
UppLetter := 'F';
var
UppLetter: UpperCase;
begin
UppLetter := 'e'; // compile-time error
Il codice sopra produrra' un errore di run-time, "Constant expression violates subrange bounds." Se
si scrive il seguente codice invece:
var
UppLetter: Uppercase;
Letter: Char;
begin
Letter :='e';
UppLetter := Letter;
Delphi lo compilera'. A run-time, se e' stato abilitato l'opzione del compilatore Range Checking
(nella pagina Compiler della finestra Project Options), si otterra' questo messaggio d'errore: Range
check error.
Nota: Suggerisco di attivare questa opzione mentre si sta sviluppando un programma, cosi' sara'
piu' robusto e piu' facile il debug, visto nel caso di errori si si otterra' uno specifico messaggio e non
un comportamento anomalo del programma. Eventualmente si puo' disabilitare questa opzione per
la compilazione finale del programma, per generarlo piu' veloce e compatto. Comunque la
differenza e' davvero limitata, e per questo suggerisco di lasciare tutte questa opzioni di controllo a
run-time abilitate, anche nel programma finito. La stessa cosa vale per le altre opzioni di controllo a
run-time tipo il controllo di overflow e dello stack.
Tipi Enumerativi
I tipi enumerativi costituiscono un altro tipo ordinale definibile dall'utente. Invece di indicare un
intervallo di un tipo esistente, in un'enumerazione bisogna elencare tutti i possibili valori del tipo. In
altre parole, un'enumerazione e' un elenco di valori. Ecco alcuni esempi:
type
Colors = (Red, Yellow, Green, Cyan, Blue, Violet);
Suit = (Club, Diamond, Heart, Spade);
Ogni valore nell'elenco ha una ardinalita' associata che parte da zero. Quando si applica la funzione
Ord ad un valore di un tipo enumerativo, si ottiene questo valore zero-based. Per esempio Ord
(Diamonds) ritorna 1.
Nota: I tipi enumerativi possono avere differenti rappresentazioni interne. Per default, Delphi usa
una rappresentazione a 8 bit, a meno che non vi siano piu' di 256 valori, nel qual caso viene usata
una rappresentazione a 6 bit. Esiste anche una rappresentazione a 32 bit, la quale puo' essere utile
per compatibilita' con le librerie C o C++. Attualmente si puo' comunque cambiare questo
comportamento di default, domandando una rappresentazione maggiore usando la direttiva di
compilatore $Z.
La VCL (Visual Component Library) di Delphi usa i tipi enumerativi in deversi punti. Per esempio,
gli stili del bordo di un form sono definiti come segue:
type
TFormBorderStyle = (bsNone, bsSingle, bsSizeable,
bsDialog, bsSizeToolWin, bsToolWindow);
Quando il valore di una proprieta' e' un'enumerazione, si puo' scegliere da una lista di valori
visualizzata nell Object Inspector, come visibile in Figura 4.1.
L'help file di Delphi generalmente elenca i possibili valori di un'enumerazione. Come alternativa si
puo' usare il programma OrdType, disponibile su www.marcocantu.com, per vedere la lista dei
valori di ogni enumerazione, set, subrange e ogni altro tipo ordinale di Delphi. Si puo' vedere un
esempio dell'output di questo programma in Figura 4.2.
Figura 4.2: Informazioni dettagliate riguardo i tipi enumerativi, come sono mostrati dal programma.
Tipi Set
I tipi set indicano un gruppo di valori, dove la lista dei valori disponibili e' indicata dal tipo ordinale
su cui il tipo set e' basato. Questi tipi ordinali sono usualmente limitati, e abbastanza spesso
rappresentati da un'enumerazione o un subrange. Se si prende il subrange 1..3, i possibili valori del
set basato su esso includono solo 1, solo 2, solo 3, sia 1 che 3, sia 2 che 3, tutti i tre valori, o
nessuno di essi.
Una variabile di solito contiene uno dei possibili valori dell'intervallo di questo tipo. Una variabile
di tipo set, invece, puo' contenere nessuno, uno, due, tre o piu' valori dell'intervallo. La variabile set
puo' anche includere tutti i valori. Ecco un esempio di un set:
type
Letters = set of Uppercase;
Adesso posso definire una variabile di questo tipo ed assegnare alcuni valori del tipo originale. Per
indicare i valori in un set, si puo' scrivere un elenco separato da virgole, racchiuso tra parentesi
quadre. Il seguente codice mostra l'assegnazione ad una variabile di diversi valori, un singolo
valore, e un valore vuoto:
var
Letters1, Letters2, Letters3: Letters;
begin
Letters1 := ['A', 'B', 'C'];
Letters2 := ['K'];
Letters3 := [];
In Delphi, un set e' generalmente usato per indicare un flag non esclusivo. Per esempio, le seguenti
due linee di codice (che sono parte della libreria di Delphi) dichiarano un'enumerazione di icone per
il bordo di una finestra e il corrispondente tipo set:
type
TBorderIcon = (biSystemMenu, biMinimize, biMaximize, biHelp);
TBorderIcons = set of TBorderIcon;
Di fatto, una data finestra puo' avere nessuna di queste icone, una, o piu' di una. Quando si lavora
con l'Object Inspector (vedi Figura 4.3), si possono provvedere i valori di un set espandendo la
selezione (doppio click sul nome della proprieta' o click sul segno di piu' sulla sinistra) e mettere on
oppure off la presenza di ogni valore.
Un'altra proprieta' basata su un tipo set e' lo stile di un font. I possibili valori indicano un font in
grassetto, in corsivo, sottolineato o barrato. Naturalmente lo stesso font puo' essere corsivo e
grassetto, non avere nessun attributo oppure tutti gli attributi. Per questa ragione e' dichiarato come
un set. Si possono assegnare valori a questo set nel codice del programma come segue:
Si puo' anche operare su di un set in diversi modi, incluso aggiungere due variabili dello stesso tipo
set (o, per essere piu' precisi, calcolare l'unione delle due variabili set):
Ancora, si puo' usare l'esempio OrdType per vedere la lista dei possibili valori di diversi set definiti
nelle librerie di Delphi.
Tipi Array
I tipi array definiscono un elenco di un numero prefissato di elementi di uno specifico tipo.
Generalmente si puo' usare un indice all'interno di parentesi quadre per accedere ad un elemento
dell'array. Le parentesi quadre sono usate anche per specificare i possibili valori dell'indice quando
l'array e' definito. Ad esempio, si puo' definire un gruppo di 24 numeri interi con il seguente codice:
type
DayTemperatures = array [1..24] of Integer;
Nella definizione dell'array, bisogna passare un tipo subrange nelle parentesi quadre oppure definire
un nuovo typo subrange usando due costanti di un tipo ordinale. Questo subrange specifica gli
indici validi dell'array. Siccome si specifica sia l'estremo inferiore che quello superiore, l'indice non
deve per forza essere zero-based, come invece necessario in C, C++, Java e altri linguaggi di
programmazione.
Siccome gli indici dell'array sono basati su di un subrange, Delphi puo' controllare il loro intervallo
come abbiamo gia' visto. Una costante subrange non valida produrra' un errore in compilazione e
usare un indice fuori dai valori consentiti a run-time produrra' un errore di run-time se la
corrispondente opzione del compilatore e' attivata.
Usando la definizione di array vista sopra, si puo' settare il valore di una variabile DayTemp1del
tipo DayTemperatures come segue:
type
DayTemperatures = array [1..24] of Integer;
var
DayTemp1: DayTemperatures;
procedure AssignTemp;
begin
DayTemp1 [1] := 54;
DayTemp1 [2] := 52;
...
DayTemp1 [24] := 66;
DayTemp1 [25] := 67; // compile-time error
Un array puo' avere piu' di una dimensione, come nel seguente esempio:
type
MonthTemps = array [1..24, 1..31] of Integer;
YearTemps = array [1..24, 1..31, Jan..Dec] of Integer;
Questi due tipi di array sono costruiti sugli stessi tipi base. In questo modo si possono dichiarare
usando il precedente tipo di dato, come nel seguente codice:
type
MonthTemps = array [1..31] of DayTemperatures;
YearTemps = array [Jan..Dec] of MonthTemps;
Questa dichiarazione inverte l'ordine degli indici come mostrato sopra, ma permette anche
assegnamenti di interi blocchi tra variabili. Ad esempio, la seguente istruzione copia la temperatura
di gennaio in febbraio:
var
ThisYear: YearTemps;
begin
...
ThisYear[Feb] := ThisYear[Jan];
Si puo' anche definire un array zero-based , un array con il limite inferione uguale a zero.
Generalmente, l'uso di limiti piu' logici e' un vamtaggio, siccome non serve usare l'indice 2 per
acceder al terzo elemento, e cosi' via. Windows, tuttavia, usa invariabilmente array zero-based
(visto che windows e' basato sul linguaggio C), e la libreria dei componenti di Delphi tende a fare lo
stesso.
Se serve lavorare con un array, si puo' comunque testare i limiti con le funzioni standard Low e
High, le quali ritornano il limite inferiore e superiore dell'array. Usare Low e High quando si opera
su un array e' altamente consigliato, specialmente nei cicli, siccome rende il codice indipendente
dall'intervallo dell'array. Successivamente, si puo' cambiare l'intervallo dell'array e il codice che usa
Low e High funzionera' ancora. Se si scrive un ciclo con i valori correnti dell'intervallo, bisognera'
aggiornare il codice quando la dimensione dell'array cambia. L'uso di Low e High rende il codice
facile da mantenere e piu' affidabile.
Nota: A proposito, non c'e' una penalizzazione delle prestazioni a run-time quando si usano le
funzioni Low e High con gli array. Esse sono risolte in costanti al momento della compilazione.
Questa conversione da funzione a costante durante compilazione accade anche per altre semplici
funzioni di sistema.
Delphi usa gli array principalmente nella forma di array di proprieta'. Si e' gia' visto un esempio di
queste proprieta' nell'esempio TimeNow, per accedere alla proprieta' Items di un componente
ListBox. Mostrero' ulteriori esempi di proprieta' array nel prossimo capitolo, quando si discuteranno
i cicli.
Nota: delphi 4 introduce i dynamic array, che sono array che possono essere ridimensionati a run-
time allocando una giusta quantita' di memoria. Usando i dynamic array e' facile, ma in questa
discussione di Pascal ritengo che non siano un argomento adatto. Si puo' trovare una descrizione dei
dynamic array di Delphi nel capitolo 8.
Tipi Record
I tipi record definiscono una collezione fissa di elementi di tipi differenti. Ogni elemento, o campo,
ha il proprio tipo. La definizione di un tipo record elenca tutti questi campi, dando ad ognumo un
nome che verra' usato piu' tardi per accedervi.
Ecco un piccolo listato con la definizione di un record, la dichiarazione di una variabile di questo
tipo, e qualche istruzione che usa questa variabile:
type
Date = record
Year: Integer;
Month: Byte;
Day: Byte;
end;
var
BirthDay: Date;
begin
BirthDay.Year := 1997;
BirthDay.Month := 2;
BirthDay.Day := 14;
Le classi e gli oggetti possono essere considerati un'estensione del tipo record. Le librerie di Delphi
tendono ad usare i tipi classe piuttosto che i tipi record, ma ci sono diversi tipi record definiti nelle
API di Windows.
I tipi record possono avere anche una parte variabile, cioe': campi multipli possono essere mappati
sulla stessa area di memoria, anche se hanno differenti tipi (Questo corrisponde al tipi union nel
linguaggio C). Alternativamente, si possono usare questi campi varianti o gruppi di campi per
accedere alla stessa locazione di memoria dentro ad un record, ma considerando questi valori da una
differente prospettiva. L'uso principale di questo tipo e' di archiviare simili ma differenti dati e di
ottenere effetti sinili a quelli del typecasting (meno utili adesso che il type casting e' stato introdotto
anche in Pascal). L'uso dei record variant e' stato rimpiazzato da tecniche object oriented e altre
tecniche moderne, benche' Delphi li usa in alcuni casi peculiari.
L'uso di un record variant non e' type-safe e non e' raccomandato come tecnica di programmazione,
particolarmente per i principianti. I programmatori esperti possono invece usare i variant record e
nel nucleo delle librerie di Delphi ci sono esempi d'uso. Ad ogni modo, non serve affrontarli finche'
non ci si sente esperti di Delphi.
Puntatori
Un tipo puntatore definisce una variabile che contiene l'indirizzo di memoria di un'altra variabile di
un dato tipo (o di un tipo indefinito). Cosi' una variabile puntatore indirettamente punta ad una
variabile. La definizione di un tipo puntatore non e' basata su una specifica keyword, ma usa invece
uno speciale carattere. Questo simbolo speciale e' il carattere (^):
type
PointerToInt = ^Integer;
Una volta che e' stata definita una variabile puntatore, si puo' assegnare ad essa l'indirizzo di un'altra
varabile dello stesso tipo, usandol'operatore @:
var
P: ^Integer;
X: Integer;
begin
P := @X;
// change the value in two different ways
X := 10;
P^ := 20;
Invece di puntare ad una locazione di memoria esistente, un puntatore puo' indirizzare un nuovo
blocco di memoria allocata dinamicamente (nell'area di memoria heap) con la procedura New. In
questo caso non serve piu' il puntatore, bisogna anche ricordarsi di liberarsi della memoria
dinamicamente allocata chiamando laprocedura Dispose.
var
P: ^Integer;
begin
// initialization
New (P);
// operations
P^ := 20;
ShowMessage (IntToStr (P^));
// termination
Dispose (P);
end;
Se un puntatore non ha valore, si puo' assegnare il valore nil ad esso. Si puo' quindi testare quando
un puntatore e' nil per vedere se attualmente referenzia qualche valore. Questo metodo e' spesso
usato, visto che dereferenziare un puntatore non valido causa una violazione d'accesso (conosciuta
anche come General Protection Fault, GPF):
Figura 4.4: L'errore di sistema risultante dall'accesso ad un puntatore nil, dall'esempio GPF
Nello stesso programma si puo' trovare un esempio di accesso ai dati sicuro. In questo secondo caso
il puntatore e' assegnato ad una variabile locale esistente, e puo' essere usato senza rischi, ma ho
aggiunto un controllo per sicurezza:
Delphi definisce anche un tipo di dato Pointer, che indica un puntatore senza tipo (come void* nel
linguaggio C). Se si usa un puntatore senza tipo bisogna usare GetMem invece di New. La
procedura GetMem e' richiesta ogni volta che la dimensione della variabile di memoria da allocare
non e' definita.
Il fatto che i puntatori sono raramente necessari in Delphi costituisce un interessante vantaggio di
questo ambiente. Nonostante cio', capire i puntatori e' importante per la programmazione avanzata e
per capire completamente il modello a oggetti di Delphi, che usa i puntatori "dietro le quinte."
Nota: Sebbene non si usino i puntatori spesso in Delphi, si usa frequentemente un costrutto similare,
le references. Ogni istanza di oggetto e' in realta' un puntatore o una referenza al suo dato corrente.
Comunque, questo e' completamente trasparente al programmatore, che usa le variabili oggetto
come qualsiasi altro tipo di dato.
Tipi File
Un altro tipo specifico del Pascal e' il tipo File. Il tipo file rappresenta i file fisici su disco, di
sicuramente una peculiarita del linguaggio Pascal. Si puo' definire un nuovo tipo file come segue:
type
IntFile = file of Integer;
Adesso si puo' aprire un file fisico associato con questa struttura e scrivere in esso valori interi o
leggerne il valore corrente.
Nota dell'autore: Gli esempi relativi ai file sono parte delle vecchie edizioni di Mastering Delphi e
progetto di aggiungerli al piu' presto.
l'uso dei file in Pascal e' abbastanza semplice, ma in Delphi ci sono anche altria componenti che
sono capaci di memorizzare o caricare il proprio contenuto su o da un file. C'e' un supporto per la
serializzazione, nella forma di stream, e c'e' anche il supporto per i database.
Conclusioni
Questo capitolo che tratta dei tipi di dati definibili dall'utente completa la copertura del sistema del
Pascal. Adesso si puo' guardare alle istruzioni che il linguaggio provvede per operare sulle variabili
che abbiamo definito.
FillChar
FillChar riempie la memoria a partire X con il conte byte o caratteri con il carattere indicato.
program FillCharDemo;
USES crt;
Var S : String[10];
I : Byte;
begin
For i:=10 downto 0 do
begin
FillChar (S,SizeOf(S),' ');
SetLength(S,I);
Writeln (s,'*');
end;
ReadLn;
end.