Parametri di Procedure e Funzioni in Object Pascal

In questa lezione esploreremo i parametri e gli argomenti delle funzioni e delle procedure in Object Pascal. Imparare a gestire i parametri e gli argomenti è fondamentale per creare codice modulare, organizzato e facilmente manutenibile.

Scopriremo la differenza tra parametri ed argomenti, ed esploreremo il passaggio per copia e il passaggio per riferimento, e come essi influiscono sull'utilizzo delle variabili e sulla modificabilità dei valori. Inoltre, vedremo l'importanza dei parametri costanti nella programmazione e come essi possono aiutare a evitare errori comuni.

Parametri ed Argomenti

Nella lezione precedente abbiamo visto come definire procedure e funzioni in Object Pascal. Le funzioni e le procedure che abbiamo definito, tuttavia, erano abbastanza semplici e non molto utili per sviluppare applicazioni complesse.

In questa lezione vediamo come fornire dati in input ad una procedura o funzione, in maniera tale che esse possano elaborarli. Introduciamo, quindi, il concetto di parametro: si tratta di una variabile con un nome che viene passata alla procedura o funzione.

Supponiamo di voler realizzare una procedura che stampi a schermo un numero passato in ingresso in fase di invocazione. Per scrivere questa procedura, che chiameremo StampaNumero, possiamo usare la sintassi seguente:

procedure StampaNumero(x: integer);
begin
    WriteLn('Numero: ', x);
end;

Nell'esempio abbiamo definito la funzione StampaNumero facendone seguire il nome dalla lista dei parametri tra parentesi. In questo caso il parametro è uno soltanto, x, ed è di tipo integer. Quando viene invocata questa procedura, la variabile o parametro x assumerà il valore fornito dal chiamante.

Supponiamo di voler chiamare la nostra procedura passandogli in input il numero 42. La sintassi da utilizzare è la seguente:

StampaNumero(42);

Ossia, bisogna indicare il nome della procedura passando i valori in input tra parentesi. Tali valori prendono il nome di argomenti e sono i valori effettivi passati in ingresso alla procedura.

Definizione

Differenza tra Parametri ed Argomenti

I parametri e gli argomenti sono termini utilizzati per descrivere i valori che vengono passati alle funzioni e alle procedure.

Un parametro è una variabile definita all'interno della dichiarazione di una funzione o di una procedura e che rappresenta un valore che può essere passato alla funzione o alla procedura quando questa viene chiamata. I parametri sono utilizzati per rendere la funzione o la procedura più flessibile e riutilizzabile, poiché possono accettare valori differenti ogni volta che vengono chiamati.

Un argomento, invece, è il valore effettivo che viene passato alla funzione o alla procedura quando questa viene chiamata. Gli argomenti sono utilizzati per passare i valori specifici che la funzione o la procedura utilizzerà per eseguire la sua attività.

In sintesi, i parametri sono i segnaposto per gli argomenti, sono cioè le variabili che verranno utilizzate all'interno della funzione/procedura per accedere agli argomenti che gli vengono passati.

Vediamo come passare più di un parametro ad una procedura. Supponiamo di voler realizzare una seconda funzione, StampaSomma che prende in ingresso due numeri interi e ne stampa la somma. Possiamo realizzarla in questo modo:

procedure StampaSomma(a: integer; b: integer);
begin
    WriteLn('La somma è ', a + b);
end;

Come possiamo osservare, nella definizione di una procedura i parametri vanno separati da un punto e virgola. In fase di invocazione, invece, gli argomenti vanno separati da una semplice virgola:

// Invocazione di StampaSomma
StampaSomma(10, 20);

La sintassi per specificare i parametri di una funzione è analoga al caso delle procedure. Supponiamo, ad esempio, di voler realizzare una funzione che calcoli la moltiplicazione tra due interi:

function Molt(a: integer; b: integer): integer;
begin
    Result := a * b;
end;

Possiamo invocare la funzione in questo modo:

program ProvaMolt;

function Molt(a: integer; b: integer): integer;
begin
    Result := a * b;
end;

var
    x: integer;
