Strutture di controllo in C: if, switch, while, do-while e for
Selezione con if e switch, macchine a stati, cicli while, do-while e for, cicli annidati, break e continue, il ciclo infinito dei microcontrollori ed errori frequenti.
Un programma che esegue sempre le stesse istruzioni nello stesso ordine serve a poco. Le strutture di controllo permettono di scegliere che cosa fare in base ai dati e di ripetere operazioni. Sono le tre strutture della programmazione strutturata (sequenza, selezione, iterazione) tradotte in C. In questa lezione vediamo if, switch, while, do-while e for, con esempi tipici e gli errori più frequenti.
La selezione: if ed else
if (temperatura > 85) {
velocita_ventola = 100;
printf("Allarme: temperatura alta\n");
} else if (temperatura > 60) {
velocita_ventola = 70;
} else {
velocita_ventola = 30;
}
La condizione è un’espressione: se vale zero è falsa, altrimenti è vera. Le graffe si possono omettere se il ramo contiene una sola istruzione, ma è un’abitudine pericolosa: aggiungendo una seconda riga indentata sembra far parte del ramo e invece viene eseguita sempre. Nel 2014 un errore di questo tipo (una riga goto fail; duplicata) disattivò la verifica dei certificati SSL nei sistemi Apple.
L’operatore condizionale. Per scegliere un valore in modo compatto: massimo = (a > b) ? a : b;.
La selezione multipla: switch
Quando si sceglie in base al valore intero di un’espressione tra molti casi:
switch (comando) {
case 'A':
accendi_led();
break;
case 'S':
spegni_led();
break;
case 'L':
case 'l': /* due casi con lo stesso codice */
lampeggia_led();
break;
default:
printf("Comando sconosciuto: %c\n", comando);
}
Il break esce dallo switch. Se manca, l’esecuzione “cade” nel caso successivo (fall-through): a volte è voluto, come per ‘L’ e ‘l’, più spesso è un errore. Lo switch è la struttura naturale per le macchine a stati, usatissime nel firmware: un protocollo di comunicazione, il menu di un dispositivo, la sequenza di avvio di un motore.
Il ciclo while
Ripete un blocco finché la condizione è vera; la condizione si controlla prima di ogni iterazione, quindi il blocco può non essere eseguito mai.
/* quante cifre ha un numero intero positivo */
int n = 40527, cifre = 0;
while (n > 0) {
n /= 10;
cifre++;
}
/* cifre vale 5 */
Il ciclo do-while
Come il while, ma la condizione si controlla dopo: il blocco viene eseguito almeno una volta. È adatto quando si deve fare qualcosa prima di poter decidere se ripeterlo, come chiedere un dato finché non è valido:
int scelta;
do {
printf("Scegli un valore tra 1 e 5: ");
scanf("%d", &scelta);
} while (scelta < 1 || scelta > 5);
Il ciclo for
È la forma compatta per i cicli con un contatore: inizializzazione, condizione e aggiornamento stanno su una riga.
/* media di n campioni */
double somma = 0.0;
for (int i = 0; i < n; i++) {
somma += campioni[i];
}
double media = somma / n;
È equivalente a un while con l’inizializzazione prima e l’aggiornamento in fondo al blocco. In C gli indici partono da 0: un array di n elementi va da 0 a n - 1, quindi la condizione è i < n, non i <= n. Sbagliare di uno (errore “off by one”) è il bug più comune in assoluto.
Cicli annidati. Per esempio la tabellina:
for (int r = 1; r <= 10; r++) {
for (int c = 1; c <= 10; c++) {
printf("%4d", r * c);
}
printf("\n");
}
Il blocco interno viene eseguito
volte. Con due cicli annidati su
elementi il costo è
: è il motivo per cui l’ordinamento a bolle è lento.
break e continue
breakesce subito dal ciclo più interno;continuesalta il resto dell’iterazione corrente e passa alla successiva.
/* cerca il primo campione fuori soglia, ignorando quelli non validi (negativi) */
int trovato = -1;
for (int i = 0; i < n; i++) {
if (campioni[i] < 0) continue;
if (campioni[i] > soglia) { trovato = i; break; }
}
Il ciclo infinito dei microcontrollori
Un programma per un microcontrollore non finisce mai: dopo l’inizializzazione entra in un ciclo infinito che legge gli ingressi, aggiorna lo stato e comanda le uscite.
int main(void)
{
inizializza_periferiche();
for (;;) { /* oppure while (1) */
leggi_sensori();
aggiorna_stato();
aggiorna_uscite();
}
}
È la struttura che Arduino nasconde dietro le funzioni setup() e loop().
Errori frequenti
- Il punto e virgola dopo la condizione:
while (i < n);è un ciclo vuoto, infinito seinon cambia. - Confrontare numeri reali con
==: dopo calcoli in virgola mobile0.1 * 3 == 0.3è falso. Si usa una tolleranza. - Modificare il contatore di un
fordentro il ciclo: funziona, ma rende il codice difficile da capire. - Dimenticare di aggiornare la variabile della condizione in un
while: ciclo infinito.



