VERSIONI & DIALETTI

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.

Standard Pascal (Wirth, 1970)

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.

Turbo Pascal (Borland, 1983-1998)

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.

Object Pascal / Delphi (Borland/Embarcadero, 1995-oggi)

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.

Free Pascal (FPC) & Lazarus
NOTA: Questa cheatsheet si basa principalmente su Free Pascal (FPC 3.2+) con l'IDE Lazarus. Gli esempi usano la modalità {$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:

FeatureSupporto FPC
Modalità compatibilità{$mode tp}, {$mode delphi}, {$mode objfpc}, {$mode fpc}
Stringhe moderneAnsiString (lunghezza illimitata, reference counted), UnicodeString
OOP completoClassi, ereditarietà, interfacce, generics
Eccezionitry..except..finally
Array dinamiciSupporto nativo con SetLength
OverloadingProcedure, funzioni e operatori
Targetx86, 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.

Altri Dialetti
DialettoNote
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.NETPascal moderno per .NET. Pensato per la didattica, supporto lambda, LINQ-like, generics.
Oxygene (RemObjects)Object Pascal per .NET/JVM/Cocoa/WebAssembly. Commerciale.
Virtual PascalClone di Borland Pascal per OS/2 e Win32. Abbandonato.
COMPILAZIONE
Free Pascal — Compilazione da Riga di Comando
{ 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 Comuni FPC
FlagDescrizione
-MdelphiModalità Delphi (compatibilità massima con Delphi)
-MobjfpcModalità ObjFPC (default FPC, richiede @ per puntatori a metodo)
-ShUsa AnsiString come tipo string di default
-O2 / -O3Livello di ottimizzazione
-gGenera informazioni di debug
-glGenera info debug con numeri di riga
-vh / -vwMostra hint / warning
-FUdirDirectory output per file .ppu (unit compilate)
-FudirAggiunge directory di ricerca unit
-dNOMEDefinisce un simbolo per compilazione condizionale
-CriotAbilita controlli runtime (range, I/O, overflow)
In Lazarus

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.

STRUTTURA DI UN PROGRAMMA PASCAL
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 }
Ordine delle Sezioni
SezioneDescrizione
programNome del programma (opzionale in FPC, obbligatorio nello standard)
usesLista delle unit da importare
constDichiarazione di costanti
typeDichiarazione di tipi personalizzati
varDichiarazione di variabili
Procedure/FunzioniImplementazione di sottoprogrammi
begin..end.Corpo principale del programma

In Free Pascal le sezioni const, type, var possono essere ripetute e mescolate liberamente.

ATTENZIONE: Il programma termina con end. (con il punto). I blocchi interni terminano con end; (con il punto e virgola).
Commenti
{ 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. }
Letterali Numerici
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') }
TIPI DI DATO
Tipi Interi
TipoDimensioneRange
Byte1 byte0 .. 255
ShortInt1 byte-128 .. 127
Word2 byte0 .. 65535
SmallInt2 byte-32768 .. 32767
LongWord / Cardinal4 byte0 .. 4294967295
LongInt / Integer4 byte-2³¹ .. 2³¹-1
QWord8 byte0 .. 2⁶⁴-1
Int648 byte-2⁶³ .. 2⁶³-1

Integer in FPC è un alias per LongInt (32 bit). Usare NativeInt / NativeUInt per dimensione dipendente dalla piattaforma (32/64 bit).

Tipi Virgola Mobile
TipoDimensioneCifre significative
Single4 byte~7
Double / Real8 byte~15
Extended10 byte~19 (piattaforma x86)
Currency8 bytePunto fisso, 4 decimali

In FPC, Real è un alias per Double (8 byte). In Turbo Pascal era 6 byte.

Char & Boolean
TipoDimensioneDescrizione
Char / AnsiChar1 byteSingolo carattere ASCII/ANSI
WideChar2 byteCarattere Unicode UTF-16
Boolean1 byteTrue o False
ByteBool / WordBool / LongBool1/2/4 byteBoolean compatibili con C (0 = False, qualsiasi altro = True)
Tipi Subrange & Type Alias
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.