begin
    x := Molt(10, 20);
    WriteLn('X = ', x);
end.

Ricapitolando:

Definizione

Parametri di Funzioni e Procedure in Object Pascal

In Object Pascal per definire i parametri di una procedura o funzione è necessario specificare la lista tra parentesi nella definizione stessa della procedura o funzione. La lista è composta dal nome dei parametri seguito dal loro tipo e separati da un punto e virgola.

La sintassi è la seguente:

procedure nome_procedura(parametro_1: tipo_1; parametro_2: tipo_2);
begin
    { Corpo Procedura }
end;
procedure nome_funzione(parametro_1: tipo_1; parametro_2: tipo_2): tipo_ritorno;
begin
    { Corpo Funzione }
end;
Definizione

Argomenti di Funzioni e Procedure in Object Pascal

In Object Pascal per passare gli argomenti ad una funzione o procedura nel punto di invocazione è necessario specificare il nome della funzione o procedura seguito dalla lista degli argomenti tra parentesi separati da virgole.

La sintassi è la seguente:

nome_procedura(argomento_1, argomento_2);
valore_ritorno := nome_funzione(argomento_1, argomento_2);

Argomenti e espressioni

In Object Pascal è possibile passare come argomenti di una funzione o procedura non esclusivamente valori immediati. Si possono usare, infatti, anche variabili o espressioni.

Ad esempio, supponiamo di avere una funzione che calcola il quadrato di un numero intero:

function Square(x: integer): integer;
begin
    Result := x * x;
end;

Possiamo passare come argomento di Square un valore immediato, una variabile o un'espressione che abbia come risultato un intero:

{ Passaggio di un valore immediato }
risultato_1 := Square(4);

{ Passaggio di una variabile }
risultato_2 := Square(y);

{ Passaggio del risultato di un'espressione }
risultato_3 := Square(y * 2 + 4);

Tutte e tre le sintassi sono legali.

Inoltre, è possibile passare anche direttamente il risultato di una funzione. Ad esempio, volendo calcolare la quarta potenza di un numero intero potremmo scrivere così:

risultato := Square(Square(y));

In questo esempio, dapprima viene calcolato il quadrato di y con la prima chiamata a Square tra parentesi. Successivamente il risultato viene passato di nuovo alla funzione Square ottenendone così la quarta potenza.

Definizione

Argomenti ed Espressioni

In Object Pascal un argomento di una funzione o procedura può essere:

  • Un valore immediato;
  • Una variabile;
  • Il risultato di una funzione;
  • Un'espressione che combina una o più delle precedenti.

Tipi dei parametri e conversione

L'Object Pascal è un linguaggio fortemente tipizzato (dall'inglese strongly typed) in quanto impone che le variabili siano definite con un tipo ben preciso e che sia vietato assegnare il valore di un tipo ad una variabile di tipo diverso. Ad esempio, il codice che segue è illegale e produce un errore da parte del compilatore:

program illegale;

var
    c: char;
begin
    // Non compila!!!
    c := 7;
end.

Analogamente, quando si passano degli argomenti ad una funzione o ad una procedura bisogna prestare attenzione al tipo dei parametri stessi. Osserviamo il programma che segue:

program illegale2;

function Somma(a: integer; b: integer): integer;
begin
    Result := a + b;
end;

var
    x: integer;
begin
    // Non compila!!!
    x := Somma(5, 'a');
    WriteLn(x);
end.

In questo caso, nella riga 12 stiamo provando a passare un argomento di tipo Char ad un parametro che è invece di tipo Integer. Di conseguenza, il compilatore ci segnalerà un errore.

illegale2.pas(12,22) Error: Incompatible type for arg no. 2: Got "Char", expected "LongInt"

Esistono, tuttavia, dei casi in cui, anche se gli argomenti sono di tipo differente è consentito passarli ad una funzione o procedura. In questi casi il compilatore effettua una conversione implicita dei valori degli argomenti al tipo di destinazione del parametro.

Chiariamo con un esempio:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
program ConversioneImplicita;

