Pascal fu creato nel 1970 da Niklaus Wirth come linguaggio didattico per insegnare la programmazione strutturata. Da allora si è evoluto in molteplici dialetti, alcuni dei quali sono diventati strumenti professionali per lo sviluppo di applicazioni reali.
Versione originale definita dallo standard ISO 7185. Linguaggio minimale, fortemente tipizzato, senza unit, senza OOP, senza stringhe dinamiche. Le stringhe sono array di char a lunghezza fissa. Oggi usato solo a scopo didattico/storico.
Estensione commerciale di Borland che introdusse: compilazione rapidissima, unit (moduli), tipo string con lunghezza fino a 255 char, accesso diretto alla memoria e porte I/O, inline assembly, e un IDE integrato. Le versioni 5.5+ aggiunsero un sistema OOP basato su object. Turbo Pascal dominò la programmazione DOS negli anni '80-'90.
Evoluzione radicale di Turbo Pascal. Introduce classi basate su TObject, ereditarietà singola, interfacce, eccezioni try/except, gestione automatica della memoria con reference counting per interfacce e stringhe, RTTI, Generics (Delphi 2009+), e il framework VCL/FMX per GUI. Oggi mantenuto da Embarcadero come prodotto commerciale. Compila per Windows, macOS, Linux, iOS, Android.
{$mode objfpc} o {$mode delphi} salvo dove specificato.
Free Pascal (FPC) è un compilatore open-source, multipiattaforma (Windows, Linux, macOS, ARM, ecc.) altamente compatibile con Turbo Pascal e Delphi. Supporta:
| Feature | Supporto FPC |
|---|---|
| Modalità compatibilità | {$mode tp}, {$mode delphi}, {$mode objfpc}, {$mode fpc} |
| Stringhe moderne | AnsiString (lunghezza illimitata, reference counted), UnicodeString |
| OOP completo | Classi, ereditarietà, interfacce, generics |
| Eccezioni | try..except..finally |
| Array dinamici | Supporto nativo con SetLength |
| Overloading | Procedure, funzioni e operatori |
| Target | x86, x86_64, ARM, AArch64, MIPS, PowerPC, RISC-V, WebAssembly |
Lazarus è l'IDE visuale (simile a Delphi) basato su FPC. Fornisce il framework LCL (Lazarus Component Library) per creare applicazioni GUI cross-platform.
| Dialetto | Note |
|---|---|
| Extended Pascal (ISO 10206) | Estensione dello standard con moduli, stringhe variabili, schemi. Poco usato. |
| GNU Pascal (GPC) | Frontend Pascal per GCC. Supporta ISO 7185/10206. Progetto inattivo. |
| PascalABC.NET | Pascal moderno per .NET. Pensato per la didattica, supporto lambda, LINQ-like, generics. |
| Oxygene (RemObjects) | Object Pascal per .NET/JVM/Cocoa/WebAssembly. Commerciale. |
| Virtual Pascal | Clone di Borland Pascal per OS/2 e Win32. Abbandonato. |
{ Compilazione base } fpc programma.pas { produce eseguibile 'programma' } { Con modalità Delphi e ottimizzazioni } fpc -Mdelphi -O2 programma.pas { Con modalità ObjFPC (raccomandato per FPC) } fpc -Mobjfpc -Sh programma.pas { -Sh abilita AnsiString di default } { Debug: include simboli per GDB } fpc -g -gl programma.pas { Specifica output e directory unit } fpc -ooutput -FUlib/ programma.pas
| Flag | Descrizione |
|---|---|
-Mdelphi | Modalità Delphi (compatibilità massima con Delphi) |
-Mobjfpc | Modalità ObjFPC (default FPC, richiede @ per puntatori a metodo) |
-Sh | Usa AnsiString come tipo string di default |
-O2 / -O3 | Livello di ottimizzazione |
-g | Genera informazioni di debug |
-gl | Genera info debug con numeri di riga |
-vh / -vw | Mostra hint / warning |
-FUdir | Directory output per file .ppu (unit compilate) |
-Fudir | Aggiunge directory di ricerca unit |
-dNOME | Definisce un simbolo per compilazione condizionale |
-Criot | Abilita controlli runtime (range, I/O, overflow) |
In Lazarus la compilazione avviene tramite il menu Run > Build (Ctrl+F9) o Run > Run (F9). Le opzioni del compilatore si configurano in Project > Project Options > Compiler Options.
program MioProgramma; { nome del programma } {$mode objfpc}{$H+} { modalità ObjFPC, AnsiString abilitate } uses SysUtils, Classes; { unit importate (simile a #include / import) } const MAX = 100; { costante } type TMyRecord = record { dichiarazione di tipo } Nome: String; Eta: Integer; end; var x: Integer; { variabili globali } s: String; { dichiarazione procedure/funzioni } procedure Saluta(Nome: String); begin WriteLn('Ciao, ' + Nome + '!'); end; { blocco principale del programma } begin x := 42; s := 'Mondo'; Saluta(s); WriteLn('x = ', x); end. { NOTA: end con il PUNTO termina il programma }
| Sezione | Descrizione |
|---|---|
program | Nome del programma (opzionale in FPC, obbligatorio nello standard) |
uses | Lista delle unit da importare |
const | Dichiarazione di costanti |
type | Dichiarazione di tipi personalizzati |
var | Dichiarazione di variabili |
| Procedure/Funzioni | Implementazione di sottoprogrammi |
begin..end. | Corpo principale del programma |
In Free Pascal le sezioni const, type, var possono essere ripetute e mescolate liberamente.
end. (con il punto). I blocchi interni terminano con end; (con il punto e virgola).
{ Commento tra parentesi graffe } (* Commento tra parentesi-asterisco — equivalente a { } *) // Commento di riga singola (Turbo Pascal / FPC / Delphi) { I commenti { } e (* *) NON si possono annidare tra loro. Ma { (* questo *) } è valido perché sono tipi diversi. }
42 { decimale } $2A { esadecimale (= 42) — prefisso $ } $FF { esadecimale (= 255) } %101010 { binario (= 42) — prefisso % (FPC) } &52 { ottale (= 42) — prefisso & (FPC) } 3.14 { virgola mobile } 1.5E3 { notazione scientifica (= 1500.0) } #65 { carattere per codice ASCII (= 'A') } #$41 { carattere per codice hex (= 'A') }
| Tipo | Dimensione | Range |
|---|---|---|
Byte | 1 byte | 0 .. 255 |
ShortInt | 1 byte | -128 .. 127 |
Word | 2 byte | 0 .. 65535 |
SmallInt | 2 byte | -32768 .. 32767 |
LongWord / Cardinal | 4 byte | 0 .. 4294967295 |
LongInt / Integer | 4 byte | -2³¹ .. 2³¹-1 |
QWord | 8 byte | 0 .. 2⁶⁴-1 |
Int64 | 8 byte | -2⁶³ .. 2⁶³-1 |
Integer in FPC è un alias per LongInt (32 bit). Usare NativeInt / NativeUInt per dimensione dipendente dalla piattaforma (32/64 bit).
| Tipo | Dimensione | Cifre significative |
|---|---|---|
Single | 4 byte | ~7 |
Double / Real | 8 byte | ~15 |
Extended | 10 byte | ~19 (piattaforma x86) |
Currency | 8 byte | Punto fisso, 4 decimali |
In FPC, Real è un alias per Double (8 byte). In Turbo Pascal era 6 byte.
| Tipo | Dimensione | Descrizione |
|---|---|---|
Char / AnsiChar | 1 byte | Singolo carattere ASCII/ANSI |
WideChar | 2 byte | Carattere Unicode UTF-16 |
Boolean | 1 byte | True o False |
ByteBool / WordBool / LongBool | 1/2/4 byte | Boolean compatibili con C (0 = False, qualsiasi altro = True) |
type { Subrange: restringono un tipo ordinale a un intervallo } TEta = 0..150; { sottointervallo di Integer } TVoto = 1..10; TLettera = 'A'..'Z'; { sottointervallo di Char } TGiornoLav = Lun..Ven; { sottointervallo di enum } { Type alias: nomi alternativi per tipi esistenti } TContatore = Integer; TNome = String; var eta: TEta; begin eta := 25; { ok } { eta := 200; — con {$R+} genera ERangeError a runtime } end;
Con il controllo range attivo ({$R+}), l'assegnamento fuori range genera un'eccezione ERangeError.
| Tipo | Descrizione |
|---|---|
ShortString | Stringa a lunghezza fissa (max 255 char). Compatibilità Turbo Pascal. |
AnsiString | Stringa dinamica, lunghezza illimitata, reference counted. Default con {$H+}. |
UnicodeString | Stringa Unicode UTF-16, reference counted. |
UTF8String | AnsiString con codepage UTF-8. |
PChar | Puntatore a char null-terminated (compatibilità C). |
String | Alias per ShortString (default) o AnsiString (con {$H+}). |
{$H+} (o -Sh) per avere AnsiString come default. Con Lazarus questo è già attivo nei progetti generati dall'IDE.const PI = 3.14159265358979; { tipo inferito: Double } MAX_ITEMS = 100; { tipo inferito: Integer } GREETING = 'Ciao Mondo'; { tipo inferito: String } NEWLINE = #10; { carattere LF (codice ASCII) } { Costanti tipizzate (in realtà sono variabili inizializzate!) } const Contatore: Integer = 0; { ATTENZIONE: modificabile se {$J+} (default) }
{$J+}). Per renderle veramente costanti, usa {$J-} o {$WRITEABLECONST OFF}.var x, y: Integer; { più variabili dello stesso tipo } Nome: String; Attivo: Boolean; Prezzo: Double; Lettera: Char; Dati: array[0..9] of Integer; { Variabili locali con inizializzazione (FPC estensione) } var Conteggio: Integer = 0; Messaggio: String = 'default';
{ Richiede {$mode delphi} oppure: } {$mode objfpc}{$modeswitch inlinevariables} begin var x: Integer := 42; { dichiarazione inline con tipo esplicito } var s := 'testo'; { con type inference } var d := 3.14; { inferisce Double } end;
| Operatore | Operazione | Esempio |
|---|---|---|
+ | Addizione | 5 + 3 → 8 |
- | Sottrazione | 5 - 3 → 2 |
* | Moltiplicazione | 5 * 3 → 15 |
/ | Divisione reale | 7 / 2 → 3.5 |
div | Divisione intera | 7 div 2 → 3 |
mod | Modulo (resto) | 7 mod 2 → 1 |
+ funziona anche come operatore di concatenazione per stringhe: 'Ciao' + ' ' + 'Mondo'.
| Operatore | Significato |
|---|---|
= | Uguale a |
<> | Diverso da |
< | Minore di |
> | Maggiore di |
<= | Minore o uguale a |
>= | Maggiore o uguale a |
in | Appartenenza a un set: 3 in [1..5] |
| Operatore | Significato | Esempio |
|---|---|---|
not | Negazione logica | not True → False |
and | AND logico | True and False → False |
or | OR logico | True or False → True |
xor | XOR logico | True xor True → False |
{$B-} (default) la valutazione è short-circuit: and/or non valutano il secondo operando se il risultato è già determinato. Con {$B+} la valutazione è completa.| Operatore | Significato | Esempio |
|---|---|---|
not | NOT bitwise | not $FF → $FFFFFF00 |
and | AND bitwise | $0F and $F0 → $00 |
or | OR bitwise | $0F or $F0 → $FF |
xor | XOR bitwise | $FF xor $0F → $F0 |
shl | Shift a sinistra | 1 shl 4 → 16 |
shr | Shift a destra | 16 shr 2 → 4 |
and, or, not, xor funzionano sia come operatori logici (su Boolean) che bitwise (su Integer). Il compilatore distingue in base al tipo degli operandi.
x := 42; { assegnamento: := (non = come in C) } x += 10; { incremento (FPC estensione, richiede {$mode objfpc} o {$mode delphi}) } x -= 5; { decremento } x *= 2; { moltiplicazione e assegnamento }
var i: Integer; d: Double; s: String; b: Boolean; { Conversione implicita: Integer -> Double } i := 42; d := i; { d = 42.0 } { Da Double a Integer (cast diretto non valido: dimensioni diverse) } i := Trunc(d); { tronca la parte decimale } i := Round(d); { arrotonda (banker's rounding) } { Cast esplicito (hard cast) — valido solo tra tipi ordinali } i := Integer('A'); { Char -> Integer: 65 } { Funzioni di conversione (unit SysUtils) } s := IntToStr(i); { Integer -> String } i := StrToInt(s); { String -> Integer (eccezione se invalido) } i := StrToIntDef(s, 0); { String -> Integer con valore default } s := FloatToStr(d); { Double -> String } d := StrToFloat(s); { String -> Double } s := BoolToStr(b, True); { Boolean -> 'True'/'False' } { Funzioni built-in } i := Ord('A'); { Char -> codice ASCII: 65 } s := Chr(65); { Codice ASCII -> Char: 'A' } i := Round(d); { arrotondamento banker's rounding } i := Trunc(d); { troncamento verso zero } { Format (simile a sprintf) } s := Format('Nome: %s, Età: %d, Media: %.2f', ['Mario', 30, 8.567]);
var s: String; i: Integer; d: Double; Codice: Integer; { codice di errore } { Str: converte numero in stringa (built-in, non richiede SysUtils) } Str(42, s); { s = '42' } Str(3.14:0:2, s); { s = '3.14' (formattato: 0 larghezza, 2 decimali) } Str(i:6, s); { s = ' 42' (larghezza 6, padding spazi) } { Val: converte stringa in numero (built-in, non lancia eccezioni!) } Val('42', i, Codice); { i = 42, Codice = 0 (successo) } Val('abc', i, Codice); { i = 0, Codice = 1 (posizione dell'errore) } Val('3.14', d, Codice); { d = 3.14, Codice = 0 } Val('$FF', i, Codice); { i = 255 (accetta anche hex!) } if Codice <> 0 then WriteLn('Errore alla posizione ', Codice) else WriteLn('Valore: ', i);
Val è più sicuro di StrToInt perché non lancia eccezioni: restituisce la posizione dell'errore nel parametro Codice (0 = successo). Ideale per parsing di input utente senza try..except. È anche più veloce perché non ha overhead di eccezioni.{ If semplice } if x > 0 then WriteLn('Positivo'); { If/Else } if x > 0 then WriteLn('Positivo') else WriteLn('Non positivo'); { NOTA: NON mettere ; prima di else! } { If con blocco begin..end } if (x > 0) and (x < 100) then begin WriteLn('Tra 1 e 99'); Inc(x); end else if x = 0 then WriteLn('Zero') else begin WriteLn('Fuori range'); x := 0; end;
(x > 0) and (x < 100). Senza parentesi, and ha precedenza più alta di > e il compilatore genererà un errore.case Voto of 10: WriteLn('Eccellente'); 8, 9: WriteLn('Ottimo'); 6..7: WriteLn('Sufficiente'); { range con .. } 1..5: WriteLn('Insufficiente'); else { oppure: otherwise (FPC) } WriteLn('Voto non valido'); end; { Case con blocchi begin..end } case Comando of 'a', 'A': begin WriteLn('Aggiungi'); Aggiungi(); end; 'q', 'Q': WriteLn('Esci'); end;
case funziona solo con tipi ordinali (Integer, Char, Boolean, Enum). Non funziona direttamente con stringhe (usare if..else if oppure una funzione helper).
{ For crescente } for i := 1 to 10 do WriteLn(i); { For decrescente } for i := 10 downto 1 do WriteLn(i); { For con blocco } for i := 0 to Length(Arr) - 1 do begin WriteLn('Elemento ', i, ': ', Arr[i]); end; { For-in (FPC 2.4.2+) } for Elemento in Arr do WriteLn(Elemento); { For-in su stringa } for c in 'Hello' do Write(c, ' ');
for non può essere modificata all'interno del ciclo. Il passo è sempre 1 (o -1 con downto).{ Condizione verificata PRIMA di ogni iterazione } while x > 0 do begin WriteLn(x); Dec(x); { equivalente a x := x - 1 } end;
{ Condizione verificata DOPO ogni iterazione (esegue almeno 1 volta) } { NOTA: repeat..until NON richiede begin..end } repeat WriteLn('Inserisci un numero positivo:'); ReadLn(x); until x > 0; { esce quando la condizione diventa True }
Break; { esce dal ciclo corrente } Continue; { salta alla prossima iterazione } Exit; { esce dalla procedura/funzione corrente } Exit(valore); { esce dalla funzione restituendo un valore (FPC) } Halt; { termina il programma immediatamente } Halt(1); { termina con exit code } { Inc e Dec (incremento/decremento) } Inc(x); { x := x + 1 } Inc(x, 5); { x := x + 5 } Dec(x); { x := x - 1 } Dec(x, 3); { x := x - 3 }
var Numeri: array[1..10] of Integer; { indice da 1 a 10 } Griglia: array[0..2, 0..3] of Double; { matrice 3x4 } Lettere: array['a'..'z'] of Boolean; { indice di tipo Char! } { Inizializzazione } const Valori: array[0..4] of Integer = (10, 20, 30, 40, 50); { Tipo array con nome } type TVettore = array[0..99] of Integer; var v: TVettore; { Accesso } Numeri[1] := 42; Griglia[1, 2] := 3.14; WriteLn(Low(Numeri), ' ', High(Numeri)); { 1 10 } WriteLn(Length(Numeri)); { 10 }
var Dati: array of Integer; { array dinamico (indice parte da 0) } Matrice: array of array of Double; begin SetLength(Dati, 5); { alloca 5 elementi: indici 0..4 } Dati[0] := 10; Dati[4] := 50; SetLength(Dati, 10); { ridimensiona: i vecchi dati sono preservati } WriteLn(Length(Dati)); { 10 } WriteLn(Low(Dati)); { sempre 0 per array dinamici } WriteLn(High(Dati)); { Length - 1 = 9 } { Inizializzazione diretta (FPC 3.2+) } Dati := [1, 2, 3, 4, 5]; { Concatenazione (FPC) } Dati := Concat(Dati, [6, 7, 8]); { Matrice dinamica } SetLength(Matrice, 3, 4); { 3 righe x 4 colonne } Matrice[0][0] := 1.0; { Liberare la memoria } SetLength(Dati, 0); { oppure: Dati := nil; } end;
{ Open array: accetta array di qualsiasi dimensione } function Somma(const Arr: array of Integer): Integer; var i: Integer; begin Result := 0; for i := Low(Arr) to High(Arr) do Result := Result + Arr[i]; end; { Chiamata con array costante } WriteLn(Somma([1, 2, 3, 4, 5])); { 15 }
type TPersona = record Nome: String; Cognome: String; Eta: Integer; Attivo: Boolean; end; var p: TPersona; begin p.Nome := 'Mario'; p.Cognome := 'Rossi'; p.Eta := 30; p.Attivo := True; { Blocco with (comodo, ma da usare con cautela) } with p do WriteLn(Nome, ' ', Cognome, ', età ', Eta); end;
{$modeswitch advancedrecords} type TVec2 = record X, Y: Double; class function Create(aX, aY: Double): TVec2; static; function Lunghezza: Double; function ToString: String; class operator +(A, B: TVec2): TVec2; end; class function TVec2.Create(aX, aY: Double): TVec2; begin Result.X := aX; Result.Y := aY; end; function TVec2.Lunghezza: Double; begin Result := Sqrt(X * X + Y * Y); end; function TVec2.ToString: String; begin Result := Format('(%.2f, %.2f)', [X, Y]); end; class operator TVec2.+(A, B: TVec2): TVec2; begin Result.X := A.X + B.X; Result.Y := A.Y + B.Y; end;
type TForma = record Colore: String; case Tipo: Integer of 0: (Raggio: Double); { cerchio } 1: (Larghezza, Altezza: Double); { rettangolo } 2: (Base, AltezzaTriangolo: Double); { triangolo } end;
I record variant condividono la stessa area di memoria per i campi variabili (simile a union in C). Utili per strutture con layout alternativo.
var s, s2: String; c: Char; begin s := 'Ciao Mondo'; c := s[1]; { 'C' — indice parte da 1! } { Lunghezza } WriteLn(Length(s)); { 10 } { Concatenazione } s2 := s + ' - Pascal'; s2 := Concat(s, ' - Pascal'); { equivalente } { Caratteri speciali } s := 'Prima riga' + #13#10 + 'Seconda riga'; { CR+LF } s := 'L''apostrofo'; { '' per apostrofo } s := 'Tab' + #9 + 'qui'; { tab } { Funzioni comuni (unit SysUtils) } s := UpperCase('ciao'); { 'CIAO' } s := LowerCase('CIAO'); { 'ciao' } s := Trim(' ciao '); { 'ciao' } s := TrimLeft(' ciao'); { 'ciao' } s := TrimRight('ciao '); { 'ciao' } { Ricerca } WriteLn(Pos('Mondo', s)); { posizione (1-based), 0 se non trovata } { Sottostringhe } s := Copy('Ciao Mondo', 6, 5); { 'Mondo' (da pos 6, 5 char) } { Modifica } Delete(s, 1, 5); { rimuove 5 char dalla posizione 1 } Insert('Salve ', s, 1); { inserisce alla posizione 1 } { Sostituzione (SysUtils) } s := StringReplace('aabaa', 'a', 'x', [rfReplaceAll]); { 'xxbxx' — flag: rfReplaceAll, rfIgnoreCase } end;
type TColore = (clRosso, clVerde, clBlu, clGiallo); TGiorno = (Lun, Mar, Mer, Gio, Ven, Sab, Dom); var c: TColore; g: TGiorno; begin c := clVerde; g := Mer; WriteLn(Ord(c)); { 1 (indice da 0) } WriteLn(Ord(g)); { 2 } { Iterare sugli enum } for c := Low(TColore) to High(TColore) do WriteLn(Ord(c)); { Successore e predecessore } g := Succ(Lun); { Mar } g := Pred(Dom); { Sab } end;
type TColoreSet = set of TColore; TCharSet = set of Char; var Colori: TColoreSet; Vocali: TCharSet; begin Colori := [clRosso, clBlu]; Vocali := ['a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U']; { Test di appartenenza } if clRosso in Colori then WriteLn('Rosso presente'); { Operazioni su set } Colori := Colori + [clVerde]; { unione (aggiungi) } Colori := Colori - [clRosso]; { differenza (rimuovi) } Colori := [clRosso, clBlu] * [clBlu]; { intersezione: [clBlu] } { Include / Exclude (più efficienti) } Include(Colori, clGiallo); Exclude(Colori, clBlu); { Uso pratico: validazione input } if c in ['0'..'9'] then WriteLn('È una cifra'); end;
I set possono contenere al massimo 256 elementi (tipo base ordinale con Ord in 0..255).
{ PROCEDURA: non restituisce un valore } procedure StampaLinea(Lunghezza: Integer); var i: Integer; begin for i := 1 to Lunghezza do Write('-'); WriteLn; end; { FUNZIONE: restituisce un valore } function Massimo(A, B: Integer): Integer; begin if A > B then Result := A { 'Result' è la variabile di ritorno implicita (FPC/Delphi) } else Result := B; end; { Alternativa: si può usare il nome della funzione } function Minimo(A, B: Integer): Integer; begin if A < B then Minimo := A { stile tradizionale (compatibile Standard Pascal) } else Minimo := B; end; { Exit con valore (FPC) — comodo per return anticipato } function Fattoriale(N: Integer): Int64; begin if N <= 1 then Exit(1); Result := N * Fattoriale(N - 1); { ricorsione } end;
{ Pascal supporta procedure/funzioni dichiarate dentro altre procedure. La procedura interna ha accesso alle variabili della procedura esterna. } procedure Ordinamento(var Arr: array of Integer); procedure Scambia(i, j: Integer); { annidata: accede a Arr } var tmp: Integer; begin tmp := Arr[i]; Arr[i] := Arr[j]; Arr[j] := tmp; end; var i, j: Integer; begin for i := Low(Arr) to High(Arr) - 1 do for j := i + 1 to High(Arr) do if Arr[j] < Arr[i] then Scambia(i, j); { chiama la procedura annidata } end;
Le procedure annidate sono utili per suddividere la logica senza esporre funzioni helper all'esterno. Possono accedere ai parametri e alle variabili locali del livello superiore (closure sullo stack).
{ Forward declaration: dichiara il prototipo prima dell'implementazione } procedure FunzA; forward; procedure FunzB; begin FunzA; { può chiamare FunzA perché è dichiarata forward } end; procedure FunzA; begin WriteLn('A'); end;
{ Per VALORE (default): la procedura riceve una copia } procedure PerValore(x: Integer); begin x := x * 2; { modifica la copia locale, non l'originale } end; { Per RIFERIMENTO (var): la procedura modifica l'originale } procedure PerRiferimento(var x: Integer); begin x := x * 2; { modifica direttamente la variabile del chiamante } end; { CONST: passaggio per riferimento (per efficienza) ma non modificabile } procedure PerCostante(const s: String); begin WriteLn(s); { ok: lettura } { s := 'altro'; — ERRORE: non si può modificare un parametro const } end; { OUT: come var, ma il valore iniziale è indefinito } procedure Inizializza(out x: Integer; out s: String); begin x := 0; s := 'inizializzato'; end; { CONSTREF: forza il passaggio per riferimento (mai copia) } procedure Elabora(constref Rec: TPersona); begin WriteLn(Rec.Nome); end;
| Modificatore | Copia? | Modificabile? | Uso tipico |
|---|---|---|---|
| (nessuno) | Sì | Solo copia locale | Tipi piccoli (Integer, Char, Boolean) |
var | No (riferimento) | Sì, modifica l'originale | Parametro di input/output |
const | Dipende dal tipo | No | Tipi grandi (String, Record) in sola lettura |
out | No (riferimento) | Sì | Solo output (valore iniziale ignorato) |
constref | No (sempre rif.) | No | Forzare riferimento senza copia |
{ Overloading: stessa funzione, parametri diversi } function Somma(A, B: Integer): Integer; overload; begin Result := A + B; end; function Somma(A, B: Double): Double; overload; begin Result := A + B; end; function Somma(A, B, C: Integer): Integer; overload; begin Result := A + B + C; end; { Parametri con valore default } procedure Log(Msg: String; Livello: Integer = 0); begin WriteLn('[', Livello, '] ', Msg); end; { Chiamate valide } Log('Avvio'); { Livello = 0 (default) } Log('Errore', 3); { Livello = 3 }
type PInteger = ^Integer; { tipo puntatore a Integer } var p: PInteger; x: Integer; ptr: Pointer; { puntatore generico (come void* in C) } begin x := 42; { Ottenere l'indirizzo } p := @x; { @ = operatore "indirizzo di" } { Dereferenziare } WriteLn(p^); { 42 — ^ dereferenzia il puntatore } p^ := 100; { modifica x attraverso il puntatore } WriteLn(x); { 100 } { Puntatore nil } p := nil; { puntatore nullo } if p <> nil then WriteLn(p^); if Assigned(p) then { equivalente: Assigned(p) = (p <> nil) } WriteLn(p^); { Puntatore generico } ptr := @x; WriteLn(PInteger(ptr)^); { cast a PInteger prima di dereferenziare } end;
type PNodo = ^TNodo; TNodo = record Valore: Integer; Prossimo: PNodo; { puntatore al prossimo nodo (lista collegata) } end; var Nodo: PNodo; begin New(Nodo); { alloca memoria per un TNodo } Nodo^.Valore := 42; Nodo^.Prossimo := nil; WriteLn(Nodo^.Valore); Dispose(Nodo); { libera la memoria } end;
var p: ^Integer; pRec: ^TPersona; begin { Alloca e libera un singolo valore } New(p); { alloca SizeOf(Integer) byte sull'heap } p^ := 42; WriteLn(p^); Dispose(p); { libera la memoria } p := nil; { buona pratica: annulla il puntatore } { Alloca un record complesso } New(pRec); pRec^.Nome := 'Mario'; pRec^.Eta := 30; Dispose(pRec); { Dispose libera anche le stringhe AnsiString nel record } end;
Dispose sa gestire correttamente i tipi managed (AnsiString, array dinamici, interfacce) contenuti nel record. FreeMem non lo fa — usare Dispose per i tipi tipizzati e FreeMem solo per buffer raw.var buf: Pointer; pArr: ^Byte; size: Integer; begin size := 1024; { Allocazione e deallocazione base } GetMem(buf, size); { alloca 'size' byte (come malloc) } FillChar(buf^, size, 0); { azzera la memoria (come memset) } FreeMem(buf, size); { libera (size opzionale in FPC) } { ReallocMem — ridimensionamento (come realloc) } GetMem(buf, 100); ReallocMem(buf, 200); { ridimensiona, preserva i dati } ReallocMem(buf, 0); { equivale a FreeMem } end;
var src, dst: array[0..99] of Byte; { FillChar: riempie N byte con un valore } FillChar(src, SizeOf(src), 0); { azzera tutto } FillChar(src, 50, Ord('A')); { riempie i primi 50 byte con 'A' } { FillByte: come FillChar ma il valore è Byte (semanticamente più chiaro) } FillByte(src, SizeOf(src), $FF); { Move: copia N byte da sorgente a destinazione (come memcpy) } Move(src, dst, SizeOf(src)); { NOTA: src e dst, NON src^ e dst^ } { CompareMem: confronta N byte (come memcmp) } if CompareMem(@src, @dst, SizeOf(src)) then WriteLn('Identici'); { SizeOf: dimensione in byte di un tipo o variabile } WriteLn(SizeOf(Integer)); { 4 } WriteLn(SizeOf(Pointer)); { 4 o 8 (dipende da 32/64 bit) } WriteLn(SizeOf(src)); { 100 }
Move in Pascal ha i parametri invertiti rispetto a memcpy in C: Move(sorgente, destinazione, dimensione). Inoltre riceve variabili, non puntatori — non usare ^.| Tipo di dato | Allocazione | Deallocazione | Note |
|---|---|---|---|
Puntatore tipizzato (^T) | New(p) | Dispose(p) | Gestisce tipi managed nel record |
| Buffer raw | GetMem(p, n) | FreeMem(p) | Nessuna gestione tipi managed |
| Array dinamici | SetLength(a, n) | SetLength(a, 0) | Automatica (reference counted) |
Stringhe (AnsiString) | Automatica | Automatica | Reference counted, nessun Free |
Oggetti (class) | T.Create | obj.Free | Sempre manuale! |
Pascal ha due sistemi OOP distinti. È importante conoscere entrambi, anche se oggi si usa quasi esclusivamente class.
type TForma = object { tipo value-type, allocato sullo stack } X, Y: Integer; constructor Init(aX, aY: Integer); destructor Done; virtual; procedure Disegna; virtual; end; TCerchio = object(TForma) { ereditarietà } Raggio: Integer; constructor Init(aX, aY, aR: Integer); procedure Disegna; virtual; end; var f: TForma; { allocato sullo stack, nessun New necessario } p: ^TForma; { oppure sull'heap con New/Dispose } begin f.Init(10, 20); f.Disegna; f.Done; New(p, Init(10, 20)); { allocazione heap + costruttore } p^.Disegna; Dispose(p, Done); { distruttore + deallocazione } end;
| Caratteristica | object (TP) | class (Delphi/FPC) |
|---|---|---|
| Allocazione | Stack (default) o heap con New | Sempre heap (TClasse.Create) |
| Tipo semantico | Value type (copia su assegnamento) | Reference type (copia il puntatore) |
| Classe base | Nessuna (standalone) | Sempre TObject (implicito) |
| Costruttore/Distruttore | Init / Done (per convenzione) | Create / Destroy |
| Liberazione | Automatica (stack) o Dispose | Manuale: .Free o FreeAndNil |
| Property | No | Sì |
| Interfacce | No | Sì |
| RTTI | No | Sì (sezione published) |
| Visibilità | private, public | private, protected, public, published, strict * |
| Uso oggi | Legacy, raro | Raccomandato per tutto il codice nuovo |
object con class. Nonostante la sintassi simile, sono incompatibili: non si può ereditare un object da una class o viceversa. Per codice nuovo, usare sempre class.{$mode objfpc} o {$mode delphi}. Tutte le classi ereditano implicitamente da TObject.type TAnimale = class { eredita implicitamente da TObject } private FNome: String; { campo privato (convenzione: prefisso F) } FEta: Integer; protected FSpecie: String; { accessibile alle sottoclassi } public constructor Create(aNome: String; aEta: Integer); destructor Destroy; override; procedure Parla; virtual; { metodo virtuale: può essere sovrascritto } function Descrizione: String; property Nome: String read FNome write FNome; property Eta: Integer read FEta; { sola lettura } end;
constructor TAnimale.Create(aNome: String; aEta: Integer); begin inherited Create; { chiama il costruttore di TObject } FNome := aNome; FEta := aEta; FSpecie := 'Sconosciuto'; end; destructor TAnimale.Destroy; begin { pulizia risorse se necessario } inherited Destroy; { SEMPRE chiamare inherited nel destructor } end; procedure TAnimale.Parla; begin WriteLn(FNome, ' fa un suono generico.'); end; function TAnimale.Descrizione: String; begin Result := Format('%s (%s), %d anni', [FNome, FSpecie, FEta]); end;
var a: TAnimale; begin a := TAnimale.Create('Fido', 5); try a.Parla; WriteLn(a.Nome); { accesso tramite property } WriteLn(a.Descrizione); finally a.Free; { Free = se non nil, chiama Destroy } end; end;
type TPersona = class private FNome: String; FEta: Integer; procedure SetEta(AValue: Integer); { setter con validazione } function GetInfo: String; { getter calcolato } public { Accesso diretto al campo } property Nome: String read FNome write FNome; { Con metodo setter (validazione) } property Eta: Integer read FEta write SetEta; { Sola lettura, calcolata } property Info: String read GetInfo; { Property con valore default (per componenti LCL) } property Attivo: Boolean read FAttivo write FAttivo default True; { Property indicizzata (array-like) } property Items[Index: Integer]: String read GetItem write SetItem; default; { 'default' senza valore rende la property accessibile con p[i] } end; procedure TPersona.SetEta(AValue: Integer); begin if (AValue >= 0) and (AValue <= 150) then FEta := AValue else raise ERangeError.Create('Età non valida'); end;
| Modificatore | Accessibile da |
|---|---|
private | Solo la stessa unit (in FPC: stessa unit, non stessa classe!) |
strict private | Solo la stessa classe |
protected | Stessa unit + sottoclassi |
strict protected | Solo sottoclassi |
public | Ovunque |
published | Come public + informazioni RTTI (usato nel RAD/LCL) |
type TCane = class(TAnimale) { eredita da TAnimale } public constructor Create(aNome: String; aEta: Integer); procedure Parla; override; { sovrascrive il metodo virtuale } procedure Riporta; end; TGatto = class(TAnimale) public constructor Create(aNome: String; aEta: Integer); procedure Parla; override; end; constructor TCane.Create(aNome: String; aEta: Integer); begin inherited Create(aNome, aEta); FSpecie := 'Canis familiaris'; end; procedure TCane.Parla; begin WriteLn(FNome, ' dice: Bau bau!'); end; procedure TCane.Riporta; begin WriteLn(FNome, ' riporta la palla.'); end; constructor TGatto.Create(aNome: String; aEta: Integer); begin inherited Create(aNome, aEta); FSpecie := 'Felis catus'; end; procedure TGatto.Parla; begin WriteLn(FNome, ' dice: Miao!'); end; { Polimorfismo in azione } var Animali: array[0..1] of TAnimale; a: TAnimale; begin Animali[0] := TCane.Create('Rex', 4); Animali[1] := TGatto.Create('Whiskers', 2); for a in Animali do begin a.Parla; { chiama il metodo corretto (late binding) } WriteLn(a.Descrizione); { Type check } if a is TCane then (a as TCane).Riporta; { 'is' verifica, 'as' fa il cast sicuro } end; for a in Animali do a.Free; end;
| Dichiarazione | Comportamento |
|---|---|
procedure Foo; | Statico: il metodo chiamato dipende dal tipo della variabile |
procedure Foo; virtual; | Virtuale: introduce un nuovo slot nella VMT |
procedure Foo; override; | Sovrascrive il metodo virtuale del genitore |
procedure Foo; virtual; abstract; | Astratto: nessuna implementazione, le sottoclassi DEVONO sovrascriverlo |
procedure Foo; reintroduce; | Nasconde il metodo virtuale del genitore (nuovo slot, nessun polimorfismo) |
type IStampabile = interface ['{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}'] { GUID opzionale } function ToText: String; procedure Stampa; end; ISalvabile = interface procedure Salva(NomeFile: String); procedure Carica(NomeFile: String); end; { Classe che implementa più interfacce } TDocumento = class(TInterfacedObject, IStampabile, ISalvabile) private FTesto: String; public constructor Create(aTesto: String); { implementazione IStampabile } function ToText: String; procedure Stampa; { implementazione ISalvabile } procedure Salva(NomeFile: String); procedure Carica(NomeFile: String); end;
TInterfacedObject fornisce il reference counting automatico. Quando l'ultima variabile interfaccia esce dallo scope, l'oggetto viene liberato automaticamente.
{$mode objfpc}{$H+} type generic TStack<T> = class private FItems: array of T; FCount: Integer; public constructor Create; procedure Push(Item: T); function Pop: T; function Peek: T; property Count: Integer read FCount; end; constructor TStack.Create; begin FCount := 0; SetLength(FItems, 4); end; procedure TStack.Push(Item: T); begin if FCount = Length(FItems) then SetLength(FItems, FCount * 2); FItems[FCount] := Item; Inc(FCount); end; function TStack.Pop: T; begin Dec(FCount); Result := FItems[FCount]; end; function TStack.Peek: T; begin Result := FItems[FCount - 1]; end; { Specializzazione (mode objfpc) } type TIntStack = specialize TStack<Integer>; TStringStack = specialize TStack<String>; { Utilizzo } var Stack: TIntStack; begin Stack := TIntStack.Create; try Stack.Push(10); Stack.Push(20); WriteLn(Stack.Pop); { 20 } finally Stack.Free; end; end;
{$mode delphi} { In mode delphi, la sintassi è più compatta } type TStack<T> = class { niente 'generic' keyword } ... end; var Stack: TStack<Integer>; { niente 'specialize' keyword }
uses fgl; type TIntList = specialize TFPGList<Integer>; TStringList2 = specialize TFPGList<String>; TIntMap = specialize TFPGMap<String, Integer>; var Lista: TIntList; begin Lista := TIntList.Create; try Lista.Add(10); Lista.Add(20); Lista.Add(30); WriteLn(Lista[1]); { 20 } WriteLn(Lista.Count); { 3 } Lista.Sort(@CompareFunc); { ordinamento con funzione comparatore } finally Lista.Free; end; end;
Write('Testo senza a capo'); WriteLn('Testo con a capo'); WriteLn; { riga vuota } { Parametri multipli di tipi diversi (NON serve Format!) } WriteLn('Nome: ', Nome, ' Età: ', Eta, ' Attivo: ', Attivo); { Output: Nome: Mario Età: 30 Attivo: TRUE } { Formattazione con : (specificatori di larghezza) } WriteLn(x:10); { intero, larghezza campo 10 (allineato a dx) } WriteLn(d:10:2); { float, larghezza 10, 2 decimali } WriteLn(b:8); { boolean, larghezza 8: ' TRUE' } WriteLn(s:20); { stringa, larghezza 20 (padding a sinistra) } { Esempio: tabella allineata } WriteLn('Prodotto':15, 'Prezzo':10, 'Qtà':6); WriteLn('Mela':15, 1.50:10:2, 12:6); WriteLn('Banana':15, 0.80:10:2, 5:6); { Prodotto Prezzo Qtà Mela 1.50 12 Banana 0.80 5 }
var Nome: String; x: Integer; d: Double; c: Char; { ReadLn: legge e consuma il newline } Write('Inserisci un numero: '); ReadLn(x); { legge Integer, poi scarta fino a fine riga } Write('Inserisci il nome: '); ReadLn(Nome); { legge l'intera riga come stringa } { Read: legge ma NON consuma il newline (resta nel buffer) } Read(x); { il newline rimane → il prossimo ReadLn lo legge vuoto! } { Lettura multipla sulla stessa riga } Write('Due numeri separati da spazio: '); ReadLn(x, d); { legge Integer poi Double, poi scarta il resto } { Leggere un singolo carattere } Read(c); { legge 1 char (attende Enter) } { ReadLn senza parametri: attende che l'utente prema Enter } WriteLn('Premi Invio per continuare...'); ReadLn;
Read(x) (senza Ln), il newline resta nel buffer. Se subito dopo si fa ReadLn(Nome), la stringa sarà vuota! Soluzione: usare sempre ReadLn oppure inserire un ReadLn di pulizia tra le due letture.uses SysUtils; WriteLn(Format('%-20s %5d %8.2f', ['Prodotto', 42, 3.14]));
| Specificatore | Descrizione | Esempio |
|---|---|---|
%d | Intero decimale | Format('%d', [42]) → '42' |
%5d | Intero, larghezza minima 5 | Format('%5d', [42]) → ' 42' |
%x | Intero esadecimale | Format('%x', [255]) → 'FF' |
%f | Float a punto fisso | Format('%f', [3.14]) → '3.14' |
%.2f | Float con 2 decimali | Format('%.2f', [3.1]) → '3.10' |
%e | Notazione scientifica | Format('%e', [1500.0]) → '1.5E+003' |
%g | Generale (sceglie f o e) | Format('%g', [0.001]) → '0.001' |
%s | Stringa | Format('%s', ['abc']) → 'abc' |
%-20s | Stringa, allineata a sinistra, larg. 20 | Format('%-20s|', ['abc']) → 'abc |' |
%p | Puntatore (indirizzo hex) | Format('%p', [ptr]) |
%% | Carattere % letterale | Format('100%%') → '100%' |
{ Approccio tradizionale senza eccezioni: usare {$I-} / {$I+} e IOResult } var f: TextFile; {$I-} { disabilita eccezioni I/O } AssignFile(f, 'inesistente.txt'); Reset(f); if IOResult <> 0 then { IOResult = 0 se ok, altrimenti codice errore } WriteLn('File non trovato!') else begin { ... leggi il file ... } CloseFile(f); end; {$I+} { riabilita eccezioni I/O } { NOTA: IOResult resetta a 0 dopo la lettura! Deve essere letto UNA SOLA volta subito dopo l'operazione. }
var f: TextFile; begin AssignFile(f, 'output.txt'); { associa la variabile file al percorso } Rewrite(f); { crea il file (sovrascrive se esiste!) } WriteLn(f, 'Riga 1'); WriteLn(f, 'Riga 2'); Write(f, 'Senza a capo'); { Write funziona anche su file } WriteLn(f, 42:6, 3.14:8:2); { formattazione con : funziona anche su file } CloseFile(f); { SEMPRE chiudere! Flushes i buffer. } { Append: aggiunta in coda (NON sovrascrive) } AssignFile(f, 'output.txt'); Append(f); { apre per aggiunta — errore se il file non esiste } WriteLn(f, 'Riga aggiunta'); CloseFile(f); end;
var f: TextFile; Linea: String; x: Integer; d: Double; begin AssignFile(f, 'dati.txt'); Reset(f); { apre in lettura } { Lettura riga per riga } while not Eof(f) do begin ReadLn(f, Linea); WriteLn(Linea); end; { Lettura di valori tipizzati da file testo } Reset(f); { riporta all'inizio } while not Eof(f) do begin ReadLn(f, x, d); { legge Integer e Double dalla stessa riga } WriteLn('Int: ', x, ' Float: ', d:0:2); end; { Lettura carattere per carattere } Reset(f); while not Eof(f) do begin while not Eoln(f) do { Eoln = End of Line } begin Read(f, c); Write(c); end; ReadLn(f); { consuma il newline } WriteLn; end; CloseFile(f); end;
| Procedura | Modalità | File deve esistere? | Posizione |
|---|---|---|---|
Rewrite(f) | Scrittura (crea/sovrascrive) | No (lo crea) | Inizio |
Reset(f) | Sola lettura | Sì | Inizio |
Append(f) | Scrittura in coda | Sì | Fine |
var f: TextFile; Linea: String; begin AssignFile(f, 'dati.txt'); Reset(f); try while not Eof(f) do begin ReadLn(f, Linea); { ... elabora ... } end; finally CloseFile(f); { chiude il file anche se c'è un'eccezione } end; end;
type TRecord = record ID: Integer; Nome: String[50]; { ShortString a lunghezza fissa per file binari! } end; var f: file of TRecord; Rec: TRecord; begin { Scrittura } AssignFile(f, 'dati.bin'); Rewrite(f); Rec.ID := 1; Rec.Nome := 'Mario'; Write(f, Rec); Rec.ID := 2; Rec.Nome := 'Luigi'; Write(f, Rec); Rec.ID := 3; Rec.Nome := 'Peach'; Write(f, Rec); CloseFile(f); { Lettura sequenziale } AssignFile(f, 'dati.bin'); Reset(f); { per file tipizzati: Reset apre in lettura/scrittura } while not Eof(f) do begin Read(f, Rec); WriteLn(Rec.ID, ': ', Rec.Nome); end; { Accesso diretto (random access) } Seek(f, 1); { posiziona al record #1 (0-based) } Read(f, Rec); { legge 'Luigi' } { Modifica di un record in-place } Seek(f, 1); Rec.Nome := 'LUIGI (aggiornato)'; Write(f, Rec); { sovrascrive il record #1 } { Informazioni sul file } WriteLn(FileSize(f)); { numero totale di record (3) } WriteLn(FilePos(f)); { posizione corrente del cursore } { Tronca il file dalla posizione corrente } Seek(f, 2); Truncate(f); { il file ora contiene solo i record 0 e 1 } CloseFile(f); end;
AnsiString nei record per file tipizzati! Le AnsiString sono puntatori — verrebbe scritto l'indirizzo, non il contenuto. Usare sempre String[N] (ShortString a lunghezza fissa).var fIn, fOut: file; { 'file' senza 'of tipo' = file non tipizzato } buf: array[0..4095] of Byte; ByteLetti: Integer; begin { Copia binaria di un file a blocchi di 4KB } AssignFile(fIn, 'input.dat'); AssignFile(fOut, 'output.dat'); Reset(fIn, 1); { il 2° param = dimensione record in byte (1 = byte) } Rewrite(fOut, 1); repeat BlockRead(fIn, buf, SizeOf(buf), ByteLetti); { legge fino a 4096 byte } if ByteLetti > 0 then BlockWrite(fOut, buf, ByteLetti); { scrive i byte letti } until ByteLetti = 0; CloseFile(fIn); CloseFile(fOut); end;
BlockRead/BlockWrite sono i più efficienti per I/O binario a blocchi. Il quarto parametro (opzionale) restituisce i byte effettivamente letti/scritti.
uses Classes, SysUtils; var fs: TFileStream; buf: array[0..255] of Byte; s: String; n: Integer; begin { Scrittura } fs := TFileStream.Create('stream.bin', fmCreate); try n := 42; fs.Write(n, SizeOf(n)); { scrive 4 byte } s := 'Ciao'; n := Length(s); fs.Write(n, SizeOf(n)); { scrive la lunghezza } fs.Write(s[1], n); { scrive i caratteri } finally fs.Free; end; { Lettura } fs := TFileStream.Create('stream.bin', fmOpenRead); try fs.Read(n, SizeOf(n)); { legge 4 byte in n } WriteLn('Numero: ', n); fs.Read(n, SizeOf(n)); { legge lunghezza stringa } SetLength(s, n); fs.Read(s[1], n); { legge i caratteri } WriteLn('Stringa: ', s); WriteLn('Dimensione: ', fs.Size); { dimensione totale del file } WriteLn('Posizione: ', fs.Position); { posizione corrente } fs.Position := 0; { equivalente a Seek } finally fs.Free; end; end;
| Flag apertura | Descrizione |
|---|---|
fmCreate | Crea il file (sovrascrive se esiste) |
fmOpenRead | Apre in sola lettura |
fmOpenWrite | Apre in sola scrittura |
fmOpenReadWrite | Apre in lettura/scrittura |
uses Classes; var SL: TStringList; Contenuto: String; begin { Con TStringList (riga per riga) } SL := TStringList.Create; try SL.LoadFromFile('input.txt'); { carica tutto il file } WriteLn('Righe: ', SL.Count); WriteLn('Riga 0: ', SL[0]); Contenuto := SL.Text; { tutto il file come unica stringa } SL.Add('Nuova riga'); SL.SaveToFile('output.txt'); { salva tutto } finally SL.Free; end; end;
uses SysUtils; { Test esistenza } if FileExists('file.txt') then ... if DirectoryExists('/tmp') then ... { Operazioni su file } DeleteFile('vecchio.txt'); { restituisce True se ok } RenameFile('old.txt', 'new.txt'); { restituisce True se ok } CopyFile('src.txt', 'dst.txt'); { FPC: unit FileUtil } { Operazioni su directory } CreateDir('nuova_dir'); { crea una directory (un livello) } ForceDirectories('/tmp/a/b/c'); { crea l'intero percorso } RemoveDir('dir_vuota'); { rimuove dir (deve essere vuota) } s := GetCurrentDir; { directory corrente } SetCurrentDir('/tmp'); { cambia directory } s := GetTempDir; { directory temporanea di sistema } { Manipolazione percorsi } s := ExtractFileName('/path/to/file.txt'); { 'file.txt' } s := ExtractFileExt('file.txt'); { '.txt' } s := ExtractFilePath('/path/to/file.txt'); { '/path/to/' } s := ExtractFileDir('/path/to/file.txt'); { '/path/to' (senza / finale) } s := ExtractFileNameOnly('file.txt'); { 'file' (FPC) } s := ChangeFileExt('file.txt', '.bak'); { 'file.bak' } s := ConcatPaths(['/home', 'user', 'docs']); { '/home/user/docs' (FPC) } s := IncludeTrailingPathDelimiter('/tmp'); { '/tmp/' } s := ExcludeTrailingPathDelimiter('/tmp/'); { '/tmp' } { Informazioni file } WriteLn(FileAge('file.txt')); { timestamp (TDateTime encoded) } WriteLn(FileDateToDateTime(FileAge('f.txt')));
uses SysUtils; var SR: TSearchRec; begin { Cerca tutti i file .pas nella directory corrente } if FindFirst('*.pas', faAnyFile, SR) = 0 then begin try repeat if (SR.Attr and faDirectory) = 0 then { escludi directory } WriteLn(SR.Name, ' ', SR.Size, ' bytes'); until FindNext(SR) <> 0; finally FindClose(SR); { SEMPRE chiamare FindClose! } end; end; end;
| Attributo | Significato |
|---|---|
faReadOnly | File di sola lettura |
faHidden | File nascosto |
faDirectory | È una directory |
faSymLink | Link simbolico (FPC) |
faAnyFile | Qualsiasi tipo di file |
unit MiaUnit; {$mode objfpc}{$H+} interface { sezione pubblica: ciò che viene esportato } uses SysUtils; const VERSIONE = '1.0'; type TCalcolatrice = class public function Somma(A, B: Double): Double; function Moltiplica(A, B: Double): Double; end; function DoQualcosa(X: Integer): Integer; implementation { sezione privata: implementazione } uses Math; { unit usate solo nell'implementazione } function TCalcolatrice.Somma(A, B: Double): Double; begin Result := A + B; end; function TCalcolatrice.Moltiplica(A, B: Double): Double; begin Result := A * B; end; function DoQualcosa(X: Integer): Integer; begin Result := X * 2; end; initialization { opzionale: codice eseguito all'avvio } WriteLn('Unit MiaUnit inizializzata.'); finalization { opzionale: codice eseguito alla chiusura } WriteLn('Unit MiaUnit finalizzata.'); end.
| Unit | Contenuto |
|---|---|
System | Sempre inclusa. Tipi base, I/O, gestione memoria. |
SysUtils | Conversioni, file, date, eccezioni, formato. |
Classes | TStringList, TStream, TComponent, TList, ecc. |
Math | Funzioni matematiche avanzate. |
StrUtils | Utility aggiuntive per stringhe. |
DateUtils | Manipolazione date/ore avanzata. |
fgl | Collezioni generiche (TFPGList, TFPGMap, ecc.). |
Generics.Collections | Collezioni generiche stile Delphi (FPC 3.2+). |
Forms, Controls, Dialogs | LCL per applicazioni GUI Lazarus. |
uses SysUtils; { Try..Except: gestione degli errori } try x := StrToInt('abc'); { genera EConvertError } except on E: EConvertError do WriteLn('Errore conversione: ', E.Message); on E: ERangeError do WriteLn('Errore range: ', E.Message); on E: Exception do { cattura qualsiasi eccezione } WriteLn('Errore generico: ', E.Message); end; { Try..Finally: pulizia risorse (eseguito sempre) } var Lista: TStringList; begin Lista := TStringList.Create; try Lista.Add('uno'); Lista.Add('due'); { ... usa la lista ... } finally Lista.Free; { viene SEMPRE eseguito } end; end; { Try..Except..Finally combinato } try try { codice rischioso } except on E: Exception do WriteLn(E.Message); end; finally { pulizia } end; { Lanciare un'eccezione } raise Exception.Create('Qualcosa è andato storto'); raise Exception.CreateFmt('Errore nel file %s alla riga %d', [NomeFile, Riga]); { Eccezione personalizzata } type EMiaEccezione = class(Exception); raise EMiaEccezione.Create('Errore specifico');
| Eccezione | Quando viene lanciata |
|---|---|
EConvertError | Conversione di tipo fallita (StrToInt, ecc.) |
ERangeError | Indice fuori range (con {$R+}) |
EOverflow | Overflow aritmetico (con {$Q+}) |
EDivByZero | Divisione per zero |
EAccessViolation | Accesso a memoria non valida (SIGSEGV) |
EInOutError | Errore I/O su file |
EOutOfMemory | Memoria insufficiente |
EInvalidPointer | Operazione su puntatore non valido |
| Direttiva | Descrizione |
|---|---|
{$mode objfpc} | Modalità ObjFPC (default raccomandato) |
{$mode delphi} | Modalità compatibilità Delphi |
{$H+} / {$H-} | AnsiString / ShortString come default per String |
{$R+} / {$R-} | Abilita/disabilita controllo range |
{$Q+} / {$Q-} | Abilita/disabilita controllo overflow |
{$I+} / {$I-} | Abilita/disabilita controllo errori I/O |
{$B+} / {$B-} | Valutazione completa / short-circuit dei booleani |
{$J+} / {$J-} | Costanti tipizzate scrivibili / non scrivibili |
{$INLINE ON} | Abilita il suggerimento inline per procedure |
{$IFDEF WINDOWS} WriteLn('Compilato per Windows'); {$ENDIF} {$IFDEF LINUX} WriteLn('Compilato per Linux'); {$ENDIF} {$IFDEF CPU64} WriteLn('Architettura 64 bit'); {$ELSE} WriteLn('Architettura 32 bit'); {$ENDIF} { Simboli predefiniti utili: } { FPC, FPC_FULLVERSION, WINDOWS, LINUX, DARWIN, UNIX, } { CPU32, CPU64, CPUARM, CPUI386, CPUX86_64, ENDIAN_BIG/LITTLE } { Definire simboli personalizzati } {$DEFINE DEBUG} {$IFDEF DEBUG} WriteLn('Modalità debug'); {$ENDIF} {$UNDEF DEBUG}
{ Inclusione di file sorgente } {$I nomefile.inc} { inserisce il contenuto del file qui } { Risorse Lazarus (per icone, ecc.) } {$R *.lfm} { file form Lazarus } {$R risorse.res} { file risorse Windows }
| Elemento | Convenzione | Esempio |
|---|---|---|
| Tipi | Prefisso T | TPersona, TCalcolatrice |
| Classi eccezione | Prefisso E | EMiaEccezione |
| Interfacce | Prefisso I | IStampabile |
| Tipi puntatore | Prefisso P | PInteger, PNodo |
| Campi privati | Prefisso F | FNome, FEta |
| Parametri costruttore | Prefisso a | aNome, aEta |
| Variabili globali | Prefisso G | GConfig |
| Identificatori | PascalCase | CalcolaSomma |
uses SysUtils; { Libera l'oggetto E imposta il riferimento a nil } FreeAndNil(MioOggetto); { Equivale a: } MioOggetto.Free; MioOggetto := nil;
uses Classes; var SL: TStringList; begin SL := TStringList.Create; try { Come lista di stringhe } SL.Add('Prima'); SL.Add('Seconda'); SL.Add('Terza'); WriteLn(SL[0]); { 'Prima' } WriteLn(SL.Count); { 3 } { Ordinamento } SL.Sort; { Come mappa chiave=valore } SL.Clear; SL.Values['nome'] := 'Mario'; SL.Values['eta'] := '30'; WriteLn(SL.Values['nome']); { 'Mario' } { Carica/Salva da file } SL.SaveToFile('dati.txt'); SL.LoadFromFile('dati.txt'); { Testo delimitato } SL.Delimiter := ';'; SL.DelimitedText := 'uno;due;tre'; WriteLn(SL[1]); { 'due' } { Unire tutte le stringhe } WriteLn(SL.Text); { tutte le righe con a capo } SL.LineBreak := ', '; WriteLn(SL.Text); { 'uno, due, tre' } finally SL.Free; end; end;
uses SysUtils, DateUtils; var Adesso: TDateTime; begin Adesso := Now; WriteLn(DateToStr(Adesso)); { '29/03/2026' } WriteLn(TimeToStr(Adesso)); { '14:30:00' } WriteLn(FormatDateTime('yyyy-mm-dd hh:nn:ss', Adesso)); WriteLn(YearOf(Adesso)); { 2026 } WriteLn(DaysBetween(Adesso, EncodeDate(2025, 1, 1))); end;
uses Math; WriteLn(Min(3, 7)); { 3 } WriteLn(Max(3, 7)); { 7 } WriteLn(Power(2, 10)); { 1024.0 } WriteLn(Log2(1024)); { 10.0 } WriteLn(Ceil(3.2)); { 4 } WriteLn(Floor(3.8)); { 3 } WriteLn(EnsureRange(15, 0, 10)); { 10 (clamp) } WriteLn(InRange(5, 1, 10)); { True } WriteLn(RandomRange(1, 100)); { random tra 1 e 99 } { Built-in (unit System, sempre disponibili) } WriteLn(Abs(-42)); { 42 } WriteLn(Sqr(5)); { 25 (quadrato) } WriteLn(Sqrt(25)); { 5.0 (radice quadrata) } WriteLn(Round(3.7)); { 4 } WriteLn(Trunc(3.7)); { 3 } WriteLn(Odd(7)); { True (dispari) } Randomize; { inizializza il seed random } WriteLn(Random(100)); { 0..99 }
WriteLn('Programma: ', ParamStr(0)); { nome eseguibile } WriteLn('N. parametri: ', ParamCount); var i: Integer; for i := 1 to ParamCount do WriteLn('Param ', i, ': ', ParamStr(i));
type TOperazione = function(A, B: Integer): Integer; function Somma(A, B: Integer): Integer; begin Result := A + B; end; function Prodotto(A, B: Integer): Integer; begin Result := A * B; end; procedure Applica(Op: TOperazione; X, Y: Integer); begin WriteLn('Risultato: ', Op(X, Y)); end; begin Applica(@Somma, 3, 4); { Risultato: 7 (@ richiesto in mode objfpc) } Applica(@Prodotto, 3, 4); { Risultato: 12 } end;
Pascal è case-insensitive: WriteLn, WRITELN, writeln sono la stessa cosa. Per convenzione si usa PascalCase per identificatori e parole chiave in minuscolo (begin, end, if...) o tutto minuscolo.