Strutture di controllo nel linguaggio C
Le strutture di controllo sono delle particolari istruzioni, tipiche dei linguaggi imperativi, che permettono di eseguire delle istruzioni secondo determinate condizioni.
Teorema di Bohm-Jacopini Gli informatici Corrado Bohm e Giuseppe Jacopini, nel 1966 enunciarono il seguente teorema: Ogni algoritmo può essere implementato utilizzando tre sole strutture
- Sequenziali: blocchi di codice procedurale le cui istruzioni vengono eseguite nella stessa sequenza con le quali sono state scritte nel codice sorgente. Queste possono contenere strutture di controllo condizionali e/o iterative.
- Condizionali: permettono di specificare due rami o blocchi (uno del vero ed uno del falso) di codice, di cui solo uno verrà eseguito in base al risultato booleano dell'espressione condizionale (vero/falso).
- Iterative: eseguono lo stesso blocco di codice, in modo ripetitivo, fino a quando l'espressione condizionale è vera.
Istruzioni e Blocchi
modificaEspressioni come y=5 o i++ diventano istruzioni quando sono seguite da un punto e virgola. In C, il punto e virgola è un terminatore di istruzione e non, come in Pascal, un semplice separatore.
In C possiamo utilizzare le parentesi graffe { e } per raggruppare più istruzioni in un blocco, che viene visto dal punto di vista sintattico come un'instruzione unica. Le parentesi che racchiudono le istruzioni di funzioni, quelle dopo un if o dopo un while sono l'esempio più palese. Molto importante da ricordare è che dopo la parentesi graffa di chiusura di un blocco non si devono mettere punto e virgola (anche se ciò non è considerato un errore di sintassi).
La cosa importante da capire, per chi non ha mai programmato, è che queste istruzioni verranno eseguite solo nel momento in cui il programma entrerà nel blocco di istruzioni, e non in altri momenti dell'esecuzione del programma o in sequenza diversa. Inoltre l'esecuzione delle istruzioni avviene in modo sequenziale.
istruzione1;
istruzione2;
istruzione3;
Pertanto, non potrà mai succedere che l'istruzione2 venga eseguita dopo l'istruzione3. Questo discorso vale anche per le istruzioni di assegnazione e di lettura delle variabili, per le quali bisogna fare attenzione all'irreversibilità dell'operazione di scrittura.
Per rendere il codice più pulito è inoltre possibile rimuovere le parentesi graffe dei blocchi se all'interno di un blocco esiste una sola istruzione, ne vedremo degli esempi con la spiegazione delle altre strutture.
Strutture condizionali
modificaIF
modificaSintassi:
if (condizione){ /*se la "condizione" è vera*/
istruzione 1; /*esegue le istruzioni altrimenti (se falsa) esce dal blocco dell'if*/
.
.
.
istruzione n;
}
Questa struttura funziona nel seguente modo: viene verificata la condizione dell'istruzione if. Se essa risulta vera, vengono eseguite le istruzioni immediatamente successive, altrimenti, se risulta falsa, viene saltato il blocco dell'if (quindi non vengono eseguite le istruzioni interne all'if) e vengono eseguite le istruzioni immediatamente successive.
IF...ELSE
modificaSintassi:
if (condizione1) { /*se la condizione1 è vera*/
istruzioni1; /*allora esegue le istruzioni1*/
}
else if (condizione2) { /*altrimenti se la condizione2 è vera*/
istruzioni2; /*allora esegue le istruzioni2*/
}
else { /*altrimenti*/
istruzioni3; /*esegue le istruzioni3*/
}
La struttura è un'estensione del semplice IF, infatti funziona in maniera uguale con l'aggiunta che, se la condizione è falsa, allora viene eseguito il blocco di istruzioni dell'ELSE. Esiste anche un modo molto più sintetico per scrivere l'istruzione if...else, ed è il seguente
'''condizione '''?''' istruzioni1 ''':''' istruzioni2;'''
SWITCH
modificaSintassi
switch(valore){/*seleziona il valore*/
case 1: /*confronta con 1*/
istruzione1; /* esegue l'istruzione1*/
break;
case 2:
istruzioni2;
break;
.
.
case n:
istruzioni n
default: /*acquisisce tutti gli altri valori diversi da quelli della selezione (case)*/
istruzioni;
}
Molto simile alla logica dell'istruzione if...else, lo switch viene usato quando bisogna confrontare i diversi valori (case1,case2...casen) che una variabile o un'espressione(switch(val)) può assumere e quindi in base ad essi eseguire azioni distinte. L'istruzione break serve per uscire dal blocco dello switch, infatti non avrebbe senso (in molti casi) continuare a confrontare( ma questo non vuol dire che, in alcune situazioni, il break ci debba sempre essere).
Esempio
switch (voto){/*seleziona la variabile voto*/
case 18: /*osserva se il voto è uguale a 18*/
studenti18++; /*studenti18 = studenti che hanno preso 18. Conta gli studenti che hanno un voto uguale a 18*/
break; /*esce dal blocco dello switch*/
case 19:
studenti19++;
break;
.
.
case 30:
studenti30++;
default:/*controlla se viene immesso un valore errato*/
printf("Inserisci un voto maggiore uguale a 18 o minore uguale a 30\n");
}
Strutture iterative
modificaI comandi di iterazione consentono di eseguire ripetutamente determinate istruzioni finché una certa condizione rimane vera.
WHILE
modificaSintassi
while (condizione){/*esegui le istruzioni finché(mentre) la condizione risulta vera*/
istruzioni;
}
Struttura usata quando si necessità di fare un controllo a priori.
Esempio-Sommare i numeri da 1 a 100
int i=1;
int somma=0;
while (i <= 100) { /*fai finche i è minore o uguale a 100*/
somma += i;
i++;
}
Questo esempio mostra come avviene la somma dei numeri interi da 1 a 100 mediante l'uso del comando di iterazione while. In questo caso, per permettere l'uscita dal while, viene usata una variabile contatore; la variabile i, che deve essere prima di tutto inizializzata, poi incrementata (o decrementata a seconda dello scopo) per permettere che essa raggiunga il valore che non verificherà più la condizione del while.
DO...WHILE
modificaSintassi
do{
istruzione/i;
}while(condizione);
Il do...while è molto simile al while, con la sola differenza che il controllo viene effettuato alla fine e quindi le istruzioni interne al blocco vengono eseguite almeno una volta, senza tener conto se la condizione nel while sia vera o falsa.
Esempio
do{ /*fai*/
printf("Premi C per continuare\n");
scanf("%c", &continua);
}while(continua != 'C');
FOR
modificasintassi
for (inizializzazione; condizione; incremento o decremento){
istruzione/i;
}
La logica del comando for è molto simile al while, infatti potrebbero essere benissimo usati per gli stessi scopi. Rimane il fatto che il for è molto più sintetico del while e viene usato quando si conosce già il numero di iterazioni che bisogna compiere.
Esempio-Stampare a video una successione di cifre da 2 a 20
for(i=2; i<=20; i++){/*per i che va da 1 a 20*/
printf("%d\n", i);/*stampa i valori*/
}
Dirty Tricks
modificaI dirty tricks, letteralmente trucchi sporchi, sono dei trucchetti ormai in uso comune che semplificano la lettura del codice. Qualche programmatore preferisce evitare il loro utilizzo poiché non viene rispettato il Teorema di Bohm-Jacopini , che abbiamo annunciato in precedenza. In generale il loro utilizzo non è sinonimo di codice scritto in malo modo. Anzi, se il vostro codice deve esser letto da altri sarà più facile da leggere e da intuire; ma attenzione ad non abusarne.
Istruzione break
modificafor (...; ...; ...) {
if (...)/*se la condizione è vera*/
break;/*allora esci dal ciclo*/
...
}
L'istruzione break(come si può intuire dall'esempio) provoca l'uscita immediata da un ciclo appena si verifica una specifica condizione. Un classico utilizzo dell'istruzione break è creare un ciclo infinito, nella condizione basta semplicemente mettere il numero uno e uscire dal ciclo solo all'avverarsi di una condizione.
Istruzione continue
modificafor (...; ...; ...) {
if (...)/*se la condizione è vera*/
continue; /*allora non eseguire le istruzioni successive*/
...
}
L'istruzione continue salta un blocco di istruzioni al verificarsi di una certa condizione, continuando però con l'iterazione.
GOTO e le LABEL
modificaIl C fornisce inoltre anche l'istruzione goto, e le label (“etichette”) per potere ramificare l'esecuzione. Nella pratica spesso la stesura di codice che non utilizza l'istruzione goto risulta meno difficoltosa.
Nonostante ciò, ci sono alcune situazioni nelle quali l'impiego del goto può rivelarsi conveniente. Il caso più comune è quando si necessita d'uscire contemporaneamente da due o più cicli innestati. L'istruzione break, permette soltanto l'uscita dal ciclo più interno, quindi si rivela in questo caso insufficiente.
for ( .... )
for ( .... )
{
....
if (disaster)
goto error;
}
....
error:
ripristina la situazione