Tipi Stringa
TipoDescrizione
ShortStringStringa a lunghezza fissa (max 255 char). Compatibilità Turbo Pascal.
AnsiStringStringa dinamica, lunghezza illimitata, reference counted. Default con {$H+}.
UnicodeStringStringa Unicode UTF-16, reference counted.
UTF8StringAnsiString con codepage UTF-8.
PCharPuntatore a char null-terminated (compatibilità C).
StringAlias per ShortString (default) o AnsiString (con {$H+}).
TIP: Usa sempre {$H+} (o -Sh) per avere AnsiString come default. Con Lazarus questo è già attivo nei progetti generati dall'IDE.
COSTANTI & VARIABILI
Costanti
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) }
ATTENZIONE: Le costanti tipizzate sono modificabili per default in FPC ({$J+}). Per renderle veramente costanti, usa {$J-} o {$WRITEABLECONST OFF}.
Variabili
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';
Dichiarazione Inline (FPC 3.2+)
{ 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;
OPERATORI
Operatori Aritmetici
OperatoreOperazioneEsempio
+Addizione5 + 38
-Sottrazione5 - 32
*Moltiplicazione5 * 315
/Divisione reale7 / 23.5
divDivisione intera7 div 23
modModulo (resto)7 mod 21

+ funziona anche come operatore di concatenazione per stringhe: 'Ciao' + ' ' + 'Mondo'.

Operatori Relazionali
OperatoreSignificato
=Uguale a
<>Diverso da
<Minore di
>Maggiore di
<=Minore o uguale a
>=Maggiore o uguale a
inAppartenenza a un set: 3 in [1..5]
Operatori Logici
OperatoreSignificatoEsempio
notNegazione logicanot TrueFalse
andAND logicoTrue and FalseFalse
orOR logicoTrue or FalseTrue
xorXOR logicoTrue xor TrueFalse
NOTA: Con {$B-} (default) la valutazione è short-circuit: and/or non valutano il secondo operando se il risultato è già determinato. Con {$B+} la valutazione è completa.
Operatori Bitwise
OperatoreSignificatoEsempio
notNOT bitwisenot $FF$FFFFFF00
andAND bitwise$0F and $F0$00
orOR bitwise$0F or $F0$FF
xorXOR bitwise$FF xor $0F$F0
shlShift a sinistra1 shl 416
shrShift a destra16 shr 24

and, or, not, xor funzionano sia come operatori logici (su Boolean) che bitwise (su Integer). Il compilatore distingue in base al tipo degli operandi.

Operatore di Assegnamento
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 }
CONVERSIONI DI TIPO
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]);
Val & Str (unit System — senza SysUtils)
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);
TIP: 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.
SELEZIONE (IF / CASE)
If / Then / Else
{ 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;
REGOLA CRITICA: Le condizioni composte devono avere le parentesi intorno a ogni sotto-espressione: (x > 0) and (x < 100). Senza parentesi, and ha precedenza più alta di > e il compilatore genererà un errore.
Case / Of
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).

ITERAZIONE
For / To / Do
{ 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, ' ');
NOTA: La variabile contatore del for non può essere modificata all'interno del ciclo. Il passo è sempre 1 (o -1 con downto).
While / Do
{ Condizione verificata PRIMA di ogni iterazione }
while x > 0 do
begin
  WriteLn(x);
  Dec(x);       { equivalente a x := x - 1 }
end;
Repeat / Until
{ 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 }
SALTI & CONTROLLO
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 }
ARRAY
Array Statici
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 }
Array Dinamici
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 (Parametri)
{ 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 }
RECORD
Record Semplice
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;
Record Avanzato (FPC {$modeswitch advancedrecords})
{$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;
Record Variant
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.

STRINGHE
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;
ENUM & SET
Tipi Enumerati
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;
Set (Insiemi)
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).

