Perché i programmi hanno bisogno di diramazioni
Tutti i programmi visti finora sono andati dritti: riga 1, riga 2, riga 3. I programmi reali prendono decisioni. La password è corretta? Il numero è negativo? Il file si è aperto? L'istruzione if è il modo in cui il C fa una domanda ed esegue del codice solo quando la risposta è sì.
La condizione temperature > 30 viene valutata una volta. È vera, quindi il messaggio viene stampato. L'ultima riga sta fuori dalle graffe, quindi viene stampata in ogni caso. Cambia 31 in 18 ed eseguilo di nuovo: il primo messaggio sparisce e il secondo resta.
Sintassi: parentesi, graffe e niente punto e virgola
Un if in C ha una forma fissa:
if (condizione) {
// eseguito quando la condizione e' diversa da zero
}
Tre regole da imparare presto. Le parentesi attorno alla condizione sono obbligatorie. Non esiste la parola chiave then. E non c'è punto e virgola dopo la parentesi di chiusura: uno messo per sbaglio chiude l'istruzione proprio lì:
// BUG: l'if non fa niente; la printf viene eseguita sempre
if (x > 10);
{
printf("grande\n");
}
Compila senza problemi. Il ; è un'istruzione vuota che diventa il corpo dell'if, e le graffe sotto sono solo un blocco a sé che viene sempre eseguito. Compilare con gcc -Wall lo intercetta.
Le graffe in sé sono facoltative per una singola istruzione, ma lasciale. Un if senza graffe a cui più avanti si aggiunge una seconda riga è una fonte classica di bug, perché in C l'indentazione non raggruppa le istruzioni.
else ed else if
else fornisce l'altro percorso:
Per più di due esiti, concatena con else if. Il C controlla ogni condizione in ordine e si ferma alla prima vera:
Qui l'ordine conta moltissimo. Dato che la catena si ferma alla prima corrispondenza, il secondo test vede solo punteggi sotto 90, ed è per questo che score >= 80 non deve dire anche && score < 90. Scrivi la catena dalla condizione più restrittiva alla meno restrittiva, altrimenti ogni valore finisce nel primo ramo.
L'else finale è facoltativo. Mettilo ogni volta che la catena deve coprire tutti i casi; la sua assenza significa "se niente corrisponde, non fare nulla", il che va bene ma deve essere una scelta voluta.
La verità: 0 è falso, tutto il resto è vero
Il linguaggio C di base non ha un tipo booleano separato nelle condizioni. Un if chiede semplicemente se l'espressione è diversa da zero.
Le ultime due righe mostrano l'altra metà della regola: gli operatori di confronto sono espressioni normali che producono 1 per vero e 0 per falso. Ecco perché if (5 > 2) funziona: in realtà è if (1).
È anche il motivo per cui -3 è vero in C. La regola è diverso da zero, non positivo. Se vuoi sapere se un numero è positivo, scrivi if (n > 0), non if (n).
C99 ha aggiunto <stdbool.h> con un vero tipo bool più true e false, che nel codice nuovo si legge meglio. Sotto il cofano restano 1 e 0, quindi la regola qui sopra non cambia: vedi i booleani in C.
Gli operatori di confronto e logici che userai nelle condizioni:
== uguale a && AND logico (entrambi i lati veri)
!= diverso da || OR logico (almeno un lato vero)
< minore di ! NOT logico
> maggiore di
<= minore o uguale a
>= maggiore o uguale a
&& e || sono a corto circuito: a && b non valuta mai b se a è falso, e a || b non valuta mai b se a è vero. È una garanzia, non un'ottimizzazione, quindi puoi scrivere tranquillamente if (n != 0 && total / n > 5): la divisione non può essere eseguita quando n vale zero.
Il bug = contro ==
È in assoluto l'errore più comune nelle condizioni in C:
// BUG: assegna 5 a x, poi valuta 5 (diverso da zero), quindi viene eseguito SEMPRE
if (x = 5) {
printf("x vale cinque\n");
}
Il C permette l'assegnazione dentro un'espressione, quindi la riga è valida. Salva 5 in x, produce il valore 5, e 5 è diverso da zero: il ramo viene eseguito ogni singola volta, e x è stata sovrascritta in silenzio.
Due difese. Primo, compila sempre con gli avvisi attivi:
gcc -Wall -Wextra program.c -o program
GCC e clang segnalano entrambi "suggest parentheses around assignment used as truth value". Secondo, alcuni programmatori scrivono la costante prima della variabile, if (5 == x), così che un if (5 = x) scritto per sbaglio sia un errore di compilazione vero e non un bug silenzioso. Quello stile è questione di gusti; attivare gli avvisi no.
Annidamento
Un if può contenere un altro if. L'annidamento esprime condizioni che hanno senso solo quando una precedente è vera:
Due livelli si leggono bene. Quattro no. Quando l'annidamento si fa profondo, cerca un modo per appiattirlo: spesso combinando le condizioni con &&, oppure gestendo prima i casi di errore e uscendo subito:
// piu' piatto: gestisci le eccezioni, poi fai il lavoro
if (age < 18) { printf("Solo per adulti.\n"); return 0; }
if (!hasTicket) { printf("Compra un biglietto, per favore.\n"); return 0; }
printf("Benvenuto.\n");
L'else pendente
Quando un if senza graffe è annidato dentro un altro, a quale if appartiene un else? La regola del C: un else si lega all'if senza else più vicino, indipendentemente da come è indentato il codice.
// L'else appartiene all'if INTERNO, nonostante l'indentazione
if (a > 0)
if (b > 0)
printf("entrambi positivi\n");
else
printf("a non e' positivo\n"); // SBAGLIATO: eseguito quando a > 0 e b <= 0
L'indentazione dice che l'else va con l'if esterno. Il compilatore lo abbina a quello interno. Le graffe eliminano del tutto l'ambiguità, ed è questo il vero argomento per usarle sempre:
if (a > 0) {
if (b > 0) {
printf("entrambi positivi\n");
}
} else {
printf("a non e' positivo\n");
}
L'operatore ternario
Quando una diramazione esiste solo per scegliere un valore, l'operatore condizionale ? : lo dice in un'unica espressione:
Leggi cond ? x : y come "se cond allora x, altrimenti y". Viene valutato esattamente uno dei due rami, e l'insieme produce un valore, ed è per questo che si può assegnare o passare come argomento dove un'istruzione non potrebbe.
Tienilo per le scelte di valore semplici. I ternari annidati (a ? b : c ? d : e) sono validi e quasi sempre più difficili da leggere della catena di else if che sostituiscono.
Domande frequenti
Come si scrive un'istruzione if else in C?
Metti la condizione tra parentesi e il codice tra graffe: if (score >= 60) { printf("Pass\n"); } else { printf("Fail\n"); }. Il blocco else viene eseguito solo quando la condizione è falsa, e in C non esiste la parola chiave then.
Cosa conta come vero in un if in C?
Qualsiasi valore diverso da zero. Il C non ha un tipo booleano dedicato nella sua sintassi di base, quindi if (n) viene eseguito quando n vale qualsiasi cosa tranne 0, e if (!n) solo quando n vale esattamente 0. Gli operatori di confronto come == e < producono 1 per vero e 0 per falso.
Qual è la differenza tra = e == in C?
== confronta, = assegna. Scrivere if (x = 5) assegna 5 a x e poi valuta 5, che è diverso da zero, quindi il ramo viene eseguito sempre. Compila senza errori, ed è per questo che è il bug da principianti più comune nelle condizioni in C: la maggior parte dei compilatori lo segnala con -Wall.
Come funziona l'operatore ternario in C?
condition ? valueIfTrue : valueIfFalse è un'espressione che produce uno tra due valori, quindi può stare dentro un'assegnazione o come argomento di printf: int max = a > b ? a : b;. Usalo per scegliere un valore, e un normale if quando devi eseguire delle istruzioni.