function Somma(a: Int64; b: Int64): Int64;
begin
    Result := a + b;
end;

var
    x, y: Int16;
    z: Int64;
begin
    Write('Primo operando:   ');
    Read(x);
    Write('Secondo operando: ');
    Read(y);
    z := Somma(x, y);
    WriteLn('La somma è ', z);
end.

In questo esempio, abbiamo la funzione Somma che richiede in ingresso due parametri di tipo Int64, ossia due interi a 64 bit. Nel corpo del nostro programma, tuttavia, nella riga 16 invochiamo la funzione passando come parametri due interi a 16 bit, Int16. Anche se gli Int16 sono diversi dagli Int64 il compilatore non restituisce errori. Questo perché internamente il compilatore converte in maniera implicita il valore a 16 bit in uno a 64 bit.

La conversione implicita, tuttavia, avviene soltanto in casi particolari e non sempre è ammessa come vedremo più avanti.

Passaggio degli argomenti per copia

Nel paragrafo precedente abbiamo visto che per passare degli argomenti ad una funzione o procedura è necessario specificare gli argomenti tra parentesi tonde dopo l'identificatore della funzione stessa.

Internamente quello che accade è che il valore dell'argomento viene copiato nel parametro della funzione. Quindi la funzione lavora su una copia dell'argomento stesso.

Per chiarire il concetto osserviamo l'esempio che segue:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
program Copia;

procedure StampaPiuUno(x: integer);
begin
    x := x + 1;
    WriteLn('Valore più uno: ', x);
end;

var
    x: integer;
begin
    Write('Inserisci un numero: ');
    Read(x);
    StampaPiuUno(x);
    WriteLn('Valore originale: ', x);
end.

Se proviamo a compilare ed eseguire il programma dell'esempio otteniamo il seguente output:

Inserisci un numero: 12
Valore più uno: 13
Valore originale: 12

Sembrerebbe un risultato strano, ma la realtà è che quando invochiamo la procedura StampaPiuUno alla riga 14 il valore della variabile x del programma viene copiato nel parametro x della procedura. Per cui, quando la procedura nella riga 5 incrementa il valore di x non sta incrementando il valore della variabile x del programma bensì sta incrementando la sua copia x locale.

Questo comportamento prende il nome di passaggio per copia degli argomenti ed è il comportamento di default quando si definiscono funzioni o procedure in Object Pascal.

Definizione

Passaggio di Argomenti per Copia

In Object Pascal, di default, il passaggio degli argomenti ad una funzione o procedura avviene per copia.

Il passaggio di un argomento per copia significa che quando un valore viene passato come argomento a una funzione o procedura, una copia di quel valore viene creata e utilizzata all'interno della funzione o procedura. Ciò significa che qualsiasi modifica apportata al valore all'interno della funzione o procedura non avrà alcun effetto sul valore originale.

Passaggio per riferimento

In Object Pascal è possibile passare i parametri anche per riferimento. Passare un argomento per riferimento consiste nel passare, appunto, un riferimento alla variabile argomento piuttosto che una copia. Per passare per riferimento un parametro è necessario premettere la parola chiave var prima del nome del parametro nella lista dei parametri della funzione o procedura.

Tornando all'esempio di prima, se vogliamo passare il parametro x per riferimento dobbiamo riscrivere la riga 3 in questo modo:

procedure StampaPiuUno(var x: integer);

Il programma diventa:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
program Riferimento;

procedure StampaPiuUno(var x: integer);
begin
    x := x + 1;
    WriteLn('Valore più uno: ', x);
end;

var
    x: integer;
begin
    Write('Inserisci un numero: ');
    Read(x);
    StampaPiuUno(x);
    WriteLn('Valore originale: ', x);
end.

Se proviamo a compilare ed eseguire il programma, otteniamo il seguente output:

Inserisci un numero: 12
Valore più uno: 13
Valore originale: 13

La differenza sta nell'ultima riga di output. Adesso la variabile x vale 13 dopo la chiamata a procedura perché abbiamo passato un riferimento anziché effettuare una copia del parametro.

Definizione

Passaggio di Argomenti per Riferimento

