CodeGym /Corsi /C++ SELF /do-while: almeno una volta

do-while: almeno una volta

C++ SELF
Livello 4, Lezione 3
Disponibile

1. Introduzione al ciclo do-while

A volte in un programma ci sono azioni da eseguire almeno una volta, anche se la «condizione di continuazione» inizialmente potrebbe risultare falsa. Nel codice ciò si verifica quando dobbiamo mostrare almeno una volta un menu, chiedere almeno una volta all'utente di inserire un valore, o eseguire almeno una volta il corpo del ciclo per ottenere dati da verificare.

Immaginate un tipico problema: «chiedere all'utente un numero e ripetere la richiesta finché non inserisce un numero maggiore di zero». Per verificare “maggiore di zero” bisogna prima leggere quel numero. Quindi è vitale avere almeno una passata: senza quella non abbiamo valore da controllare, e il do-while rende questo scenario il più diretto possibile.

Sintassi di do-while e il punto e virgola obbligatorio ;

La sintassi del do-while è semplice ma con un carattere particolare: richiede il punto e virgola alla fine. È uno dei rari casi in C++ in cui il compilatore sembra dire: «so che sembra strano, ma deve essere così». È importante memorizzare il modello completo, invece di ricomporlo a pezzi durante l'esame del compilatore. La forma è: prima do, poi il blocco, poi while (condizione) e il punto e virgola obbligatorio ;.

Ecco un esempio minimo «eseguito una volta e termina», perché la condizione è falsa:

#include <iostream>

int main() {
    int x = 0;

    do {
        std::cout << "Hello!\n"; // Hello!
    } while (x > 0);
}

Pensiero chiave: la condizione viene verificata dopo il corpo. Perciò il corpo è garantito che venga eseguito almeno una volta. E sì, il punto e virgola dopo while (...) è obbligatorio — senza di esso ci sarà un errore di compilazione.

while e do-while: una differenza — effetti diversi

La differenza tra while e do-while sembra piccola, ma le conseguenze possono essere molto evidenti. In while controlliamo prima la condizione e poi eseguiamo il corpo. In do-while eseguiamo prima il corpo e poi verifichiamo la condizione. Questo influisce su se il corpo verrà eseguito almeno una volta, ed è spesso proprio quello che ci manca.

Confrontiamo con lo stesso valore x = 0:

#include <iostream>

int main() {
    int x = 0;

    while (x > 0) {
        std::cout << "while\n";
    }

    do {
        std::cout << "do-while\n"; // do-while
    } while (x > 0);
}

Se lo leggete come una storia, while dice: «faccio finché è vero». E do-while dice: «faccio, poi decido se ripetere». La seconda opzione è logica quando l'azione iniziale è necessaria di per sé: mostrare un menu, richiedere input, mostrare un suggerimento, eseguire un “tentativo iniziale”.

2. Quando il do-while è comodo nel codice reale

Input e menu senza acrobazie inutili

In pratica il do-while si trova più spesso in due scenari. Il primo — “input con ripetizione”: chiedere un valore all'utente, verificare se va bene e, in caso contrario, richiederlo di nuovo. Il secondo — “menu da console”: mostrare la lista di comandi, far scegliere un comando, eseguire l'azione e ridisplayare il menu finché non si sceglie di uscire.

