Vai al contenuto
Fondamenti di Informatica

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 10\times 10 = 100 volte. Con due cicli annidati su n elementi il costo è O(n^2): è il motivo per cui l’ordinamento a bolle è lento.

break e continue

  • break esce subito dal ciclo più interno;
  • continue salta 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 se i non cambia.
  • Confrontare numeri reali con ==: dopo calcoli in virgola mobile 0.1 * 3 == 0.3 è falso. Si usa una tolleranza.
  • Modificare il contatore di un for dentro il ciclo: funziona, ma rende il codice difficile da capire.
  • Dimenticare di aggiornare la variabile della condizione in un while: ciclo infinito.
Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted
0
Would love your thoughts, please comment.x
()
x