In Object Pascal, il passaggio di un argomento per riferimento significa che quando un valore viene passato come argomento a una funzione o procedura, non viene creata una copia di quel valore, ma viene utilizzato un riferimento all'originale. Ciò significa che qualsiasi modifica apportata al valore all'interno della funzione o procedura avrà effetto anche sul valore originale.

Per passare un argomento per riferimento in Object Pascal, si utilizza la parola chiave var prima del nome del parametro nella dichiarazione della funzione o procedura.

La sintassi è la seguente:

procedure nome_procedura(var parametro_1: tipo_1; var parametro_2: tipo_2);
begin
    { Corpo Procedura }
end;
procedure nome_funzione(var parametro_1: tipo_1; var parametro_2: tipo_2): tipo_ritorno;
begin
    { Corpo Funzione }
end;

Una regola fondamentale da ricordare quando si usa il passaggio per riferimento è che possiamo usare come argomenti solo esclusivamente variabili. Non possiamo usare ne espressioni ne valori letterali. Ad esempio, richiamare la funzione di prima, StampaPiuUno, in questo modo genera un errore del compilatore:

// Errore: stiamo passando un valore immediato
StampaPiuUno(5);

// Errore: stiamo passando il risultato di un'espressione
StampaPiuUno(3 * x);

Nel primo caso stiamo passando un valore immediato, 5, che non corrisponde a nessuna variabile. In altre parole, non c'è nessuna entità di cui ottenere il riferimento. Nel secondo caso stiamo passando il risultato di un'espressione e di nuovo non abbiamo un'entità di cui ottenere il riferimento.

Quando si utilizza il passaggio per riferimento, inoltre, bisogna ricordarsi che non valgono le regole di conversione implicita. Prendiamo l'esempio che segue:

program illegale;

procedure StampaPiuUno(var x: Int64);
begin
    x := x + 1;
    WriteLn(x);
end;

var
    x: Int16;
begin
    x := 2;
    StampaPiuUno(x);
end.

Se proviamo a compilare il programma di sopra otteniamo un errore del genere:

illegale.pas(13,19) Error: Call by var for arg no. 1 has to match exactly:
Got "SmallInt" expected "Int64"

Questo accade perché il tipo dell'argomento e del parametro non sono uguali e le conversioni implicite non vengono effettuate per i parametri passati per riferimento.

Nota

Limiti del Passaggio di Argomenti per Riferimento

In Object Pascal il passaggio di un argomento per riferimento presenta due limiti:

  • Si possono passare per riferimento esclusivamente variabili;
  • Non valgono più le regole di conversione implicita.

Funzioni che restituiscono valori multipli

Una importante applicazione dei parametri passati per riferimento è la possibilità di implementare funzioni che restituiscono più di un solo risultato. Normalmente, infatti, le funzioni restituiscono un solo valore di ritorno. Possiamo sfruttare il passaggio per riferimento per restituire altri valori.

Partiamo da un esempio: supponiamo di voler realizzare una funzione che restituisca contemporaneamente il massimo e il minimo tra due interi. Possiamo realizzarla in questo modo:

function MaxMin(a: integer; b: integer; var min: integer): integer;
begin
    if a < b then
    begin
        min := a;
        Result := b;
    end
    else
    begin
        min := b;
        Result := a;
    end;
end;

Questa funzione restituisce come valore di ritorno il massimo tra a e b. Inoltre, attraverso il parametro min che viene passato per riferimento, essa restituisce anche il minimo tra i due numeri.

Possiamo, quindi, invocare questa funzione in questo modo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
program ValoriMultipli;

function MaxMin(a: integer; b: integer; var min: integer): integer;
begin
    if a < b then
    begin
        min := a;
        Result := b;
    end
    else
    begin
        min := b;
        Result := a;
    end;
end;

var
    a, b, max, min: integer;
begin
    Write('Inserisci a: ');
    Read(a);
    Write('Inserisci b: ');
    Read(b);
    max := MaxMin(a, b, min);
    WriteLn('Il massimo è ', max);
    WriteLn('Il minimo è  ', min);
