Logo

Marco Cantù
Essential Pascal


Tradotto dall'inglese da: Paolo Rossi

Capitolo 3
Tipi, variabili e costanti

Il linguaggio Pascal originario era basato su pochi semplici concetti, i quali sono ora diventati abbastanza comuni nei linguaggi di programmazione. Il primo e' il concetto di tipo di dato. Il tipo determina i valori che una variabile puo' assumere e le operazioni che si possono eseguire su di essa. Il concetto di tipo e' piu' forte in Pascal che in C, dove l'aritmetica dei tipi e' praticamente intercambiabile e piu' forte che nella versione originale del BASIC, che non ha un simile concetto.

Variabili

Il Pascal richiede che tutte le variabili siano dichiarate prima del loro effettivo uso. Ogni volta che si dichiara una variabile, bisogna specificarne il tipo. Ecco alcuni esempi di dichiarazioni di variabili:

var
  Value: Integer;
  IsCorrect: Boolean;
  A, B: Char;

La keyword var puo' essere usata in diversi punti del codice, come all'inizio del codice di una funzione o procedura, per dichiarare variabili locali alla routine, o in una unit per dichiarare variabili locali. Dopo la keyword var segue una lista di nomi di variabile, seguite da un due punti (:) e dal nome del tipo. Si puo' scrivere piu' di un nome di varabile su una singola riga, come nell'esempio sopra.

Una volta definita una variabile di un certo tipo, si possono eseguire su di essa soltanto le operazioni supportate dal tipo di dato. Ad esempio, si puo' usare un valore booleano in un test ed un valore intero in un'espressione numerica. Non si possono mischiare booleani e interi (come permesso in C).

Usando semplici assegnamenti, si puo' scrivere il seguente codice:

Value := 10;
IsCorrect := True;

La prossima istruzione non e' corretta, poiche' le due variabili hanno differenti tipi di dato:

Value := IsCorrect; // error

Se si prova a compilare questo codice, Delphi genera un errore di compilazione con questa descrizione: Incompatible types: 'Integer' and 'Boolean'. Di solito, errori come questo sono errori di programmazione, siccome non ha senso assegnare ad un intero un valore booleano (true o false). Non si puo' incolpare Delphi per questo errore. Delphi avvisa solo che c'e' qualcosa di sbagliato nel codice.

Certamente e' spesso possibile convertire il valore di una variabile da un tipo all'altro. In diversi casi questa conversione e' automatica ma spesso bisogna invocare una specifica funzione di sistema che cambia la rappresentazione interna dei dati.

In Delphi si puo' assegnare un valore iniziale ad una variabile globale mentre si dichiara. Ad esempio, si puo' scrivere:

var
  Value: Integer = 10;
  Correct: Boolean = True;

Questa tecnica di inizializzazione funziona solo per le variabili globali, non per le variabili dichiarate all'interno di procedure o metodi.

Costanti

Il Pascal permette anche la dichiarazione di costanti per dare il nome a valori che non cambiano durante l'esecuzione del programma. Per dichiarare una costante non serve specificare il tipo di dato, ma solo assegnare un valore iniziale. Il compilatore guardera' il valore ed automaticamente usera' il tipo di dato appropriato. Ecco alcuni esempi:

const
  Thousand = 1000;
  Pi = 3.14;
  AuthorName = 'Marco Cantù';

Delphi determina il tipo basandosi sul valore. Nell'esempio sopra, la costante Thousand e' di tipo SmallInt, il tipo piu' piccolo che puo' contenerla. Se si vuole che Delphi assegni uno specifico tipo, si aggiunge semplicemente il nome del tipo nella dichiarazione, come nel seguente esempio:

const
  Thousand: Integer = 1000;

Quando si dichiara una costante il compilatore puo' scegliere se assegnare una locazione di memoria alla costante e salvarne il valore oppure duplicare il valore attuale ogni volta che la costante e' usata. Questo secondo approccio ha senso particolarmente per costanti semplici.

