Istruzione If in Linguaggio C
Il linguaggio di programmazione C mette a disposizione l'istruzione condizionale if
che permette di eseguire del codice solo nel caso in cui una condizione risulta vera, ossia diversa da zero.
A seconda del valore della condizione di controllo l'istruzione if
eseguirà o meno un'istruzione che può essere semplice o composta.
Esiste anche la possibilità, attraverso la clausola else
, di specificare un'altra istruzione che venga eseguita nel caso opposto, ossia quando la condizione non è soddisfatta cioè pari a zero.
In questa lezione vedremo la sintassi delle istruzioni if
e delle clausole else
. Vedremo, anche, come gestire casi più complessi in cui è necessario concatenare oppure innestare più istruzioni if
tra di loro.
- Un'istruzione
if
è composta da una condizione di controllo e da un'istruzione; - L'istruzione viene eseguita se e soltanto se la condizione di controllo risulta diversa da zero ossia vera;
- L'istruzione può essere semplice o composta, ossia composta a sua volta da più istruzioni semplici;
- Per specificare un'istruzione da eseguire quando la condizione di controllo risulta falsa, cioè pari a zero si utilizza la clausola
else
; - Le istruzioni
if
possono essere concatenate e innestate tra di loro per casi in cui le condizioni da verificare non si riducano semplicemente al caso vero e falso.
Istruzione if
L'istruzione if
permette ad un programma di scegliere tra due alternative valutando il risultato di un'espressione. Nella sua forma più semplice, l'istruzione if
ha il seguente aspetto:
if ( espressione ) istruzione;
La prima cosa da notare è che le parentesi attorno all'espressione sono obbligatorie. Inoltre, a differenza di altri linguaggi di programmazione, l'istruzione if
non prevede la parola chiave then
.
Quando un'istruzione if
viene eseguita, l'espressione tra parentesi viene valutata. Se il risultato di tale espressione è diverso da zero, che come abbiamo visto nella lezione sulle espressioni logiche viene interpretato come falso dal linguaggio C, allora l'istruzione che segue l'espressione viene eseguita.
Utilizzando un diagramma di flusso possiamo rappresentare l'istruzione if
in questo modo:
Nel diagramma, l'istruzione if
è rappresentata come un rombo con all'interno la condizione da verificare. Nel caso in cui quest'ultima sia vera viene eseguita l'istruzione, altrimenti il controllo di flusso prosegue avanti. Per tal motivo l'istruzione if
rappresenta un'istruzione condizionale.
Istruzione if
Un'istruzione if
è un'istruzione condizionale che permette di eseguire o meno un'istruzione a seconda del valore di una condizione.
La sintassi di un'istruzione if
è la seguente:
if (espressione_logica)
istruzione;
Dove espressione_logica
è un'espressione logica, ossia un'espressione che viene considerata vera se il suo valore è diverso da zero. In tal caso l'istruzione istruzione
viene eseguita.
Proviamo a chiarire con un esempio:
if (a > b)
printf("A è maggiore di B\n");
In questo esempio l'istruzione printf
viene eseguita se la condizione a > b
risulta essere vera, ossia se ha un valore diverso da zero.
Attenzione a non confondere l'uguaglianza con l'assegnamento.
Bisogna prestare attenzione, quando si usa l'istruzione if
, a non confondere l'operatore di uguaglianza ==
con l'operatore di assegnamento =
.
Infatti, l'istruzione if
che segue:
if (a == 0)
è ben diversa dalla seguente:
if (a = 0)
Infatti, nel secondo caso l'espressione tra parentesi, i = 0
, assegna zero alla variabile i
e restituisce il valore 0
, per cui risulta sempre falsa.
Confondere l'uguaglianza, ==
, con l'assegnamento, =
, è forse uno degli errori più comuni che si trovano nei programmi scritti in C. La motivazione è data forse dal fatto che in matematica il simbolo di uguale =
è identico al simbolo usato per l'assegnamento in linguaggio C.
Alcuni compilatori generano un warning se trovano un operatore di assegnamento dove normalmente dovrebbe esserci un operatore di uguaglianza, ma non bisogna fare affidamento su questa feature.
Verifica di appartenenza in un intervallo
Spesso, in linguaggio C si utilizza l'istruzione if
per verificare se il valore di una certa variabile ricada o meno all'interno di un intervallo.
Ad esempio, potremmo voler verificare se il valore contenuto nella variabile i
sia compreso tra 0 ed n
. In termini matematici, vogliamo verificare se:
Per far questo, in linguaggio C si può scrivere in questo modo:
if (0 <= i && i <= n)
Mentre, se vogliamo testare la condizione opposta possiamo scrivere in questo modo:
if (i < 0 || i > n)
Da notare che in questo secondo caso abbiamo usato l'operatore or, ||
, anziché l'operatore and, &&
.
Istruzioni composte
Prima, nel definire la sintassi dell'istruzione if
abbiamo usato la forma seguente:
if ( espressione ) istruzione;
Da notare che istruzione
è singolare, ossia l'istruzione if
può essere seguita da una sola istruzione.
Come possiamo fare in modo che l'istruzione if
determini l'esecuzione di due o più istruzioni? Possiamo utilizzare le istruzioni composte, ossia istruzioni della forma seguente:
{
istruzione_1;
istruzione_2;
/* ... */
istruzione_n;
}
In sostanza, mettendo le parentesi graffe attorno ad una sequenza di istruzioni forziamo il compilatore a trattarle come un'unica istruzione.
Istruzioni composte
Un'istruzione composta è un'istruzione formata dalla sequenza di una o più istruzioni racchiuse tra parentesi graffe. La sintassi è la seguente:
{
istruzione_1;
istruzione_2;
/* ... */
istruzione_n;
}
Un esempio di istruzione composta è la seguente:
{
printf("All'interno di un'istruzione composta\n");
a++;
}
Da notare che le istruzioni interne all'istruzione composta terminano con un punto e virgola mentre l'istruzione composta non ha bisogno del punto e virgola finale.
A questo punto, possiamo combinare le istruzioni if
e le istruzioni composte in questo modo:
if (a < b) {
printf("All'interno di un'istruzione composta\n");
a++;
}
L'istruzione composta da printf
e dall'incremento della variabile a
verrà eseguita se e soltanto se la condizione a < b
risulta essere verificata (ossia diversa da zero).
Clausola else
Un'istruzione if
può essere seguita da una clausola else
in questo modo:
if ( espressione ) istruzione_1; else istruzione_2;
Con questa sintassi stiamo dicendo al compilatore di eseguire l'istruzione istruzione_1
se espressione
ha un valore diverso da zero, oppure di eseguire l'istruzione istruzione_2
nel caso opposto.
Possiamo rappresentare la clausola else
con un diagramma di flusso in questo modo:
Come è possibile osservare dal diagramma di flusso, l'istruzione if
con clausola else
permette di biforcare il controllo di flusso in due rami: il ramo vero che viene percorso nel caso in cui la condizione risulta vera, e il ramo falso che viene percorso in caso contrario.
Un possibile esempio di utilizzo di un'istruzione if
con clausola else
è il seguente:
if (a < b)
max = b;
else
max = a;
Bisogna notare che entrambe le istruzioni dei due rami sono terminate da un punto e virgola.
Clausola else
La clausola else
di un'istruzione if
permette di selezionare un'istruzione nel caso la condizione risulti falsa. La sintassi da adoperare è la seguente:
if (condizione)
istruzione_ramo_vero;
else
istruzione_ramo_falso;
Piazzamento della clausola else
Da un punto di vista sintattico il compilatore C non impone nessun vincolo sul piazzamento della clausola else
.
Possiamo, infatti, scrivere codice allineando la clausola else
all'istruzione if
in questo modo:
if (a < b)
max = b;
else
max = a;
Oppure, se le istruzioni sono brevi, possiamo essere più sintetici e scrivere il tutto così:
if (a < b) max = b;
else max = a;
Istruzioni if
innestate
Non vi è alcuna restrizione sul tipo di istruzioni che possono apparire all'interno di un'istruzione if
. Infatti, non è raro trovare istruzioni if
innestate, ossia istruzioni if
interne ad altre istruzioni if
.
Prendiamo un esempio: vogliamo trovare il massimo tra tre variabili intere a
, b
e c
. Possiamo implementare la ricerca del massimo in questo modo:
/* Trova il massimo tra a, b e c */
if (a > b)
if (a > c)
max = a;
else
max = c;
else
if (b > c)
max = b;
else
max = c;
Usando un diagramma di flusso, possiamo rappresentare lo stralcio di codice soprastante in questo modo:
Abbiamo realizzato la ricerca del massimo innestando più istruzioni if
. Non vi è un limite al numero di istruzioni if
che possono essere innestate. L'unico limite è, probabilmente, la leggibilità del codice. Infatti, all'aumentare delle istruzioni if
innestate, il codice può diventare particolarmente complesso.
Istruzioni if
innestate
Le istruzioni if
possono contenere al proprio interno altre istruzioni if
; in tal caso si parla di istruzioni if
innestate.
La sintassi generale per innestare istruzioni if
è la seguente:
if (condizione_1)
if (sub_condizione_1)
istruzione_1;
else
istruzione_2;
else
if (sub_condizione_2)
istruzione_3;
else
istruzione_4;
Un modo per semplificare la scrittura di istruzioni if
innestate è quello di adoperare le parentesi graffe anche quando queste non sono necessarie. Pertanto possiamo riscrivere il codice di sopra in questo modo:
/* Trova il massimo tra a, b e c */
if (a > b) {
if (a > c)
max = a;
else
max = c;
} else {
if (b > c)
max = b;
else
max = c;
}
Usare le parentesi graffe attorno alle istruzioni, anche quando non sono strettamente necessarie, è come usare le parentesi tonde nelle espressioni matematiche: aiutano a rendere il codice più leggibile e, allo stesso tempo, evitano che il compilatore possa interpretare il codice in maniera diversa rispetto all'intenzione con cui l'abbiamo scritto.
In alcuni casi, può essere utile aggiungere le parentesi graffe anche attorno alle istruzioni delle istruzioni if
innestate, in questo modo:
/* Trova il massimo tra a, b e c */
if (a > b) {
if (a > c) {
max = a;
} else {
max = c;
}
} else {
if (b > c) {
max = b;
} else {
max = c;
}
}
Usare le parentesi graffe in questo modo ha due vantaggi:
- Il programma risulta più semplice da modificare in quanto possiamo aggiungere altre istruzioni alle clausole
if
oelse
in maniera rapida; - Evita gli errori che possono scaturire dall'aver dimenticato le parentesi quando aggiungiamo istruzioni ad una clausola.
Istruzioni if
concatenate
Possono verificarsi situazioni in cui la scelta che un programma debba effettuare non dipenda soltanto dal fatto che una condizione possa essere vera o falsa. Anzi, può capitare che il programma debba verificare una serie di condizioni e debba fermarsi non appena una di queste risulti vera.
In tal caso, in linguaggio C è possibile adoperare le istruzioni if
concatenate (cascaded).
Per esempio, supponiamo di voler stampare un messaggio a schermo che ci dica se il valore di una variabile intera sia minore, uguale o maggiore di zero. In tal caso, non possiamo adoperare una semplice istruzione if
ma dobbiamo metterne due in cascata, in questo modo:
if (a < 0)
printf("a è minore di zero\n");
else
if (a == 0)
printf("a è uguale a zero\n");
else
printf("a è maggiore di zero\n");
Usando un diagramma di flusso, possiamo rappresentare il codice di sopra in questo modo:
Anche se la seconda istruzione if
è innestata nella prima nella pratica non si usa l'indentazione per evidenziare la seconda istruzione if
. Quello che si fa di solito è di far seguire gli if
alle clausole else
ed allineare gli else
al primo if
:
if (a < 0)
printf("a è minore di zero\n");
else if (a == 0)
printf("a è uguale a zero\n");
else
printf("a è maggiore di zero\n");
Facendo così le istruzioni if
concatenate assumono un aspetto tipico:
if ( espressione_1 )
istruzione_1;
else if ( espressione_2 )
istruzione_2;
else if ( espressione_3 )
istruzione_3;
/* ... */
else if ( espressione_n )
istruzione_n;
else
istruzione_finale;
Questo modo di indentare le funzioni if
concatenate evita il problema di avere codice eccessivamente indentato quando il numero di test da effettuare cresce a dismisura. Inoltre, ha il vantaggio di risaltare all'occhio come una semplice successione di test.
Istruzioni if
concatenate
Le istruzioni if
concatenate sono istruzioni if
che contengono all'interno della clausola else
un'altra istruzione if
e così via. Esse sono utili quando le condizioni da testare sono più di una ed esistono più casi differenti.
La forma generale delle istruzioni if
innestate è la seguente:
if ( espressione_1 )
istruzione_1;
else if ( espressione_2 )
istruzione_2;
else if ( espressione_3 )
istruzione_3;
/* ... */
else if ( espressione_n )
istruzione_n;
else
istruzione_finale;
Bisogna sempre ricordare che le istruzioni if
concatenate non sono un nuovo tipo di istruzioni. Si tratta semplicemente di un'istruzione if
ordinaria che ha al suo interno un'altra istruzione if
nella sua clausola else
, e così via.
Problema del Dangling else
Quando si adoperano le istruzioni if
innestate bisogna fare attenzione al famigerato problema del "Dangling else
" o, in italiano, else
"appeso".
Per chiarire il tutto prendiamo l'esempio che segue:
if (b != 0)
if (a != 0)
result = a / b;
else
printf("Errore: b è uguale a 0\n");
A prima vista lo stralcio di codice soprastante sembra essere corretto. Ad uno sguardo superficiale sembra che il codice verifichi dapprima che b
sia diverso da zero e in caso contrario stampi un messaggio di errore evitando una divisione per zero. L'indentazione, però, ci trae in inganno.
In realtà il codice di sopra è ambiguo in quanto non è chiaro a quale delle due istruzioni if
la clausola else
appartenga. Il linguaggio C segue la convenzione di associare una clausola else
all'istruzione if
più prossima che non sia stata già accoppiata ad un'altra clausola else
.
Per tal motivo, una versione correttamente indentata del codice precedente è la seguente:
/* Versione correttamente indentata */
if (b != 0)
if (a != 0)
result = a / b;
else
printf("Errore: b è uguale a 0\n");
In questo caso, quindi, la clausola else
sarà sempre associata all'istruzione if
più vicina. L'unico modo per cambiare questo comportamento è quello di utilizzare le parentesi graffe e forzare l'associazione all'istruzione if
esterna, in questo modo:
/* Versione che associa l'else all'if esterno */
if (b != 0) {
if (a != 0)
result = a / b;
} else
printf("Errore: b è uguale a 0\n");
Il problema del dangling else
mostra l'importanza dell'utilizzo delle parentesi graffe. Usandole è possibile rendere il codice non ambiguo. Spesso, infatti, molti sviluppatori preferiscono usare sempre le parentesi anche quando non sono necessarie per non incappare in questo problema.
Problema del Dangling else
In linguaggio C, in presenza di istruzioni if
innestate, se non vengono esplicitamente racchiuse le istruzioni tra parentesi graffe, si usa la convenzione di associare un else
al più vicino if
di cui ne è sprovvisto.
In questo caso, la clausola else
è associata al secondo if
:
if (condizione_1)
if (condizione_2)
istruzione_1;
else
istruzione_2;
In quest'altro caso, usando esplicitamente le parentesi graffe, la clausola else
è associata al primo if
:
if (condizione_1) {
if (condizione_2)
istruzione_1;
} else
istruzione_2;
In entrambe i casi, l'indentazione del codice è ininfluente.
In Sintesi
In questa lezione abbiamo visto la sintassi delle istruzioni condizionali if
in linguaggio C. Abbiamo visto come specificare la condizione da verificare e come specificare le istruzioni da eseguire nel caso in cui la condizione sia vera, ossia diversa da zero, e viceversa attraverso la clausola else
.
Per condizioni più complesse, che non si limitino al caso vero o falso abbiamo visto come concatenare più istruzioni if
tra di loro. Inoltre, abbiamo visto anche come innestare più istruzioni if
, l'una dentro l'altra, scoprendo, anche, che in tal caso vi sono delle insidie: il problema del Dangling else
.
Nella prossima lezione studieremo la seconda istruzione condizionale messa a disposizione dal linguaggio C: l'istruzione switch