PROCEDURE & FUNZIONI
{ 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;
Procedure Annidate (Nested)
{ 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).

Dichiarazione Forward
{ 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;
PASSAGGIO PARAMETRI
Per Valore, per Riferimento, Costante, Out
{ 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;
Riepilogo Passaggio Parametri
ModificatoreCopia?Modificabile?Uso tipico
(nessuno)Solo copia localeTipi piccoli (Integer, Char, Boolean)
varNo (riferimento)Sì, modifica l'originaleParametro di input/output
constDipende dal tipoNoTipi grandi (String, Record) in sola lettura
outNo (riferimento)Solo output (valore iniziale ignorato)
constrefNo (sempre rif.)NoForzare riferimento senza copia
OVERLOADING & PARAMETRI DEFAULT
{ 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 }
PUNTATORI
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;
Puntatori a Record
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;
ALLOCAZIONE DINAMICA
New / Dispose — Allocazione Tipizzata
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;
NOTA: 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.
GetMem / FreeMem — Allocazione 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;
Operazioni sulla Memoria
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 }
ATTENZIONE: Move in Pascal ha i parametri invertiti rispetto a memcpy in C: Move(sorgente, destinazione, dimensione). Inoltre riceve variabili, non puntatori — non usare ^.
Riepilogo Gestione Memoria
Tipo di datoAllocazioneDeallocazioneNote
Puntatore tipizzato (^T)New(p)Dispose(p)Gestisce tipi managed nel record
Buffer rawGetMem(p, n)FreeMem(p)Nessuna gestione tipi managed
Array dinamiciSetLength(a, n)SetLength(a, 0)Automatica (reference counted)
Stringhe (AnsiString)AutomaticaAutomaticaReference counted, nessun Free
Oggetti (class)T.Createobj.FreeSempre manuale!
OBJECT vs CLASS

Pascal ha due sistemi OOP distinti. È importante conoscere entrambi, anche se oggi si usa quasi esclusivamente class.

object (Turbo Pascal / stile antico)
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;
Confronto object vs class
Caratteristicaobject (TP)class (Delphi/FPC)
AllocazioneStack (default) o heap con NewSempre heap (TClasse.Create)
Tipo semanticoValue type (copia su assegnamento)Reference type (copia il puntatore)
Classe baseNessuna (standalone)Sempre TObject (implicito)
Costruttore/DistruttoreInit / Done (per convenzione)Create / Destroy
LiberazioneAutomatica (stack) o DisposeManuale: .Free o FreeAndNil
PropertyNo
InterfacceNo
RTTINoSì (sezione published)
Visibilitàprivate, publicprivate, protected, public, published, strict *
Uso oggiLegacy, raroRaccomandato per tutto il codice nuovo
ATTENZIONE: Non confondere 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.
CLASSI (OOP)
NOTA: In Free Pascal, le classi richiedono {$mode objfpc} o {$mode delphi}. Tutte le classi ereditano implicitamente da TObject.
Dichiarazione di una Classe
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;
Implementazione
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;
Utilizzo
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;
Property con Getter/Setter
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;
Visibilità dei Membri
ModificatoreAccessibile da
privateSolo la stessa unit (in FPC: stessa unit, non stessa classe!)
strict privateSolo la stessa classe
protectedStessa unit + sottoclassi
strict protectedSolo sottoclassi
publicOvunque
publishedCome public + informazioni RTTI (usato nel RAD/LCL)
EREDITARIETÀ & POLIMORFISMO
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;
Metodi Virtuali vs Statici
DichiarazioneComportamento
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)
INTERFACCE
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.

GENERICS
Classe Generica (mode objfpc)
{$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;
Sintassi Delphi-style (mode delphi)
{$mode delphi}

{ In mode delphi, la sintassi è più compatta }
type
  TStack<T> = class    { niente 'generic' keyword }
    ...
  end;

var
  Stack: TStack<Integer>;  { niente 'specialize' keyword }
Collezioni Generiche (unit fgl)
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;
I/O STANDARD
Output — Write / WriteLn
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  }
Input — Read / ReadLn
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;
TRAPPOLA COMUNE: Dopo 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.
Formattazione con Format (SysUtils)
uses SysUtils;

WriteLn(Format('%-20s %5d %8.2f', ['Prodotto', 42, 3.14]));
SpecificatoreDescrizioneEsempio
%dIntero decimaleFormat('%d', [42])'42'
%5dIntero, larghezza minima 5Format('%5d', [42])' 42'
%xIntero esadecimaleFormat('%x', [255])'FF'
%fFloat a punto fissoFormat('%f', [3.14])'3.14'
%.2fFloat con 2 decimaliFormat('%.2f', [3.1])'3.10'
%eNotazione scientificaFormat('%e', [1500.0])'1.5E+003'
%gGenerale (sceglie f o e)Format('%g', [0.001])'0.001'
%sStringaFormat('%s', ['abc'])'abc'
%-20sStringa, allineata a sinistra, larg. 20Format('%-20s|', ['abc'])'abc |'
%pPuntatore (indirizzo hex)Format('%p', [ptr])
%%Carattere % letteraleFormat('100%%')'100%'
I/O con Gestione Errori ({$I})
{ 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. }
I/O SU FILE
File di Testo — Scrittura
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;
File di Testo — Lettura
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;
Riepilogo Apertura File di Testo
ProceduraModalitàFile deve esistere?Posizione
Rewrite(f)Scrittura (crea/sovrascrive)No (lo crea)Inizio
Reset(f)Sola letturaInizio
Append(f)Scrittura in codaFine
Lettura Sicura con try..finally
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;
File Tipizzati (Binari)
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;
ATTENZIONE: Non usare 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).
File Non Tipizzati (Blocchi Raw)
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.

