Introduzione

Se il computer potesse comprendere il linguaggio umano, si potrebbe chiedere:

  • Ehi Mario, calcola questo per me e restituiscimi il risultato
  • Ehi Anna, esegui questo gruppo di istruzioni per me

Questo genere di ordini sono stati impartiti raggruppando le istruzioni, in modo da poterle riutilizzare più volte. Un programma che utilizza questo tipo di struttura viene detto “procedurale”.

Procedure e funzioni

Il termine procedura è legato al termine funzione (anche se vi sono piccole differenze di significato). Per capire cosa sia una funzione in un programma basta pensare ad una funzione matematica, dove c'è la variabile indipendente x e quella dipendente y, dove si trova il risultato y=f(x). Ad esempio, la funzione y=x2 calcola il quadrato dei valori forniti tramite x.

Nella programmazione procedurale, ad ogni funzione viene affidata l'esecuzione di un compito specifico e ogni volta che si deve svolgere quel compito si chiama in esecuzione quella funzione.

Tra parentesi possono essere specificati degli argomenti che la funzione può utilizzare. Alcune funzioni, come main(), possono essere prive di argomenti.

È utile eseguire questi programmi in modalità debug e visualizzare il contenuto delle variabili in runtime.

11.cpp
// Questo programma è stato scritto da Fabio.
// Serve a calcolare il triplo di un numero
// scrivendo tutto il codice nella funzione principale.
#include <iostream> //che cosa contiene il file iostream e iostream.h?
 
int main ()
{
  int mioNumero;
 
  std::cout << "Per favore scrivi un numero intero: ";
  std::cin >> mioNumero;
 
  std::cout << "il triplo di " << mioNumero << " vale " 
            << 3*mioNumero << endl;
  return 0;
}

Passaggio per valore

12.cpp
// Questo programma è stato scritto da Fabio.
// Come nel programma precedente calcola il triplo di un numero, ma
// ora usando una semplice funzione chiamata triplicare().
#include <iostream>
 
// questo modo di passare il valore crea una nuova variabile locale (int x)
// questa funzione restituisce un int (return)
int triplicare(int x) // definizione della funzione con parametro formale 'x'
{
  return 3*x;
}
 
int main()
{
  int mioNumero,mioTriplo;
 
  std::cout << "Per favore scrivi un numero intero: ";
  std::cin >> mioNumero;
 
  mioTriplo = triplicare(mioNumero); // chiamata della funzione con il 
                                     // parametro effettivo 'mioNumero'
 
  std::cout << "il triplo di " << mioNumero << " vale "
            << mioTriplo << endl;
  return 0;
}
  • main() è una funzione (la funzione principale) che (di solito) restituisce un valore intero.
  • triplicare() è una funzione che restituisce un valore intero.

Dentro la definizione della funzione triplicare() tra parentesi c'è scritto int x questo permette di passare alla funzione una copia di una variabile, e questo meccanismo si chiama appunto passaggio per valore. Per parlare in termini semplificati, sarebbe, più o meno, come se ci fosse scritto il seguente codice “invisibile”…

 int x;         // definizione di una nuova variabile locale
 x = mioNumero; // assegnazione del valore passato come argomento

Questo codice serve solo per capire cosa accade dentro la funzione, e non va preso alla lettera, cioè non vanno copiate quelle istruzioni…

Ecco una tabella che mostra i valori contenuti nelle aree di memoria utilizzate durante l'esecuzione dell'ultimo esempio:

mioNumero mioTriplo x
6 - -
6 - 6
6 18 -

Questo tipo di tabella che tiene la traccia delle variabili si può paragonare allo strumento di debug. Come si può notare la vita della variabile locale della funzione triplicare() (il parametro formale x) è più breve, rispetto alle variabili della funzione main() (il parametro effettivo mioNumero). Queste due funzioni vengono eseguite in aree di memoria separate, che non sono reciprocamente visibili. Per questo motivo le variabili delle funzioni sono dette variabili locali. a comunicazione tra le due funzione avviene solo grazie alla copia del valore dentro x e grazie al comando return.

Questo programma contiene un'istruzione che non rispetta la visibilità delle variabili. Provare a compilare il seguente programma:

13.cpp
// Questo programma è stato scritto da Fabio.
// Serve a dimostrare la visibilità delle variabili locali.
#include <iostream>
 
// questo modo di passare il valore crea una nuova variabile locale (int x)
// questa funzione restituisce un int (return)
int triplicare(int x) 
{
  return 3*x;
}
 
int main()
{
  int mioNumero,mioTriplo;
 
  std::cout << "Per favore scrivi un numero intero: ";
  std::cin >> mioNumero;
 
  mioTriplo = triplicare(mioNumero); // chiamata della funzione
 
  std::cout << "il triplo di " << mioNumero << " vale "
            << x << std::endl;
  return 0;
}

Si dovrebbe ottenere questo errore

   In function ‘int main()’:
        : error: ‘x’ was not declared in this scope 

Esistono due diversi tipi di funzioni:

  • quelle che restituiscono un valore (uno!) a chi le chiama;
  • quelle che non restituiscono nessun valore (tipo void) a chi le chiama.

Il valore restituito dalle prime (vedi return) può essere usato per ottenere il risultato di un calcolo. NOTA sul C: a volte i programmatori C usano il valore restituito da una funzione per stabilire se questa è stata eseguita senza errori (restituendo 0) oppure se ha prodotto un errore (restituendo ad esempio 5). In C++ il valore restituito non dovrebbe essere usata per questo scopo, perché, per stabilire se ci sono errori, si usano le exception (vedi gestione delle eccezioni).

Le funzioni si possono anche classificare in ispettori e modificatori:

  • le prime non modificano il contenuto delle variabili passate, quindi di solito, per avere un effetto al loro esterno, devono restituire un valore (quindi non sono di tipo void)
  • le seconde possono modificare il contenuto delle variabili passate, quindi possono anche essere di tipo void

Spiegare i termini variabile locale, argomento, parametro formale, parametro effettivo

14.cpp
// Questo programma è stato scritto da Fabio.
// Serve a dimostrare l'ambito di visibilità delle variabili locali
// usando due funzioni
 
#include <iostream>
 
int triplicare() 
{
  return 3*mioNumero; // ERRORE 
}
 
int main()
{
  int mioNumero,mioTriplo;
 
  std::cout << "Per favore scrivi un numero intero: ";
  std::cin >> mioNumero;
 
  mioTriplo = triplicare(); // chiamata della funzione
 
  std::cout << "il triplo di " << mioNumero << " vale "
            << mioTriplo << endl;
  return 0;
}
la variabile mioTriplo NON era necessaria... riscrivere il programma 12.cpp
15.cpp
// Questo programma è stato scritto da Fabio.
// Dimostra l'uso di una funzione dentro l'operatore di inserimento.
 
#include <iostream>
 
int triplicare(int x)
{
  return 3*x;
}
 
int main()
{
  int mioNumero;
 
  std::cout << "Per favore scrivi un numero intero: ";
  std::cin >> mioNumero;
 
// la chiamata della funzione (triplicare) dentro insertion 
  std::cout << "il triplo di "<< mioNumero << " vale "
            << triplicare(mioNumero) << std::endl;
  return 0;
}
16.cpp
// Questo programma è stato scritto da Fabio.
// Contiene una funzione di "tipo" void
// ed inoltre mostra l'effetto del passaggio per valore.
 
#include <iostream>
 
// questa funzione non restituisce nulla (manca return)
void triplicare(int x)
{
  x= 3*x; // ERRORE? Ha effetto sul parametro reale?
}
 
int main()
{
  int mioNumero;
 
  std::cout << "Per favore scrivi un numero intero: ";
  std::cin >> mioNumero;
  std::cout << "il triplo di " << mioNumero << " vale "
 
  triplicare();  // chiamata della funzione
  std::cout  << mioNumero << std::endl;
  return 0;
}
17.cpp
// questo programma è stato scritto da Fabio
// Serve a calcolare la media (valore intero) tra due interi
#include <iostream>
 
int media(int x, int y)
{
  return (x+y)/2;
}
 
int main()
{
  int numero1,numero2;
 
  std::cout << "Per favore scrivi un numero intero: ";
  std::cin >> numero1;
  std::cout << "Per favore scrivi un altro numero intero: ";
  std::cin >> numero2;
 
  std::cout <<  "La media vale" << media(numero1,numero2) << endl;
  return 0;
}
18.cpp
// Questo programma è stato scritto da Fabio.
// Dimostra l'utilità delle funzioni per riutilizzare il codice
// effettuando più chiamate della stessa funzione. 
// 
// Serve a calcolare la media di 4 numeri.
 
#include <iostream>
 
int mediare(int x, int y)
{
  return (x+y)/2;
}
 
int main()
{
  int numero1,numero2,numero3,numero4,media;
 
  std::cout << "Per favore scrivi un numero intero: ";
  std::cin >> numero1;
  std::cout << "Per favore scrivi un altro numero intero: ";
  std::cin >> numero2;
  std::cout << "Per favore scrivi un numero intero: ";
  std::cin >> numero3;
  std::cout << "Per favore scrivi un altro numero intero: ";
  std::cin >> numero4;
 
  media = mediare(mediare(numero1,numero2),mediare(numero3,numero4));
  std::cout <<  "La media vale " << media << endl;
 
  return 0;
 
}

Ora che è chiaro l'importanza delle funzioni per il riutilizzo del codice, bisogna capire anche che il compilatore ha bisogno di controllare la correttezza delle chiamate delle funzioni, che cioè vengano chiamate con il giusto numero e tipo di parametri. Nei precedenti esempi questo controllo era reso possibile dal fatto che le nuove funzioni erano definite all'inizio del file.

Le funzioni possono essere definite anche dopo, ma bisogna comunque dichiarare o “annunciare” la loro forma. Vedere il seguente esempio

19.cpp
// Questo programma è stato scritto da Fabio.
// Dimostra la differenza tra concetto di dichiarazione e di definizione 
 
#include <iostream>
 
int mediare(int x, int y); // qui dichiarazione senza definizione!
 
int main()
{
  int numero1,numero2,numero3,numero4,media;
 
  std::cout << "Per favore scrivi un numero intero: ";
  std::cin >> numero1;
  std::cout << "Per favore scrivi un altro numero intero: ";
  std::cin >> numero2;
  std::cout << "Per favore scrivi un numero intero: ";
  std::cin >> numero3;
  std::cout << "Per favore scrivi un altro numero intero: ";
  std::cin >> numero4;
 
  media = mediare(mediare(numero1,numero2),mediare(numero3,numero4));
  std::cout <<  "La media vale " << media << endl;
 
  return 0;
 
}
 
int mediare(int x, int y)
{
  return (x+y)/2;
}
  • appunti3s/programmi_procedurali.txt
  • Last modified: 2018/05/03 22:35
  • by profpro