Notate: entrambe le situazioni hanno un senso comune — senza la prima passata nulla funziona. Finché non mostri il menu non c'è niente da scegliere. Finché non leggi il valore non c'è niente da verificare. È come cercare di giudicare il sapore di una zuppa senza assaggiarla (anche se alcuni lo fanno lo stesso, ma quella è un'altra disciplina).

Esempio: chiediamo un numero > 0

Iniziamo con il semplice: leggiamo n e non molliamo finché l'utente non inserisce n > 0. Questo è il caso ideale per do-while, perché dobbiamo leggere almeno una volta.

#include <iostream>

int main() {
    int n = 0;

    do {
        std::cout << "Enter n (> 0): ";
        std::cin >> n;
    } while (n <= 0);

    std::cout << "n = " << n << '\n'; // ad esempio: n = 5
}

Notate: la condizione alla fine si legge quasi come testo: «ripetere finché n <= 0». È uno stile buono per il do-while: formulare la condizione come condizione di continuazione, non di uscita. Così ci si confonde di meno con il ! e con i significati logici.

3. Esempio: menu da console “salvadanaio”

Cominciamo a costruire una piccola applicazione da console che si svilupperà durante le lezioni sui cicli. Oggi faremo la base: un “salvadanaio” (budget semplificato), dove si possono aggiungere soldi, vedere il bilancio, azzerare il bilancio e uscire. Niente array, funzioni o strutture complesse — per ora tutto con int, if/else e cicli.

Scheletro del menu con do-while

Prima creiamo lo scheletro del menu con do-while. È comodo: il menu deve apparire almeno una volta.

#include <iostream>

int main() {
    int balance = 0;
    int cmd = -1;

    do {
        std::cout << "1 - Add\n2 - Show\n3 - Reset\n0 - Exit\n";
        std::cin >> cmd;

        if (cmd == 2) {
            std::cout << "Balance = " << balance << '\n'; // Balance = 0
        }
    } while (cmd != 0);
}

Non è ancora “l'app dei sogni”, ma si vede l'essenziale: il do-while rende la struttura «mostra menu → leggi comando → azione → ripeti» molto diretta. Con while si può fare lo stesso, ma spesso bisogna duplicare la stampa del menu o preparare passaggi preliminari prima del ciclo.

Aggiunta e azzeramento del bilancio

Ora aggiungiamo la gestione dei comandi 1 e 3. Restiamo nell'ambito già studiato: solo if/else e aritmetica di base. È importante non trasformare il corpo del ciclo in un "pasticcio" dove è impossibile capire cosa succede. Meglio mantenere la logica lineare: leggi il comando → scegli l'azione → esegui.

#include <iostream>

int main() {
    int balance = 0;
    int cmd = -1;

    do {
        std::cout << "1 - Add\n2 - Show\n3 - Reset\n0 - Exit\n";
        std::cin >> cmd;

        if (cmd == 1) {
            int add = 0;
            std::cout << "Add amount: ";
            std::cin >> add;
            balance = balance + add;
        } else if (cmd == 2) {
            std::cout << "Balance = " << balance << '\n';
        } else if (cmd == 3) {
            balance = 0;
            std::cout << "Balance reset.\n"; // Balance reset.
        }
    } while (cmd != 0);
}

Notate un punto importante: la variabile add vive solo dentro il ramo dove cmd == 1. Questo è normale e utile: meno possibilità di usarla inavvertitamente più avanti quando non serve più.

Gestione dei comandi sconosciuti

Se l'utente inserisce 42, ora il programma lo ignora e rimostra il menu. Formalmente non è un errore, ma l'UX diventa come una macchinetta del caffè che ti ignora e fa finta che nulla sia successo (eppure vuole i soldi). Aggiungiamo una gestione gentile dei comandi sconosciuti.

#include <iostream>

int main() {
    int balance = 0;
    int cmd = -1;

    do {
        std::cout << "1 - Add\n2 - Show\n3 - Reset\n0 - Exit\n";
        std::cin >> cmd;

        if (cmd == 1) {
            int add = 0;
            std::cout << "Add amount: ";
            std::cin >> add;
            balance = balance + add;
        } else if (cmd == 2) {
            std::cout << "Balance = " << balance << '\n';
        } else if (cmd == 3) {
            balance = 0;
            std::cout << "Balance reset.\n";
        } else if (cmd != 0) {
            std::cout << "Unknown command.\n"; // Unknown command.
        }
    } while (cmd != 0);
}

Notate quanto è elegante la condizione else if (cmd != 0): non segnaliamo l'uscita come errore perché è la scelta corretta. È un dettaglio, ma sono questi dettagli che danno «sensazione di programma vivo».

4. Ordine di esecuzione e trappole frequenti

Schema di esecuzione del do-while

Quando i principianti si confondono con i cicli, spesso il problema non è la sintassi, ma l'ordine di esecuzione. Per questo è utile vedere una volta il diagramma a blocchi. Leggetelo così: prima si esegue il corpo, poi si verifica la condizione, e si decide se ripetere o uscire.

flowchart TD
    A[Ingresso in do-while] --> B[Eseguire il corpo del ciclo]
    B --> C{Condizione vera?}
    C -->|sì| B
    C -->|no| D[Uscita dal ciclo]

Se tenete in mente questo schema, spariranno molte domande del tipo «perché è stato eseguito una volta se la condizione è falsa». Per il do-while non è un bug, è il comportamento voluto.

Il ciclo deve avvicinarsi alla terminazione

Il do-while non annulla la regola principale di tutti i cicli: deve esistere una ragione per cui il ciclo prima o poi finirà. A volte la ragione è la modifica di una variabile, a volte l'input dell'utente, a volte la diminuzione di un conteggio come visto in altri esempi. Ma ci deve essere una ragione.

Nel nostro menu la ragione della terminazione è semplice: l'utente inserisce 0, e la condizione cmd != 0 diventa falsa. Se ci dimentichiamo di leggere cmd dentro il corpo (o sovrascriviamo cmd per errore), potremmo ottenere un ciclo infinito. I cicli infiniti non sono per forza “cattivi”, ma qui ci concentriamo sui cicli che terminano correttamente, quindi considerate questo come una regola: «ogni iterazione deve avvicinarci all'uscita».

Punto e virgola e corpo vuoto

Il do-while ha due “trappole classiche” che capitano così spesso da essere folklore tra i programmatori. La prima — dimenticare il ; dopo while (...). La seconda — mettere per sbaglio un ; dopo do { ... } while (...), che trasforma il ciclo in una costrutto strano dove sembra che qualcosa avvenga dentro, ma in realtà no.

Vale la pena memorizzare il modello visivamente:

do {
    // corpo
} while (condizione);
Modello del ciclo do-while con il punto e virgola obbligatorio ;

Se una volta iniziate a vedere } while (...) ; come una singola “unità”, gli errori diminuiranno.

