Istruzioni di salto in C++
Le istruzioni di salto interrompono il flusso di esecuzione.
C++ offre quattro istruzioni per il salto: break
, continue
e goto
, che trattiamo in questo capitolo, e l'istruzione return
, che descriveremo in seguito quando studieremo le funzioni
- Le istruzioni di salto modificano il flusso di esecuzione di un programma.
break
termina un ciclo o uno switch.continue
termina l'iterazione corrente di un ciclo.goto
fornisce un salto incondizionato.
L'Istruzione break
Un'istruzione break
termina la più vicina istruzione while
, do while
, for
o switch
in cui è contenuta. L'esecuzione riprende dall'istruzione immediatamente successiva all'istruzione terminata.
Un break
può apparire solo all'interno di un'istruzione iterativa o di un'istruzione switch
(incluse le istruzioni o i blocchi annidati all'interno di tali cicli). Un break
influisce solo sul ciclo o switch
circostante più vicino.
Ad esempio, consideriamo il codice seguente:
string buffer;
while (cin >> buffer && !buffer.empty()) {
switch(buffer[0]) {
case '-':
// processa fino al primo spazio
for (auto it = buffer.begin()+1; it != buffer.end(); ++it) {
if (*it == ' ')
break; // #1, esce dal ciclo for
// ...
}
// break #1 trasferisce il controllo qui
// elaborazione rimanente di '-':
break; // #2, esce dall'istruzione switch
case '+':
// ...
} // fine switch
// fine dello switch: break #2 trasferisce il controllo qui
} // fine while
Il break
etichettato come #1 termina il ciclo for
che segue l'etichetta case del trattino. Non termina l'istruzione switch
circostante e in effetti non termina nemmeno l'elaborazione per il case corrente. L'elaborazione continua con la prima istruzione che segue il for
, che potrebbe essere codice aggiuntivo per gestire un trattino o il break
che completa quella sezione.
Il break
etichettato come #2 termina lo switch
ma non termina il ciclo while
circostante. L'elaborazione continua dopo quel break
eseguendo la condizione nel while
.
Esercizio
-
Scrivi un programma per leggere una sequenza di stringhe dall'input standard fino a quando la stessa parola non si verifica due volte di seguito o tutte le parole sono state lette. Usa un ciclo
while
per leggere il testo una parola alla volta. Usa l'istruzionebreak
per terminare il ciclo se una parola si verifica due volte di seguito. Stampa la parola se si verifica due volte di seguito, altrimenti stampa un messaggio che dice che nessuna parola è stata ripetuta.Soluzione:
#include <iostream> #include <string> using namespace std; int main() { string previous, current; bool repeated = false; cout << "Inserisci parole (Ctrl+D per terminare):" << endl; while (cin >> current) { if (current == previous) { repeated = true; break; // Esce dal ciclo se la parola si ripete } previous = current; } if (repeated) { cout << "Parola ripetuta: " << current << endl; } else { cout << "Nessuna parola è stata ripetuta." << endl; } return 0; }
In questo programma, leggiamo parole dall'input standard fino a quando una parola non si ripete due volte di seguito. Se ciò accade, usiamo l'istruzione
break
per uscire dal ciclowhile
. Alla fine, stampiamo la parola ripetuta o un messaggio se nessuna parola è stata ripetuta.
L'Istruzione continue
Un'istruzione continue
termina l'iterazione corrente del ciclo circostante più vicino e inizia immediatamente l'iterazione successiva. Un continue
può apparire solo all'interno di un ciclo for
, while
o do while
, incluse le istruzioni o i blocchi annidati all'interno di tali cicli. Come l'istruzione break
, un continue
all'interno di un ciclo annidato influisce solo sul ciclo circostante più vicino. A differenza di un break
, un continue
può apparire all'interno di uno switch
solo se quello switch
è incorporato all'interno di un'istruzione iterativa.
Un continue
interrompe l'iterazione corrente; l'esecuzione rimane all'interno del ciclo. Nel caso di un while
o di un do while
, l'esecuzione continua valutando la condizione. In un ciclo for
tradizionale, l'esecuzione continua all'espressione all'interno dell'intestazione del for
. In un for
a intervallo, l'esecuzione continua inizializzando la variabile di controllo dal prossimo elemento nella sequenza.
Ad esempio, il seguente ciclo legge l'input standard una parola alla volta. Solo le parole che iniziano con un underscore verranno elaborate. Per qualsiasi altro valore, terminiamo l'iterazione corrente e otteniamo il prossimo input:
string buffer;
while (cin >> buffer && !buffer.empty()) {
if (buffer[0] != '_')
continue; // ottieni un altro input
// ancora qui? l'input inizia con un underscore; processa buffer ...
}
Esercizio
-
Rivedi il programma dall'esercizio nella sezione precedente in modo che cerchi solo parole duplicate che iniziano con una lettera maiuscola.
Soluzione:
#include <iostream> #include <string> using namespace std; int main() { string previous, current; bool repeated = false; cout << "Inserisci parole (Ctrl+D per terminare):" << endl; while (cin >> current) { if (!isupper(current[0])) { continue; // Salta parole che non iniziano con una lettera maiuscola } if (current == previous) { repeated = true; break; // Esce dal ciclo se la parola si ripete } previous = current; } if (repeated) { cout << "Parola ripetuta: " << current << endl; } else { cout << "Nessuna parola maiuscola è stata ripetuta." << endl; } return 0; }
In questo programma, usiamo l'istruzione
continue
per saltare qualsiasi parola che non inizia con una lettera maiuscola. Solo le parole che iniziano con una lettera maiuscola vengono confrontate per la ripetizione. Abbiamo comunque lasciato l'istruzionebreak
per uscire dal ciclo se una parola si ripete.
L'Istruzione goto
Un'istruzione goto
fornisce un salto incondizionato dal goto
a un'altra istruzione nella stessa funzione.
I programmi non dovrebbero usare i goto
. I goto
rendono i programmi difficili da capire e difficili da modificare.
La forma sintattica di un'istruzione goto
è
goto etichetta;
dove etichetta
è un identificatore che identifica un'istruzione. Un'istruzione etichettata è qualsiasi istruzione che è preceduta da un identificatore seguito da due punti:
// istruzione etichettata;
// può essere il target di un goto
fine: return;
Gli identificatori delle etichette sono indipendenti dai nomi usati per le variabili e altri identificatori. Quindi, un'etichetta può avere lo stesso identificatore di un'altra entità nel programma senza interferire con gli altri usi di quell'identificatore. Il goto
e l'istruzione etichettata a cui trasferisce il controllo devono essere nella stessa funzione.
Come con un'istruzione switch
, un goto
non può trasferire il controllo da un punto dove una variabile inizializzata è fuori scope a un punto dove quella variabile è in scope:
// ...
goto fine;
int ix = 10; // errore: goto bypassa una definizione di variabile inizializzata
fine:
// errore: il codice qui potrebbe usare ix ma il goto ha bypassato la sua dichiarazione
ix = 42;
Un salto all'indietro oltre una definizione già eseguita è accettabile. Saltare indietro a un punto prima che una variabile venga definita distrugge la variabile e la costruisce nuovamente:
// salto all'indietro oltre una definizione di variabile inizializzata è ok
inizio:
int dim = ottieni_dimensione();
if (dim <= 0) {
goto inizio;
}
Qui dim
viene distrutta quando il goto
viene eseguito. Viene definita e inizializzata nuovamente quando il controllo passa di nuovo attraverso la sua definizione dopo il salto indietro a inizio
.
Esercizio
-
L'ultimo esempio in questa sezione che è saltato indietro a
inizio
potrebbe essere scritto meglio usando un ciclo. Riscrivi il codice per eliminare ilgoto
.Soluzione:
int dim; do { dim = ottieni_dimensione(); } while (dim <= 0);
In questo esempio, abbiamo sostituito il
goto
con un ciclodo while
. Il ciclo continua a chiamareottieni_dimensione()
finchédim
non è maggiore di 0, eliminando la necessità delgoto
e rendendo il codice più chiaro e strutturato.