Il ciclo for
è uno dei costrutti iterativi del linguaggio C++ per il controllo di flusso di un programma.
Sintassi del ciclo for in C++
Mediante esso, è infatti possibile eseguire il medesimo blocco di istruzioni per un certo numero di ripetizioni. La sintassi generale è la seguente:
for (<inizializzazione>; <condizione>; <espressione_iterativa>)
{
// istruzioni
}
Le espressioni di inizializzazione, condizione ed iterazione devono essere separate da ;
.
La semantica più comune del ciclo for
è la seguente: si dichiara una variabile contatore che di fatto è identificativa dell'iterazione corrente, e le si assegna un valore iniziale. Questa operazione, detta di inizializzazione del ciclo, viene eseguita solamente una volta, prima di effettuare la prima iterazione. Si procede quindi alla valutazione di una espressione booleana (o il cui risultato è convertibile ad un valore booleano) e in caso di esito positivo si esegue il corpo di istruzioni associato al ciclo.
Al termine di esso, viene eseguita un'altra istruzione, il cui scopo è tipicamente quello di assegnare un nuovo valore alla variable contatore, prima di passare alla prossima iterazione.
Segue quindi nuovamente la valutazione della condizione il cui esito determina l'esecuzione della prossima iterazione o la terminazione immediata del ciclo con restituzione del controllo di flusso alla istruzione immediatamente successiva.
Da un punto di vista sintattico, esso è quindi costituito dalle quattro strutture seguenti:
- inizializzazione: una qualsiasi istruzione sintatticamente valida, tipicamente la dichiarazione di una o più variabili che fungeranno da contatore per le iterazioni del ciclo.
- condizione: un'espressione il cui risultato sia convertibile al tipo booleano. Rientra in questa categoria anche l'inizializzazione di una variabile, ad esempio mediante l'operatore di assegnamento
=
, il cui tipo sia convertibile a booleano. Tipicamente questa espressione include la valutazione della variabile contatore, ad esempio per determinarne la soglia massima o minima. - espressione iterativa: una qualsiasi istruzione sintatticamente valida, tipicamente una che assegni un nuovo valore alla variabile contatore, o che in qualche modo influenzi la valutazione della condizione di iterazione del ciclo.
- istruzioni: una sequenza di istruzioni qualsiasi. Si noti che le parentesi graffe del ciclo determinano un constesto di visibilità separato dall'esterno, di cui fanno parte anche le variabili eventualmente definite in fase di inizializzazione. La definizione di una variable con lo stesso nome di una variabile contatore all'interno del blocco di istruzioni del ciclo determinerà quindi un errore in fase di compilazione. Inoltre, le parentesi graffe sono opzionali nel caso in cui il blocco sia costituito da un'unica espressione.
Semantica del ciclo for
Nel frammento seguente si mostra un'applicazione semanticamente conforme alla definizione data per queste strutture sintattiche, in cui viene calcolata la somma dei primi 100 numeri pari:
int acc = 0;
for (int i = 0; i < 100; i += 2)
{
acc += i;
}
La variabile i è inizializzata al valore 0, viene usata nella determinazione della condizione di uscita mediante il confronto con un valore limite (100), e influsce sull'occorrenza della prossima iterazione mediante un'operazione di incremento.
Tuttavia bisogna considerare il fatto che le espressioni di inizializzazione, condizione ed iterazione possono anche non essere vincolate all'uso di variabili contatore.
Il ciclo for
può infatti essere espresso in una forma sintatticamente valida, con una semantica totalmente differente da quella qui descritta. In effetti, le istruzioni di inizializzazione, condizione ed iterazione possono essere costituite da una qualunque istruzione valida, compresa l'istruzione nulla ;
. Il frammento seguente mostra ad esempio un ciclo for
infinito:
for (;;)
{
// istruzioni
}
Un altro esempio è illustrato nel listato seguente, in cui si fa un uso discutibilmente creativo del ciclo for
per implementare un lanciatore perpetuo di dadi:
#include <iostream>
#include <cstdlib>
int main()
{
int d1 = 1;
int d2 = 1;
for (std::cout << "Press Ctrl+C to kill me!" << std::endl;
d1 = rand() % 6 + 1, d2 = rand() % 6 + 1;
std::cout << d1 << " " << d2 << std::endl);
return 0;
}
Le espressioni usate per l'inizializzazione e l'iterazione, seppure valide dal punto di vista sintattico, non hanno niente a che vedere con la semantica dei contatori. Esse in questo caso producono degli effetti collaterali, come l'output a schermo. L'espressione usata per esprimere la condizione di iterazione o uscita è composta da due istruzioni separate da virgola per l'assegnamento di un valore casuale compreso tra 1 e 6 alle variabili d1 e d2. In questo caso il risultato globale è dato dall'istruzione eseguita per ultima, cioè l'assegnamento di d2. Poichè però 0 non è tra i valori possibili, il ciclo viene reiterato all'infinito.
Infine il blocco di istruzioni associate al ciclo è costituito dall'istruzione nulla, cioè composta dal solo ;
.
Siamo quindi in presenza di un uso molto diverso da quello consueto, che seppure sintatticamente corretto, travalica gli intenti propri della semantica del costrutto, ed è quindi sconsigliabile.
Cicli annidati
I costrutti iterativi come il ciclo for
possono essere combinati per definire strutture di controllo più complesse mediante annidamento. Ad esempio, nel frammento seguente, ad ogni itarazione del ciclo esterno corrispondono 10 iterazioni del ciclo annidato.
for (int i = 2; i < 10; i++)
{
std::cout << "Tabellina del " << i << std::endl;
for (int j = 1; j <= 10; j++)
{
std::cout << i << " * " << j << " = " << i*j << std::endl;
}
}
Un caso tipico, che prenderemo in esame nel seguito, è l'uso di cicli annidati per la scansione ordinata degli elementi di variabili multidimensionali (vettori e matrici) ad esempio per righe o per colonne.