5. Errori tipici lavorando con do-while

Errore n.1: dimenticare il punto e virgola dopo while (condizione).
Questo errore è fastidioso perché la mente pensa: «ma è quasi come while!», mentre il compilatore risponde: «no». Si risolve facilmente: imparate il do-while come modello pronto do { ... } while (...); e nei progetti didattici usate sempre le graffe, anche per una singola riga.

Errore n.2: confondere condizione di continuazione e condizione di uscita.
Spesso si scrive la condizione «di uscita» e la si mette in while (...), ottenendo un ciclo che funziona al contrario. Abitudine utile — leggere la condizione ad alta voce come continuazione: «ripetere finché …». Se la frase suona strana, probabilmente avete scritto la condizione di uscita invece che quella di continuazione.

Errore n.3: il corpo non cambia la situazione e il ciclo diventa infinito.
Nel menu ciò accade se dimenticate lo std::cin >> cmd;, o leggete in una variabile diversa, o assegnate sempre lo stesso valore a cmd. Nei cicli di input succede se la variabile della condizione non viene mai modificata. Prima di eseguire, chiedetevi: «Cosa cambia in una iterazione? Perché questo ci avvicina alla fine?».

Errore n.4: troppa logica in un solo punto e il ciclo diventa illeggibile.
Il do-while è spesso usato per i menu, e i menu possono trasformarsi in un “combine”: dieci comandi, venti if, e si teme aprire quel file. Anche senza funzioni (per ora non le vediamo) si può mantenere disciplina: il corpo faccia sempre le stesse cose nello stesso ordine — mostra suggerimento → leggi comando → esegui un ramo → ritorna all'inizio.

Errore n.5: aver creato per sbaglio un ciclo vuoto a causa di un punto e virgola in più.
A volte si mette do { ... }; while (cond); o altre combinazioni dopo le quali il programma compila ma si comporta in modo «strano». Quando il comportamento sembra magico, quasi sempre la punteggiatura è colpevole: ; e le graffe {}. In C++ non sono decorazioni, ma elementi di controllo del linguaggio — trattateli come fili in un quadro elettrico: un filo in più può non solo disturbare, può cortocircuitare la logica.

1
Compito
C++ SELF, livello 4, lezione 3
Bloccato
Il primo segnale "positivo"
Il primo segnale "positivo"
1
Compito
C++ SELF, livello 4, lezione 3
Bloccato
Password sul terminale
Password sul terminale
1
Compito
C++ SELF, livello 4, lezione 3
Bloccato
Clicker da console
Clicker da console
1
Compito
C++ SELF, livello 4, lezione 3
Bloccato
Minimo e massimo fino al segnale di stop
Minimo e massimo fino al segnale di stop
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION