Sezione 2.1 
Strutture di controllo. 


Normalmente un programma non è semplicemente una sequenza di istruzioni da eseguire una dopo l'altra. Durante la sua esecuzione esso può scegliere tra gruppi di istruzioni o ripetere più volte uno stesso gruppo di istruzioni. A questo scopo il C++ fornisce delle strutture di controllo che servono a specificare come si deve comportare il programma.

Per parlare di strutture di controllo occorre introdurre il concetto di blocco di istruzioni. Un blocco di istruzioni è un gruppo di istruzioni separate da punti e virgola (; ) e racchiuse tra parentesi graffe: { e } .

Nella maggior parte delle strutture di controllo che vedremo ora viene indicata una (o più) generica Istruzione   come  parametro, questa può essere sia una singola istruzione che  un blocco di istruzioni. Se mettiamo una singola istruzione non  è  necessario racchiuderla tra parentesi graffe ( {}), ma naturalmente  occorre mettere il punto e virgola ( ;) che la termina. Se  vogliamo mettere più di una istruzione allora  occorre racchiuderle  tra parentesi graffe ( {} ) per formare  un blocco.

Strutture condizionali: if ed else

Viene usata per eseguire una istruzione o un blocco di istruzioni soltanto se si verifica una determinata condizione. La sua forma è:
if (Condizione) Istruzione
dove Condizione è una espressione di tipo bool (ossia una espressione il cui risultato è uno dei due valori di verità true o false ). L'espressione booleana Condizione viene valutata e se essa ritorna il valore true l'istruzione (o gruppo di istruzioni) Istruzione viene eseguita mentre se essa ritorna il valore false l'istruzione Istruzione viene ignorata (non eseguita) e l'esecuzione del programma continua con le istruzioni che seguono imediatamente la struttura condizionale.

Ad esempio il seguente frammento di codice stampa  x e' 100    soltanto se il valore della variabile x è proprio 100:

if (x == 100)
       cout << "x e' 100";
Se vogliamo che vengano eseguite più istruzioni nel caso in cui Condizione è true dobbiamo racchiuderle tra parentesi graffe per formare un unico blocco di istruzioni :
if (x == 100)
 {
  cout << "x e' ";
  cout << x;
 }
Possiamo anche usare la parola chiave else per specificare una diversa istruzione o blocco di istruzioni da eseguire nel caso in cui la condizione sia falsa. La forma della struttura condizionale è in questo caso la seguente:
if (Condizione) Istruzione1 else Istruzione2
Ad esempio:
if (x == 100)
    cout << "x e' 100";
else
         cout << "x non e' 100";
stampa x e' 100 se x vale proprio 100, ma se non è così -e solo se non è così- stampa x non e' 100.

La struttura if + else si può concatenare per verificare un insieme di valori. Il seguente esempio illustra il suo uso per verificare se il valore della variabile x è negativo, positivo o nessuno dei due  (ossia è zero).

if (x > 0)
    cout << "x e' positivo";
else if (x < 0)
         cout << "x e' negativo";
else
    cout << "x e' 0";

Istruzioni iterative o cicli

I cicli hanno lo scopo di ripetere una istruzione o gruppo di istruzioni un certo numero di volte oppure fino a che rimane vera una certa condizione.
Il ciclo while.

Il suo formato è:
while (Espressione ) Istruzione
ed il suo effetto è semplicemente ripetere Istruzione fintanto che Espressione ha il valore true .

Vediamo, ad esempio, un programma che effettua un conto alla rovescia utilizzando un ciclo while:  

// conto alla rovescia usando while
#include <iostream.h>
int main ()
{
  int n;
  cout << "Dammi il valore da cui partire > ";
  cin >> n;
  while (n>0) {
    cout << n << ", ";
    --n;
  }
  cout << "FUOCO!";
  return 0;
}
Dammi il valore da cui partire > 8
8, 7, 6, 5, 4, 3, 2, 1, FUOCO!

Quando il programma inizia viene chiesto all'utente il numero da cui partire con il conto alla rovescia. Quando inizia il ciclo while, se il valore introdotto soddisfa la condizione n>0 (ossia se n è maggiore di 0 ), il blocco di istruzioni viene ripetuto un numero indefinito di volte fintanto che la condizione (n>0) rimane vera.

L'esecuzione del programma precedente si può descrivere con la seguente procedura: cominciando da  main :
 


Occorre assicurarsi che il ciclo ad un certo punto termini. Per questo occorre prevedere, all'interno del blocco di istruzioni da ripetere, qualche metodo per fare in modo che prima o poi la condizione del ciclo diventi falsa. In caso contrario il ciclo continua indefinitamente. Nel nostro caso abbiamo inserito l'istruzione --n; che ci assicura che la condizione diventerà sicuramente falsa dopo un certo numero di ripetizioni: e precisamente quando n diventa 0 , ossia quando termina il nostro conto alla rovescia.

Naturalmente, siccome il blocco da ripetere è molto semplice, il calcolatore esegue il programma pressoché istantaneamente, senza apprezzabile intervallo tra un numero e il successivo del conto alla rovescia.
 

Il ciclo do-while.

Formato:
do Istruzione while (Condizione );
Esso funziona esattamente come il ciclo while tranne il fatto che Condizioneviene controllata dopo l'esecuzione di Istruzione invece che prima di eseguirla.Pertanto con il ciclo do-whilel'istruzione o blocco di istruzioni Istruzione viene eseguita sempre almeno una volta, anche se la condizione Condizione non dovesse risultare mai vera. Ad esempio, il seguente programma ripete ogni numero che l'utente inserisce finché non viene inserito 0.

// ripetitore di numeri
#include <iostream.h>
int main ()
{
  unsigned long n;
  do {
    cout << "Dammi un numero (0 per finire): ";
    cin >> n;
    cout << "Mi hai dato: " << n << "\n";
  } while (n != 0);
  return 0;
}
Dammi un numero (0 per finire): 12345
Mi hai dato: 12345
Dammi un numero (0 per finire): 160277
Mi hai dato: 160277
Dammi un numero (0 per finire): 0
Mi hai dato: 0

Il ciclo do-while si usa quando la condizione che deve terminare il ciclo viene determinata all'interno del ciclo stesso, come nel programma precedente in cui la terminazione del ciclo è determinata dall'input dato dall'utente. Se l'utente non introduce mai il valore 0 il ciclo continua all'infinito.
 

Il ciclo for.

Il suo formato è:
for ( Inizializzazione; Condizione ; Incremento) Istruzione
ed il suo scopo è quello di ripetere Istruzione finché la condizione Condizione rimane vera, esattamente come il ciclo while. A differenza di while il ciclo forpermette di indicare anche una istruzione di inizializzazione Inizializzazione ed una istruzione di incremento Incremento. Il ciclo forè quindi particolarmente adatto ad eseguire delle ripetizioni usando un contatore.

Esso opera in questo modo:

1, viene eseguita l'istruzione Inizializzazione . Generalmente essa è una assegnazione di un valore iniziale al contatore. Essa viene eseguita una sola volta.
2, viene controllata la condizione Condizione , se essa è true il ciclo continua (al punto 3 ), altrimenti il ciclo termina.
3, viene eseguita l'istruzione (o blocco di istruzioni) Istruzione.
4, infine, viene eseguita l'istruzione Incremento e si ritorna al punto 2.
Ecco un esempio di conto alla rovescia realizzato con un ciclo for :

// conto alla rovescia usando un ciclo for
#include <iostream.h>
int main ()
{
  for (int n=10; n>0; n--) {
    cout << n << ", ";
  }
  cout << "FUOCO!";
  return 0;
}
10, 9, 8, 7, 6, 5, 4, 3, 2, 1, FUOCO! 

I campi Inizializzazione e Incremento sono opzionali. Essi si possono quindi omettere ma non si può omettere il punto e virgola che li separa de Condizione . Possiamo quindi scrivere for (;n<10;) se non vogliami indicare né Inizializzazione Incremento oppure  for (;n<10;n++) se vogliamo includere il campo Incremento ma non Inizializzazione .

Possiamo inoltre usare l'operatore virgola (,) per specificare più di una azione in uno qualsiasi dei campi di for. L'operatore virgola è un separatore di istruzioni che serve a separare più istruzioni dove ne è richiesta una soltanto. Ad esempio, supponiamo di voler inizializzare più di una variabile per il nostro ciclo:

for ( n=0, i=100 ; n!=i ; n++, i-- )
{
  // qualcosa...
}
Questo ciclo viene ripetuto 50 volte sempre che né n i vengano modificate all'interno del ciclo:
n inizia da 0 e i da 100 e la condizione è n!=i (ossia che n sia diverso da i ). Siccome n viene aumentato di uno ed i viene diminuito di uno ad ogni iterazione, la condizione diventerà falsa esattamente dopo 50 iterazioni, quando sia n che i diventano uguali a 50.

Biforcazioni di controllo e salti.

L'istruzione break.

Possiamo uscire da un ciclo anche senza che la condizione sia soddisfatta usando una istruzione break. Essa si può usare per uscire da un ciclo infinito oppure per forzare la terminazione di un ciclo in presenza di qualche anomalia. Possiamo, ad esempio, interrompere il nostro conto alla rovescia prima che esso termini in modo naturale (a causa di un guasto al motore):

// esempio di interruzione di un ciclo
#include <iostream.h>
int main ()
{
  int n;
  for (n=10; n>0; n--) {
    cout << n << ", ";
    if (n==3)
    {
      cout << "conto alla rovescia interrotto!";
      break;
    }
  }
  return 0;
}
10, 9, 8, 7, 6, 5,  4, 3, conto alla rovescia interrotto!

L'istruzione continue .

L'istruzione continue termina immediatamente l'iterazione che si stà eseguendo ma non fa uscire dal ciclo. Essa salta tutto il resto del blocco di istruzioni che si sta eseguendo andando direttamente alla fine del blocco e passando quindi all'iterazione successiva. Ad esempio, potremmo saltare il numero 5 nel nostro conto alla rovescia: 

// esempio di interruzione di un ciclo
#include <iostream.h>
int main ()
{
  for (int n=10; n>0; n--) {
    if (n==5) continue;
    cout << n << ", ";
  }
  cout << "FUOCO!";
  return 0;
}
10, 9, 8, 7, 6, 4, 3, 2, 1, FUOCO!

L'istruzione  goto.

L'istruzione goto permette di saltare direttamente ad un altro punto del programma. Deve essere  usata soltanto nei casi di effettiva necessità (che sono estremamente  rari) perché essa sovverte la struttura del programma. 

Il punto di arrivo del salto è indicato dall'etichetta che compare come argomento dell'istruzione goto . L'etichetta deve essere premessa all'istruzione a cui saltare e separata da essa dal carattere due punti  (: ).

Questa istruzione non ha una reale utilità nella programmazione strutturata e nella programmazione orientata agli oggetti. Soltanto i fanatici della programmazione a basso livello possono trovare utile usarla. Come esempio, ecco il nostro conto alla rovescia realizzato con l'istruzione goto:

// esempio di ciclo con goto
#include <iostream.h>
int main ()
{
  int n=10;
  loop:
  cout << n << ", ";
  n--;
  if (n>0) goto loop;
  cout << "FUOCO!";
  return 0;
}
10, 9, 8, 7, 6, 5,  4, 3, 2, 1, FUOCO!

La funzione exit .

La funzione exit è definita nella libreria standard (stdlib.h).                                                            

Essa termina l'esecuzione del programma ritornando un codice di terminazione intero. La sua forma è:

exit(cod) ;
dove cod è un valore intero che viene ritornato al sistema operativo. Per convenzione il valore 0 di cod significa che il programma è terminato correttamente mentre un valore diverso da zero indica che è stato riscontrato un errore di esecuzione.                                      

L'istruzione di scelta: switch.

La sintassi dell'istruzione switch è un pò particolare. Il suo scopo è quello di confrontare il valore di una espressione con un certo numero di valori costanti ed eseguire il blocco di istruzioni associato al valore dell'espressione. La sua forma è: 
switch (Espressione) {
  case Costante1:
    blocco di istruzioni 1
    break;
  case Costante2:
    blocco di istruzioni 2
    break;
  .
  .
  .
  default:
    blocco di istruzioni di default
  }
Funziona così: switch valuta l'espressione Espressione e controlla se essa è uguale a Costante1, se lo è esegue il blocco di istruzioni 1 fino a quando arriva all'istruzione break che fa uscire dall'istruzione switch. Se Espressione non è uguale a Costante1 essa viene confrontata con Costante2. Se è uguale a Costante2 viene eseguito il blocco di istruzioni 2 fino all'istruzione break . Infine, se l'espressione non risulta uguale a nessuna delle costanti elencate viene eseguito il blocco di istruzioni di default se presente (la sezione default: è infatti opzionale).
Attenzione
: se si dimentica di inserire l'istruzione break alla fine dei blocchi di istruzioni l'esecuzione continua controllando anche le successive costanti ed infine eseguendo il blocco di istruzioni di default.                                                                                 

I seguenti frammenti di codice sono equivalenti:

Esempio switch Equivalente  if-else
switch (x) {
  case 1:
    cout << "x e' 1";
    break;
  case 2:
    cout << "x e' 2";
    break;
  default:
    cout << "valore di x ignoto";
  }
if (x == 1) {
  cout << "x e' 1";
  }
else if (x == 2) {
  cout << "x e' 2";
  }
else {
  cout << "valore di x ignoto";
  }

Attenzione : Se si dimentica di inserire l'istruzione break alla fine dei blocchi di istruzioni l'esecuzione continua controllando anche le successive costanti ed infine eseguendo il blocco di istruzioni di default. Questo può essere utile nel caso in cui vogliamo eseguire lo stesso blocco di istruzioni per diversi valori dell'espressione come nell'esempio seguente:

switch (x) {
  case 1:
  case 2:
  case 3:
    cout << "x e' 1, 2 o 3";
    break;
  default:
    cout << "x non e' 1, 2 o 3";
  }
Notare che switch si può usare soltanto per confrontare una espressione con delle costanti. Non si possono usare variabili o espressioni o intervalli di valori (case (n*2): e case (1..3): non vanno bene).

Se occorre controllare intervalli di valori o valori non costanti bisogna usare una sequenza di frasi if ed else if


Precedente:
         1-4. Comunicazione da console.

indice
Seguente:
2-2. Funzioni.