Nota: La versione a 16 bit di Delphi permette di cambiare il valore di una costante tipizzata a run-time, come se fosse una variabile. La versione a 32 bit permette ancora questo comportamento per compatibilita' quando si abilita la direttiva $J o si spunta il checkbox Assignable typed constants nella pagina Compiler della finestra di dialogo Project Options. Sebbene questo sia il default, e' decisamente sconsigliato l'uso di questo trucchetto come tecnica di programmazione. Assegnare un nuovo valore ad una costante disabilita tutte le ottimizzazioni sulle costanti. In casi come questo, dichiarare invece una variabile.

Costanti Resource String

Quando si definisce una costante di tipo stringa, invece di scrivere:

const
  AuthorName = 'Marco Cantù';

a cominciare da Delphi 3 si puo' scrivere:

resourcestring
  AuthorName = 'Marco Cantù';

In entrambi i casi si sta' definendo una costante, che e' un valore che non cambia durante l'esecuzione del programma. La differenza e' solo nell'implementazione. Una costante di tipo stringa definita con la direttiva resourcestring e' memorizzata nelle risorse del programma nella tabella delle stringhe.

Per vedere questa particolarita' in azione, si puo' guardare l'esempio ResStr, che ha un pulsante con il seguente codice:

resourcestring
  AuthorName = 'Marco Cantù';
  BookName = 'Essential Pascal';

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage (BookName + #13 + AuthorName);
end;

L'output delle due stringhe e' su due righe a perche le stringhe sono separate dal carattere newline (indicato dal suo valore numerico nella costante carattere #13).

L'aspetto interessante di questo programma e' che se lo si esamina con un Resource Explorer (se ne trova uno negli esempi forniti con Delphi) si possono notare le stringhe nelle risorse. Cio' vuol dire che le stringhe non fanno parte del codice compilato ma sono memorizzate in un'area separata del file eseguibile (il file EXE).

Nota: In breve i vantaggi delle risorse sono: un'efficiente gestione della memoria da parte di Windows e la possibilita' di tradurre un programma senza dover modificare il codice sorgente.

Tipi di dato

In Pascal ci sono diversi tipi di dato predefiniti, che possono essere divisi intre gruppi: tipi ordinali, tipi reali e stringhe. I tipi ordinali e reali verranno discussi nella seguente sezione, mentre le stringhe saranno discusse piu' avanti in questo capitolo. In questa sezione introdurro' alcuni tipi definiti nelle librerie di Delphi (quindi non predefiniti dal compilatore). Questi tipi possono essere considerati predefiniti.

Delphi include anche tipi di dato non tipizzati, chiamati variant, non discussi in questo libro (vedere il capitolo 2 di Mastering Delphi 4). Stranamente un variant non ha controllo sul tipo (type-checking). I variant furono introdotti in Delphi 2 per gestire OLE Automation.

Tipi Ordinali

I tipi ordinali sono basati sul concetto di ordine o sequenza. Non solo si possono comparare due valori per vedere il piu' alto, ma si puo' anche richiedere il valore precedente o successivo di un dato valore o calcolare il piu' piccolo o il piu' grande valore possibile.

I tre piu' importanti tipi ordinali predefiniti sono: Integer, Boolean e Char (caratteri). Comunque ci sono diversi altri tipi che hanno uno stesso stesso significato, ma una differente rappresentazione interna e intervallo di valori. La seguente Tabella 3.1 elenca i tipi ordinali per rappresentare i numeri.

Tabella 3.1: Tipi Ordinali per rappresentare i numeri

Dimensione Intervallo con segno Intervallo senza segno
8 bits ShortInt
-128 to 127
Byte
0 to 255
16 bits SmallInt
-32768 to 32767
Word
0 to 65,535
32 bits LongInt
-2,147,483,648 to 2,147,483,647
LongWord (since Delphi 4)
0 to 4,294,967,295
64 bits Int64
16/32 bits Integer Cardinal

Come si puo' vedere, questi tipi corrispondono a differenti rappresentazioni di numeri e differiscono dal numero di bit usati per esprimere il valore e dalla presenza o assenza del bit del segno. I valori con segno possono essere positivi o negativi, ma hanno un intervallo piu' piccolo di valori, siccome e' per i valore e' disponibile un bit in meno. Ci si puo' riferire all'esempio Range, discusso nella prossima sezione, per l'intervallo di valori di ogni tipo.

L'ultimo gruppo (segnato come 16/32) indica valori che hanno una diversa rappresentazione nelle versioni 16 e 32 bit di Delphi. Inetger e Cardinal sono usati di frequente siccome corrispondono alla rappresentazione nativa dei numeri nella CPU.

Tipi integrali in Delphi 4

In Delphi 3, i numeri a 32 bit senza segno indicati dal tipo Cardinal sono in realta' valori a 31 bit, con un intervallo fino a 2 megabyte. Delphi 4 introduce un nuovo tipo numerico senza segno, LongWord, il quale usa un valore a 32 bit che arriva fino a 4 megabyte. Il tipo Cardinal e' adesso solo un alias al nuovo tipo. Il tipo LongWord permette di indirizzare piu' di 2MB di dati come numero senza segno. Oltretutto corrisponde alla rappresentazione nativa della CPU.

Un altro nuovo tipo introdotto da Delphi 4 e' il tipo Int64, il quale rappresenta numeri interi fino a 18 cifre. Questo nuovo tipo e' completamente supportato da diverse routine di tipo ordinale (come le funzioni Low e High), le routine numeriche (come Inc e Dec) e le routine di conversione delle stringhe. Per la conversione opposta (da stringa a numero), ci sono due nuove specifiche funzioni: StrToInt64 e StrToInt64Def.

Boolean

I valori booleani al di fuori del tipo Boolean sono raramente usati. Alcuni valori booleani con una specifica rappresentazione sono richiesti dalle funzioni API di Windows. I tipi sono ByteBool, WordBool e LongBool.

In Delphi 3 per compatibilita' con Visual Basic e OLE Automation, i tipi ByteBool, WordBool e LongBool sono stati modificati per rappresentare il valore True con -1, mentre il valore False e' ancora 0. Il tipo Boolean e' rimasto invariato (True e' 1, False e' 0). Portare codice Delphi 2 contenente typecast espliciti alle successive versioni di Delphi puo' portare quindi degli errori.

Caratteri

Ci sono 2 differenti rappresentazioni per i caratteri: ANSIChar e WideChar. Il primo tipo rappresenta i caratteri a 8 bit, che corrispondono al set di caratteri ANSI tradizionalmente usati da Windows, il secondo tipo rappresenta caratteri a 16 bit, che corrispondono ai nuovi caratteri Unicode supportati da Windows NT e solo parzialmente da Windows 95 e 98. Solitamente si usera' il tipo Char, che in Delphi 3 corrisponde a ANSIChar. Ricordarsi, tuttavia, che i primi 256 caratteri Unicode corrispondono esattamente ai caratteri ANSI.

Le costanti carattere possono essere rappresentate con la loro notazione simbolica, come in 'k', o con una notazione numerica, come in #78. L'ultima puo' anche essere scritta usando la funzione Ord, come in Ord(78).

In genere e' meglio usare la notazione simbolica per indicare lettere, numeri o simboli. Quando si usano caratteri speciali, invece, e' meglio usare la notazione numerica. La lista che segue elenca diversi caratteri speciali comunemente usati:

L'esempio Range

Per dare un'idea dei diversi intervalli dei tipi ordinali, ho scritto un esempio in Delphi chiamato Range. I risultati sono mostrati in Figura 3.1.

Figura 3.1: L'esempio Range mostra informazioni riguardo i tipi ordinali (in questo caso interi).

Il programma Range e' basato su un semplice form, il quale ha 6 pulsanti (ognuno chiamato con il nome del tipo) e alcune label per le categorie d'informazione, come si puo' vedere in Figura 3.1. Alcune label contengono testo statico, le altre mostrano le informazioni riguardo al tipo di dato ogni volta che preme un pulsante.

Ogni volta che un pulsante viene premuto, il programma aggiorna le etichette con i dati opportuni. Le informazioni mostrate sono: il tipo di dato, il numero di bytes usati e il massimo ed il minimo valore che il particolare tipo puo' contenere. Ogni pulsante ha la propria risposta all'evento OnClick visto che il codice usato per calcolare i tre valori e' leggermente diverso da pulsante a pulsante. Ad esempio, ecco il sorgente dell'evento OnClick del pulsante Integer (BtnInteger):

procedure TFormRange.BtnIntegerClick(Sender: TObject);
begin
  LabelType.Caption := 'Integer';
  LabelSize.Caption := IntToStr (SizeOf (Integer));
  LabelMax.Caption := IntToStr (High (Integer));
  LabelMin.Caption := IntToStr (Low (Integer));
end;

Se si ha esperienza di programmazione Delphi, si puo' esaminare il codice sorgente del programma per capire come funziona. Per i principianti, basta sottolineare l'uso di tre funzioni: SizeOf, High, e Low. I risultati delle ultime due funzioni sono ordinali dello stesso tipo (in questo caso interi) e il risultato della funzione SizeOf e' ancora un intero. Il valore di ritorno di ognuna di queste funzioni e' prima convertito in stringhe usando la funzione IntToStr, quindi copiato nella proprieta' Caption delle tre label.

I metodi associati con gli altri pulsanti sono simili a quello visto sopra. La sola differenza significativa e' nel tipo di dato passato come parametro alle varie funzioni. La Figura 3.2 mostra i risultati dell'esecuzione di questo programma sotto Windows 95 dopo essere stato compilato con la versione a 16 bit di Delphi. Paragonando la Figura 3.1 con la Figura 3.2, si possono notare le differenze tra il tipo Integer a 16 bit e a 32 bit.

Figura 3.2: L'output della versione a 16 bit dell'esempio Range, che mostra informazioni sul tipo Integer.

La dimensione del tipo Integer dipende dalla CPU e dal sistema operativo che si sta usando. In Windows a 16-bit, una variabile intera occupa due byte. In Windows a 32-bit, un intero occupa invece quattro byte. Per questa ragione, quando si ricompila l'esempio Range, si ottengono differenti risultati.

Le due differenti rappresentazioni del tipo Integer non sono un problema, fino a che il programma non si basa direttamente sulla dimensione degli interi. Se capita di salvare un intero in un file usando una versione e caricarlo usando l'altra versione, si avra' percio' lo stesso problema. In questo caso, bisogna scegliere un tipo di dato indipendente dalla piattaforma (come ad esempio il LongInt o lo SmallInt). Per i calcoli matematici o codice generico, e' meglio lavorare integralmente con la rappresentazione standard per la piattaforma in uso, ovvero usare il tipo Integer visto che e' il tipo che la CPU tratta in modo nativo. Il tipo Integer e' la scelta piu' ovvia quando si manipolano numeri. Usare quindi una differente rappresentazione solo quando c'e' una ragione per fare cio'.

Routine di tipo ordinale

Ci sono diverse routine di sistema (routine definite nel linguaggio Pascal e nelle unit di sistema di Delphi) che lavorano su tipi ordinali. Esse sono mostrate nella tabella 3.2. I programmatori C++ noteranno che le due versioni della procedura , con uno o due parametri, corrispondono agli operatori ++ e += (la stessa cosa vale per la procedura Dec).

Tabella 3.2: Routine di sistema per i tipi ordinali

RoutineScopo
DecDecrementa la variabile passata come parametro, di uno o del valore del secondo parametro opzionale.
IncIncrementa la variabile passata come parametro, di uno o del valore del secondo parametro opzionale.
OddRitorna True se l'argomento e' un numero dispari.
PredRitorna il valore precedente dell'argomento nell'ordine determinato dal tipo di dato.
SuccRitorna il valore successivo dell'argomento.
OrdRitorna un numero indicante l'ordine che l'argomento assume nell'insieme di valori del tipo di dato.
LowRitorna il minimo valore nell'intervallo del tipo ordinale passato come parametro.
HighRitorna il massimo valore nell'intervallo del tipo ordinale passato come parametro.

Notare che alcune di queste routine, quando applicatye su costanti, sono automaticamente valutate dal compilatore e rimpiazzate dal loro valore. Ad esempio se si scrive High(X) dove X e' difinito come intero, il compilatore puo' semplicemente sostituire l'espressione con il valore piu' alto del tipo Integer.

Tipi reali

I tipi reali rappresentano i numeri in virgola mobile (floating-point) in diversi formati. L'occupazione in memoria piu' piccola e' data dai numeri Single, che occupano 4 bytes. Poi ci sono i Double numeri in virgola mobile, che occupano 8 bytes e gli Extended, che occupano 10 bytes. Questi, sono tutti numeri floating-point con differente precisione, che corrispondono alla rappresentazione standard IEEE e sono direttamente supportati dal coprocessore numerico della CPU (FPU), per la massima velocita' di esecuzione.

In Delphi 2 e Delphi 3 il tipo Real ha la stessa definizione della versione di Delphi a 16-bit, era un tipo a 48-bit. L'uso del tipo Real e' sconsigliato da Borland, che suggerisce di usare invece i tipi Single ed Extended. La ragione e' che il vecchio formato a 6 bytes non e' supportato dalle CPU Intel e nemmeno elencato nei tipi reali IEEE.

Oltre ad avere il vantaggio di usare una definizione standard, questo cambio permette anche ai componenti di esporre proprieta' basate sui tipi reali, Delphi 3 non lo permetteva. Tra gli svantaggi ci possono essere problemi di compatibilita'. Se necessario, si puo' superare la possibilita' di incompatibilita' restando con la definizione del tipo di Delphi 2 e 3, per fare cio' usare la seguente opzione di compilazione:

{$REALCOMPATIBILITY ON}

Ci sono anche due strani tipi: il tipo Comp descrive interi di grosse dimensioni usando 8 bytes (possono contenere numeri di 18 cifre) e il tipo Currency (non disponibile nella versione a 16-bit di Delphi) che indica valori decimali a virgola fissa con 4 cifre decimali e la stessa rappresentazione a 64-bit del tipo Comp. Come suggerisce il nome, il tipo Currency e' stato aggiunto per gestire valori monetari estremamente precisi, con 4 posizioni decimali.

Non si puo' creare un programma simile all'esempio Range per i tipi reali, siccome non si possono usare le funzioni High e Low o la funzione Ord sulle variabili di tipo reale. I tipi reali rappresentano (in teoria) un insieme infinito di numeri, i tipi ordinali rappresentano un insieme finito di valori.

Nota: Spiego meglio la cosa. Quando si ha il numero intero 23 si puo' determinare con precisione quale e' il valore successivo. Gli interi sono finiti (hanno cioe' un intervallo definito e anche un ordine). I numeri in virgola mobile (appunto i numeri reali) sono infiniti anche su di un piccolo intervallo e non hanno un ordine: ad esempio, quanti valori ci sono tra 23 e 24? E che numero segue 23.46? Forse 23.47, 23.461 o 23.4601? E' davvero difficile saperlo!

Per questa ragione, ha senso chiedere la posizione ordinale del carattere w nell'intervallo del tipo Char, ma non ha nessun senso chiedere la stessa cosa per il numero 7143.1562 nell'intervallo di valori dei tipi reali. Tuttavia si puo' sapere se un numero reale ha un valore piu' grande di un altro numero, ma non ha senso chiedere quanti numeri esistono prima di un dato valore (questo e' il significato della funzione Ord).

I tipi reali hanno un ruolo limitato nella parte dell'interfaccia utente (dal lato Windows), ma essi sono pienamente supportati in Delphi, incluso la parte database. Il supporto dello standard IEEE per i tipi in virgola mobile rende l'Object Pascal assolutamente appropriato per una vasta scelta di programmi che richiedono calcoli numerici. Se si e' interessati a questo aspetto, si puo' guardare la unit system a proposito delle funzioni aritmetiche (vedere l'help di Delphi per maggiori dettagli)

Nota: Delphi ha anche una unit Math che definisce routine matematiche avanzate: funzioni trigonometriche (come la funzione ArcCosh), funzioni finanziarie (come la funzione InterestPayment) e funzioni statistiche (come la procedura MeanAndStdDev). Ci sono diverse routine, alcune delle quali mi risultano abbastanza strane, come la procedura MomentSkewKurtosis.

Data e Ora

Delphi usa i tipi reali per gestire le informazioni sulla data e l'ora. Per essere piu' precisi, Delphi definisce uno specifico tipo, il TDateTime. Questo e' un tipo floating-point, visto che deve essere grande abbastanza per memorizzare anni, mesi, giorni, minuti e secondi fino ad una risoluzione dei millisecondi in una singola variabile. Le date sono memorizzate in numeri di giorni dal 30/12/1899 (i valori negativi indicano le date prima del 1899) nella parte intera del valore TDateTime. L'ora e' memorizzata nella parte decimale del valore.

TDateTime non e' un tipo predefinito che il compilatore riconosce, ma e' definito nella unit system come:

type
  TDateTime = type Double;

Usare il tipo TDateTime e' abbastanza semplice, visto che Delphi mette a disposizione diverse funzioni per operare con questo tipo. Si puo' trovare un elenco di queste funzioni in Tabella 3.3.

Tabella 3.3: Routine di sistema per il tipo TDateTime

RoutineDescrizione
NowRitorna la data e l'ora corrente in un singolo valore di tipo TDateTime.
DateRitorna solamente la data corrente.
TimeRitorna solamente l'ora corrente.
DateTimeToStrConverte una data e ora in una stringa, usando la formattazione di default, per avere maggior controllo sulla conversione usare invece la funzione FormatDateTime.
DateTimeToStringCopia la data e l'ora in un buffer di tipo stringa, con la formattazione di default.
DateToStrConverte la parte data di una valore TDateTime in una stringa.
TimeToStrConverte la parte ora di una valore TDateTime in una stringa.
FormatDateTimeFormatta la data e l'ora usando uno specifico formato, si puo' specificare quale valori vedere e quale formato usare, fornendo una stringa formattata dettagliatamente.
StrToDateTimeConverte una stringa con informazioni di data e ora in un valore TDateTime, sollevando un'eccezione nel caso di un errore nel formato della stringa.
StrToDateConverte una stringa con informazioni sulla data in un valore TDateTime.
StrToTimeConverte una stringa con informazioni sull'ora in un valore TDateTime.
DayOfWeekRitorna il numero corrispondente al giorno della settimana di un avlore TDateTime passato come parametro.
DecodeDateRitorna l'anno, il mese ed il giorno di un valore TDateTime.
DecodeTimeRitorna l'ora di un valore TDateTime.
EncodeDateConverte anno, mese, giorno in un valore TDateTime.
EncodeTimeConverte ore, minuti, secondi, millisecondi in un valore TDateTime.

Per mostrare come usare questo tipo di dato e le funzioni ad esso correlate, ho creato un semplice esempio, chiamato TimeNow. Il form principale di qesto esempio ha un pulsante e un listbox. Quando il programma parte, automaticamente calcola e visualizza la data e l'ora corrente. Ogni volta che il pulsante e' premuto, il programma mostra il tempo passato da quando il programma e' partito.

Ecco il codice nell'evento OnCreate del form:

procedure TFormTimeNow.FormCreate(Sender: TObject);
begin
  StartTime := Now;
  ListBox1.Items.Add (TimeToStr (StartTime));
  ListBox1.Items.Add (DateToStr (StartTime));
  ListBox1.Items.Add ('Press button for elapsed time');
end;

La prima istruzione e' la chiamata alla funzione Now, la quale ritorna la data e l'ora corrente. Questo valore e' memorizzato nella variabile StartTime, dichiarata come variabile globale:

var
  FormTimeNow: TFormTimeNow;
  StartTime: TDateTime;

Ho aggiunto solo la seconda dichiarazione, siccome la prima e' opera di Delphi. Di solito, e' la seguente:

var
  Form1: TForm1;

Cambiando il nome del form, questa dichiarazione e' automaticamente aggiornata. Usando una variabile globale non e' davvero il miglior approccio: sarebbe meglio usare un campo privato della classe del form, un argomento correlato alla programmazione orientata agli oggetti e discussa in Mastering Delphi 4.

Le prossime tre istruzioni aggiungono tre elementi al listbox sul lato sinistro del form, con il risultato che si puo' vedere in Figura 3.3. La prima linea contiene la parte dell'ora del valore TDateTime convertita in stringa, la seconda linea contine la prte della data dello stesso valore. Alla fine il codice aggiunge una semplice nota.

Figura 3.3: L'output dell'esempio TimeNow alla partenza

Questa terza stringa e' sostituita dal programma quando l'utente preme il pulsante Elapsed:

procedure TFormTimeNow.ButtonElapsedClick(Sender: TObject);
var
  StopTime: TDateTime;
begin
  StopTime := Now;
  ListBox1.Items [2] :=  FormatDateTime ('hh:nn:ss',
    StopTime - StartTime);
end;

Questo codice recupera il nuovo valore dell'ora corrente e calcola la differenza con il valore memorizzato nella variabile quando il programma parte. Siccome si deve usare un valore che si calcola in un evento differente, bisogna immagazzinarlo in una variabile globale. Ci sono in realta' alternative migliori, basate sulle classi.

Nota: Il codice che sostituisce il valore della terza stringa usa l'indice 2. La ragione di questo e' che gli elementi di un listbox partono da zero: il primo quindi e' il numero 0, il secondo il numero 1, il terzo il numero 2.

Oltre alle funzioni TimeToStr e DateToStr si possono usare la piu' potente funzione FormatDateTime, come ho fatto nell'ultimo motodo sopra (guardare l'help di Delphi per maggiori dettagli sui parametri di formattazione). Notare anche che i valori di data e ora sono trasformati in stringhe seguendo i settaggi internazionali di Windows. Delphi legge questi valori dal sistema e li copia in costanti globali dichiarate nella unit SysUtils. Ecco alcune di queste costanti:

DateSeparator: Char;
ShortDateFormat: string;
LongDateFormat: string;
TimeSeparator: Char;
TimeAMString: string;
TimePMString: string;
ShortTimeFormat: string;
LongTimeFormat: string;
ShortMonthNames: array [1..12] of string;
LongMonthNames: array [1..12] of string;
ShortDayNames: array [1..7] of string;
LongDayNames: array [1..7] of string;

Diverse costanti sono relative alla formattazione delle valute e dei numeri floating-point. Si puo' trovare la lista completa nell'help di Delphi cercando l'argomento Currency and date/time formatting variables.

Nota: Delphi include un componente DateTimePicker, il quale fornisce un metodo sofisticato per l'input delle date, selezionabili da un calendario.

Tipi Windows specifici

I tipi predefiniti che abbiamo visto finora sono parte del linguaggio Pascal. Delphi introduce altri tipi di dato definiti da Windows. Questi tipi non sono parte del linguaggio, ma sono parte delle librerie di Windows. I tipi di Windows includono: nuovi tipi di default (come DWORD o UINT), numerosi record (o strutture), diversi tipi puntatore e cosi' via.

Tra i tipi di Windows, il piu' importante e' rappresentato dal tipo handle. Il nome di questo tipo e' THandle e il tipo e' definito nella unit Windows come:

type
  THandle = Integer;

Il tipo Handle e' implementato come numero, ma non sono usati come tali. In Windows, un handle e' un riferimento ad una struttura dati interna al sistema. Ad esempio, quando si lavora con le finestre (o un form di Delphi), il sistema fornisce un handle a questa finestra. Il sistema informa che la finestra in cui si sta lavorando e' la finestra numero 142, ad esempio. Da qui in poi, l'applicazione puo' chiedere al sistema di operare sulla finestra 142 per muoverla, ridimensionarla, ridurla ad icona e cosi' via. Diverse funzioni API di Windows, di fatto, hanno un handle come primo parametro. Questo non vale solo per le funzioni operanti su finestre: altre funzioni API di Windows hanno come primo parametro un GDI handle, un menu handle, un instance handle, o uno degli altri numerosi tipi di handle.

In altre parole, un handle e' un codice interno che si puo' usare per riferirsi ad uno specifico elemento gestito dal sistema, icluso finestre, bitmap, icone, blocchi di memoria, font, e cosi' via. In Delphi, raramente serve usare un handle direttamente, siccome essi sono nascosti all'interno dei for, dei bitmap e altri oggetti di Delphi. Essi diventano utili quando bisogna chiamare una funzione API di Windows non supportata da Delphi.

Nota: La dimensione del tipo handle, varia nelle versioni a 16-bit e 32-bit di Windows. La stessa cosa vale per le versioni a 16-bit e a 32-bit di Delphi. Se non vengono adottate le opportune cautele, si puo' incorrere in problemi di compatibilita' quando si passa l'applicazione tra le due piattaforme. Nella maggior parte dei casi, comunque, la dimensione degli handle non e' un problema.

Typecast e conversioni di tipo

Come si e' visto, non si puo' assegnare una variabile ad un altra di un tipo diverso. In caso in cui serve fare cio', ci sono due scelte. Laprima e' il typecasting, che usa una semplice notazione funzionale, con il nome del tipo di destinazione:

var
  N: Integer;
  C: Char;
  B: Boolean;
begin
  N := Integer ('X');
  C := Char (N);
  B := Boolean (0);

Si puo' effettuare il typecast tra tipi che hanno la stessa dimensione. Di solito e' sicuro il typecast tra tipi ordinali, o tra tipi reali ma si puo' effettuare il typecast anche tra puntatori (e quindi anche oggetti) purche' si sappia quel che si sta facendo.

Il typecasting, e' generalmente una tecnica di programmazione pericolosa, siccome permette di accedere un valore anche se rappresenta qualcosa d'altro. Siccome la rappresentazione interna dei tipi di dato generalmente non corrisponde, il rischio in cui si incorre e' un errore difficilmente rintracciabile. Per questa ragione, bisogna in genere evitare il typecasting.

La seconda scelta e' di usare le routine di conversione dei tipi. Le routine per i vari tipi di conversioni sono elencate nella Tabella 3.4. Alcune di queste routine lavorano sui tipi che verranno discussi nelle sezioni seguenti. Natare che la tabella non include le routine per i tipi speciali (come i TDateTime o i Variant) o routine specifiche per la formattazione, come le potenti funzioni Format e FormatFloat.

Tabella 3.4: Routine di sistema per le conversioni di tipo

RoutineDescrizione
ChrConverte un numero ordinale in un carattere ANSI.
OrdConverte un valore di tipo ordinale nel numero indicante la sua posizione.
RoundConverte un valore di tipo reale in un valore di tipo intero, arrotondando il valore.
TruncConverte un valore di tipo reale in un valore di tipo intero, troncando il valore.
IntRitorna la parte intera del valore floating-point passato come argomento.
IntToStrConverte un numero in una stringa.
IntToHexConverte un numero in una stringa con la sua rappresentazione esadecimale.
StrToIntConverte una stringa in un numero, sollevando un'eccezione se la stringa non rappresenta un intero valido.
StrToIntDefConverte una stringa in un numero, usando un valore di default se la stringa non e' corretta.
ValConverte una stringa in un numero (routine del TurboPascal, mantenuta per compatibilita').
StrConverte un numero in una stringa, usando parametri di formattazione (routine del TurboPascal, mantenuta per compatibilita').
StrPasConverte una stringa null-terminated in una stringa Pascal. Questa conversione e' automatica per il tipo AnsiString nelle versioni a 32-bit di Delphi. (Vedere la sezione sulle stringhe in questo capitolo)
StrPCopyCopia una stringa Pascal in una stringa null-terminated. Questa conversione e' effettuata con un semplice typecast su PChar in Delphi a 32-bit. (Vedere la sezione sulle stringhe in questo capitolo)
StrPLCopyCopia una porzione di una stringa Pascal in una stringa null-terminated.
FloatToDecimalConverte un valore floating-point in un record includendo la sua rappresentazione decimale (esponente, cifre, segno).
FloatToStrConverte un valore floating-point nella sua rappresentazione in stringa usando la formattazione di default.
FloatToStrFConverte un valore floating-point nella sua rappresentazione in stringa usando la formattazione specificata.
FloatToTextCopia un valore floating-point in un buffer di tipo stringa, usando al formattazione specificata.
FloatToTextFmtCome la precedente routine, copia un valore floating-point in un buffer di tipo stringa, usando la formattazione specificata
StrToFloatConverte una stringa Pascal in un valore floating-point.
TextToFloatConverte una stringa null-terminated in un valore floating-point.

Conclusioni

In questo capitolo si sono viste le nozioni di base dei tipi in Pascal. Il linguaggio, pero', ha un'altra importantissima caratteristicha: permette ai programmatori di definire nuovi tipi do dato, chiamati tipi di dato definiti dall'utente. Questo e' l'argomento del prossimo capitolo.

Prossimo Capitolo: Tipi di dato definiti dall'utente

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