5 Livello 3
Il presente capitolo approfondisce i meccanismi di deviazione del flusso di esecuzione nel linguaggio C, analizzando la sintassi e l’applicazione dei costrutti di selezione if-else e switch, le regole di valutazione delle espressioni logiche e l’impiego degli operatori relazionali e booleani.
5.1 Istruzioni di controllo decisionali
Nel linguaggio C, le istruzioni if e switch costituiscono i meccanismi fondamentali per le strutture condizionali. Il costrutto if opera valutando un’espressione logica generica e instradando il programma verso un determinato ramo in base al test effettuato. Lo switch implementa una struttura di selezione multivia confrontando il valore di una singola espressione (esclusivamente di tipo intero o carattere) con un elenco di costanti predefinite, semplificando la gestione di scenari a scelta multipla senza concatenare una sequenza di verifiche condizionali.
5.1.1 Istruzione if
L’istruzione di controllo più comune è il costrutto if, che rappresenta la forma più elementare di ramificazione. Tale costrutto valuta un’espressione logica, se l’espressione risulta vera (true), l’istruzione (o il blocco di istruzioni) ad essa associata viene eseguita, in caso contrario (false), l’istruzione viene omessa. È inoltre possibile combinare questo meccanismo con l’istruzione else per definire un ramo alternativo da eseguire qualora la condizione iniziale non sia soddisfatta.
È fondamentale tenere presente che nel linguaggio C la logica booleana viene valutata su base numerica (non esiste un tipo di dato booleano, almeno fino al C99). Il C99 introduce il tipo _Bool e l’header <stdbool.h>. Qualsiasi valore diverso da zero viene interpretato come vero (true), mentre il valore zero rappresenta il falso (false).
In C, un blocco di istruzioni è rigidamente delimitato dall’uso delle parentesi graffe {}. Questo dettaglio sintattico è cruciale. Se si desidera eseguire un insieme di più istruzioni condizionate, l’uso delle graffe è obbligatorio. In assenza delle parentesi {}, il compilatore associerà all’istruzione if (o else) esclusivamente la prima istruzione immediatamente successiva, ignorando le righe seguenti, che verranno eseguite in modo sequenziale indipendentemente dall’esito del test.
5.1.2 Operatori relazionali e logici
Operatori relazionali e logici possono essere utilizzati per costruire espressioni più complesse. Gli operatori relazionali consentono di effettuare confronti tra variabili o espressioni.
==Uguaglianza: verifica se i due operandi hanno lo stesso valore.!=Diversità: verifica se i due operandi hanno valori differenti.<Minore di: verifica se l’operando a sinistra è strettamente minore di quello a destra.>Maggiore di: verifica se l’operando a sinistra è strettamente maggiore di quello a destra.<=Minore o uguale a: verifica se l’operando a sinistra è minore o uguale a quello a destra.>=Maggiore o uguale a: verifica se l’operando a sinistra è maggiore o uguale a quello a destra.
La valutazione di un confronto genera un’espressione logica, ovvero un costrutto sintattico il cui esito definisce un valore di verità: VERO o FALSO. In C tale esito è concretamente il valore intero 1 se la condizione è verificata, 0 in caso contrario. L’espressione 5 > 3, ad esempio, vale 1.
Per strutturare predicati condizionali complessi è necessario concatenare più espressioni. Questa operazione si realizza mediante gli operatori logici (o operatori booleani):
!NOT (inversione logica): operatore unario che inverte il valore di verità dell’operando associato.&&AND (congiunzione logica): operatore binario che restituisce VERO esclusivamente se entrambi gli operandi sono veri.||OR (disgiunzione logica): operatore binario che restituisce VERO se almeno uno dei due operandi è vero.
Gli operatori && e || godono di una proprietà importante, nota come valutazione a corto circuito. Il secondo operando viene valutato solo se strettamente necessario. Se il primo operando di un && è falso, il risultato complessivo è certamente falso e il secondo operando viene del tutto ignorato. In modo speculare, se il primo operando di un || è vero, il risultato è certamente vero. Questo comportamento può essere sfruttato per scrivere condizioni sicure.
Vediamo due esercizi.
Scrivere un programma in C che permetta di valutare se un numero intero (letto da tastiera) è pari o dispari.
#include <stdio.h>
int main(void){
int n;
printf("Inserisci un numero intero: ");
scanf("%d",&n);
if(n%2 == 0)
printf("\n%d e' pari\n",n);
else
printf("\n%d e' dispari\n",n);
return 0;
}Scrivere un programma in C che permetta di verificare se un anno (letto da tastiera) è bisestile.
#include <stdio.h>
int main(void){
int anno;
printf("Inserisci l'anno: ");
scanf("%d",&anno);
if ((anno%4 == 0 && anno%100 != 0) || anno%400 == 0)
printf("\n%d e' bisestile\n", anno);
else
printf("\n%d non e' bisestile\n", anno);
return 0;
}5.1.3 Istruzione switch
Questo costrutto consente di selezionare uno specifico blocco di istruzioni in base al valore (int o char) assunto da una variabile o da un’espressione, verificata per uguaglianza rispetto a un elenco di valori costanti.
Vediamo lo stesso esercizio di prima.
Scrivere un programma in C che permetta di valutare se un numero intero (letto da tastiera) è pari o dispari.
#include <stdio.h>
int main(void){
int n;
printf("Inserisci un numero intero \n");
scanf("%d",&n);
switch (n % 2) {
case 0:
printf("Il numero %d e' pari\n",n);
break;
default:
printf("Il numero %d e' dispari\n",n);
break;
}
return 0;
}La sintassi del costrutto richiede due precisazioni importanti:
break: ogni ramocaseviene chiuso dall’istruzionebreak, che interrompe loswitche trasferisce il controllo alla prima istruzione successiva al blocco. Ilbreaknon è obbligatorio, ma la sua omissione cambia radicalmente il comportamento del costrutto, una volta trovata la corrispondenza, l’esecuzione prosegue “a cascata” (fall-through) attraverso tutti i case successivi, ignorandone le etichette, fino al primobreakincontrato o alla fine delloswitch. Dimenticare unbreakè uno degli errori più classici e insidiosi del linguaggio C.default: è possibile aggiungere un ramo opzionaledefault, che viene eseguito quando il valore dell’espressione non corrisponde ad alcuna delle costanti elencate. Svolge un ruolo analogo a quello dell’elsenel costruttoif.