end.

Nella riga 24 la funzione MaxMin viene invocata passando come terzo argomento la variabile min in cui verrà memorizzato il valore minimo tra le due variabili.

Quando un parametro passato per riferimento viene usato esclusivamente per restituire un altro valore di ritorno, al posto della parola chiave var è possibile utilizzare la parola chiave out. In questo modo si comunica al compilatore che il parametro verrà usato solo per restituire un valore e che qualunque valore abbia la variabile a cui fa riferimento non verrà utilizzato nel corpo della funzione. Usando out possiamo riscrivere MaxMin in questo modo:

function MaxMin(a: integer; b: integer; out min: integer): integer;
begin
    if a < b then
    begin
        min := a;
        Result := b;
    end
    else
    begin
        min := b;
        Result := a;
    end;
end;
Definizione

Argomenti per Riferimento di sola uscita: out

Quando si usa un argomento passato per riferimento esclusivamente per ottenere un valore in uscita si può esplicitare questa situazione utilizzando al posto di var la parola chiave out.

In questo modo si segnala al compilatore che la funzione o procedura non utilizzerà il valore contenuto nell'argomento sapendo che verrà usato solo come meccanismo per fornire un valore in uscita. Questo permette al compilatore di ottimizzare il codice generato.

La sintassi è la seguente:

procedure nome_procedura(out parametro_1: tipo_1; out parametro_2: tipo_2);
begin
    { Corpo Procedura }
end;
procedure nome_funzione(out parametro_1: tipo_1; out parametro_2: tipo_2): tipo_ritorno;
begin
    { Corpo Funzione }
end;

Parametri costanti

Un terzo modo per passare parametri ad una funzione o procedura è quello di passare dei parametri costanti. Attraverso l'uso della parola chiave const si può marcare un parametro come costante, indicando al compilatore il fatto che tale parametro non deve e non sarà modificato dalla routine, funzione o procedura che sia.

Conoscendo questa informazione, il compilatore può ottimizzare il passaggio dei parametri e utilizzerà il passaggio per riferimento in maniera implicita.

Ad esempio, possiamo scrivere una funzione square che calcola il quadrato di un numero in questo modo:

function Square(const value: Integer): Integer;
begin
    Square := value * value;
end;

Se, tuttavia, proviamo a modificare un parametro costante il compilatore ci segnalerà un errore:

function InvalidSquare(const value: Integer): Integer;
begin
    value := value * value;
    InvalidSquare := value;
end;

Provando a compilare il codice di sopra, il compilatore FreePascal ci segnala il seguente errore:

prova_const.pas(7,5) Error: Can't assign values to const variable
Definizione

Parametri per Riferimento costanti

In Object Pascal, utilizzando la parola chiave const al posto di var si indica al compilatore che l'argomento verrà passato per riferimento ma non può essere modificato nel corpo della funzione o procedura.

La sintassi è la seguente:

procedure nome_procedura(const parametro_1: tipo_1; const parametro_2: tipo_2);
begin
    { Corpo Procedura }
end;
procedure nome_funzione(const parametro_1: tipo_1; const parametro_2: tipo_2): tipo_ritorno;
begin
    { Corpo Funzione }
end;

In Sintesi

Questa lezione è di importanza fondamentale nello studio delle Funzioni e Procedure in Object Pascal.

Abbiamo visto la differenza tra parametri ed argomenti. Soprattutto, abbiamo visto come definire quali parametri una funzione prenda in ingresso e come passare degli argomenti ad essa.

Successivamente abbiamo studiato il passaggio degli argomenti:

  • Passaggio per copia: in cui l'argomento viene copiato in una variabile temporanea all'interno della funzione;
  • Passaggio per riferimento: in cui viene passato un riferimento ad una variabile che può, così, essere modificata dalla funzione;

Inoltre abbiamo esplorato i parametri costanti, ossia parametri passati per riferimento che tuttavia non possono essere modificati nel corpo della funzione.

Nella prossima lezione studieremo un altro concetto avanzato che riguarda le funzioni e le procedure: l'Overloading.