TFileStream (approccio moderno, unit Classes)
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 aperturaDescrizione
fmCreateCrea il file (sovrascrive se esiste)
fmOpenReadApre in sola lettura
fmOpenWriteApre in sola scrittura
fmOpenReadWriteApre in lettura/scrittura
Leggere/Scrivere Intero File come Stringa
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;
Operazioni su File e Directory (unit SysUtils)
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')));
FindFirst / FindNext — Enumerazione File
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;
AttributoSignificato
faReadOnlyFile di sola lettura
faHiddenFile nascosto
faDirectoryÈ una directory
faSymLinkLink simbolico (FPC)
faAnyFileQualsiasi tipo di file
UNIT (MODULI)
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 Comuni FPC / Lazarus
UnitContenuto
SystemSempre inclusa. Tipi base, I/O, gestione memoria.
SysUtilsConversioni, file, date, eccezioni, formato.
ClassesTStringList, TStream, TComponent, TList, ecc.
MathFunzioni matematiche avanzate.
StrUtilsUtility aggiuntive per stringhe.
DateUtilsManipolazione date/ore avanzata.
fglCollezioni generiche (TFPGList, TFPGMap, ecc.).
Generics.CollectionsCollezioni generiche stile Delphi (FPC 3.2+).
Forms, Controls, DialogsLCL per applicazioni GUI Lazarus.
ECCEZIONI
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');
Eccezioni Comuni
EccezioneQuando viene lanciata
EConvertErrorConversione di tipo fallita (StrToInt, ecc.)
ERangeErrorIndice fuori range (con {$R+})
EOverflowOverflow aritmetico (con {$Q+})
EDivByZeroDivisione per zero
EAccessViolationAccesso a memoria non valida (SIGSEGV)
EInOutErrorErrore I/O su file
EOutOfMemoryMemoria insufficiente
EInvalidPointerOperazione su puntatore non valido
DIRETTIVE DEL COMPILATORE
Direttive Comuni
DirettivaDescrizione
{$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
Compilazione Condizionale
{$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}
Include & Risorse
{ 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 }
IDIOMI & TIPS
Convenzioni di Naming FPC/Delphi
ElementoConvenzioneEsempio
TipiPrefisso TTPersona, TCalcolatrice
Classi eccezionePrefisso EEMiaEccezione
InterfaccePrefisso IIStampabile
Tipi puntatorePrefisso PPInteger, PNodo
Campi privatiPrefisso FFNome, FEta
Parametri costruttorePrefisso aaNome, aEta
Variabili globaliPrefisso GGConfig
IdentificatoriPascalCaseCalcolaSomma
Pattern FreeAndNil
uses SysUtils;

{ Libera l'oggetto E imposta il riferimento a nil }
FreeAndNil(MioOggetto);

{ Equivale a: }
MioOggetto.Free;
MioOggetto := nil;
TStringList — Coltellino Svizzero
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;
Date e Ore
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;
Funzioni Matematiche (unit Math)
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 }
Parametri da Riga di Comando
WriteLn('Programma: ', ParamStr(0));       { nome eseguibile }
WriteLn('N. parametri: ', ParamCount);

var i: Integer;
for i := 1 to ParamCount do
  WriteLn('Param ', i, ': ', ParamStr(i));
Procedure di tipo callback / Puntatori a funzione
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;
Case-Insensitivity

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.