Guida di C (base)

Cicli

Siamo ormai quasi giunti alla fine di questo corso base sul linguaggio C, ma manca ancora un'importante "pezzo di programmazione" da analizzare: i cicli.

Un ciclo è una particolare struttura che permette di ripetere più volte un'istruzione, senza di fatto riscriverla uguale tante volte.

"Tocchiamo con mano" questo concetto con questo esempio:

#include <stdio.h>

main()
{
	printf("Ti saluto dieci volte!\n\n");
	printf("ciao\n");
	printf("ciao\n");
	printf("ciao\n");
	printf("ciao\n");
	printf("ciao\n");
	printf("ciao\n");
	printf("ciao\n");
	printf("ciao\n");
	printf("ciao\n");
	printf("ciao\n");
}

Intuitivamente il codice ha l'effetto di stampare dieci volte la parola 'ciao'. Ma il modo in cui ho risolto il problema non è per niente elegante; se in un futuro volessi modificare il programma in modo che stampi, ad esempio, dieci 'grazie', al posto di dieci 'ciao', dovrei riscrivere praticamente tutto il codice; per non parlare del caso in cui volessi scrivere "Mille grazie": ci perderei un pomeriggio!

Fortunatamente il C (ed i linguaggi di programmazione in generale) mettono a disposizione un modo più efficace per risolvere questo problema (e molti altri problemi un po' più interessanti):

int i;

printf("Ti saluto dieci volte:\n\n");

for (i=1; i<=10; i++) printf("ciao\n");

Ciclo "for"

Chi ha provato a compilare il codice qua sopra ha senz'altro verificato che i due programmi proposti sono equivalenti. Effettivamente il for ha l'effetto di eseguire 10 volte l'istruzione printf(), utilizzando la variabile i come contatore. Vediamo però di analizzare il funzionamento del costrutto utilizzato, ed in particolare di capire il significato delle tre funzioni di controllo del for, racchiuse tra le parentesi del for, separate dal punto e virgola:

  • i=1 è detta inizializzazione; questa istruzione viene eseguita una volta sola, ed è la prima ad essere eseguita.
  • i<=10 (i minore o uguale a 10) è la condizione. Il ciclo for eseguirà  l'istruzione printf("ciao\n") se e solo se la condizione è vera. Questo è banale per la prima volta (abbiamo visto che inizialmente i vale 1, che è minore di 10), ma per capire come si evolve l'esecuzione dobbiamo esaminare l'ultima condizione.
  • i++ è l'istruzione di aggiornamento. Questa istruzione sarà  eseguita al termine di ogni ciclo. In questo modo la variabile i è incrementata dopo ogni printf(). è evidente che prima o poi i assumerà  il valore 11, che renderà  falsa la condizione ed avrà  quindi l'efetto di fermare il ciclo.

Ricapitolando, facciamo un "viaggio" nel ciclo e simuliamone mentalmente l'esecuzione:

  • Per prima cosa alla variabile i viene assegnato il valore 1 (i=1;).
  • è quindi controllata la condizione i<=10: 1<=10 è una condizione vera quindi il ciclo prosegue.
  • Viene eseguita l'istruzione all'interno del ciclo: printf("ciao\n");
  • Viene incrementata la variabile contatore (i++): ora i vale 2.
  • è quindi controllata la condizione i<=10: 2<=10 è una condizione vera quindi il ciclo prosegue.
  • [...]
  • Si arriva al punto in cui i=10. La condizione 10<=10 è vera, quindi il ciclo prosegue.
  • Il ciclo stampa quello che sarà  l'ultimo 'ciao'.
  • Il contatore viene incrementato ad 11 (i++).
  • è quindi controllata la condizione i<=10: 11<=10 è una condizione falsa quindi il ciclo termina.

Vediamo infine un ultimo esempio, un po' più articolato (ma non troppo):

#include <stdio.h>

main()
{
	int i,n;

	printf("Inserisci un numero: ");
	scanf("%d",&n);

	printf("So contare fino a %d! ",n);

	for ( i=1 ; i&=n ; i++ ) printf("%d ",i);

	printf("\n");
}

Chi riesce a fare un programma che conti all'indietro, ovvero da n a 1?

Cicli "do...while" e "while"

Il for non è l'unico modo per produrre un ciclo in C. Ci sono altri due costrutti, molto simili tra loro, per ottenere risultati simili, il primo è il ciclo do...while:

int n;

do {
	scanf("%d",&n);
	printf("Hai inserito il numero %d!\n",n);
} while (n != 0);

Il suo funzionamento, più semplice del for, possiamo capirlo "ad occhio": il codice racchiuso tra le parentesi graffe è eseguito fintantochè la condizione rimane vera. Chi riesce a capire quando si ferma il ciclo dell'esempio?

La risposta è... Quando l'utente inserisce 0. Infatti ad ogni ciclo viene letto un numero, che viene stampato subito dopo. Se il numero è diverso da 0, la condizione n != 0 è vera, quindi il ciclo si ripete. Altrimenti, se l'utente inserisce proprio 0, la condizione è valutata come falsa (perchè, logicamente, 0!=0 è un'affermazione falsa), e quindi il ciclo termina.

Altro modo per risolvere il problema è utilizzare il ciclo while:

int n;

scanf("%d",&n);
while (n != 0) {
	printf("Hai inserito il numero %d!\n",n);
	scanf("%d",&n);
}

La differenza con il costrutto precedente è davvero sottile, e consiste nel fatto che la condizione è valutata prima delle istruzioni, anzichè dopo come avveniva nel caso precedente.

In questo caso l'uso di while ha l'effetto di non stampare l'ultimo numero inserito (lo zero), che non è male, visto che lo zero è stato utilizzato come carattere "di servizio".

Osserviamo però che è stato necessario ripetere la scanf() prima di iniziare il ciclo. Il while, infatti, per prima cosa (prima ancora di eseguire le istruzioni del ciclo), valutando l'espressione n!=0, confronta il valore contenuto in n; ma se non leggessi nessun valore prima dell'inizio del ciclo n rimarrebbe indefinita, e questo in C, come sappiamo, può dare risultati imprevedibili.

Istruzioni di controllo: "break" e "continue"

Il C mette a disposizione due istruzioni per modificare il flusso naturale dei cicli: break e coninue:

break, come si può intuire, interrompe il ciclo, bypassando di fatto la condizione di uscita:

char c;

while ( 1 ) {
	scanf("%c",&c);
	if ( c == 'b') break;
}

La condizione che governa questo ciclo (semplicemente 1) è una condizione sempre vera (1 è un numero intero, che in C significa vero): se non ci fosse break il ciclo continuerebbe all'infinito, generando quella situazione che in informatica è chiamata infinite loop!

continue, di contro, serve a ripetere da capo il ciclo, ignorando tutte le istruzioni successive:

char c;

while ( 1 ) {
	scanf("%c",&c);
	if ( c != 'b') continue;

	break;
}

Questo ciclo, benchè più articolato, si comporta esattamente come il precedente: ad ogni inserimento, se il carattere inserito non è una b, il ciclo ricomincia da capo, di volta in volta saltando il break finale.

Conclusioni

In conclusione...

  • Quando è necessario ripetere più volte un'istruzione, in programmazione si usa un ciclo.
  • Un ciclo è una struttura che ripete un'istruzione fintantochè una condizione è vera. Tutti i cicli terminano quando la condizione è falsa.
  • Il C mette a disposizione 3 cicli:
    • for( inizializzazione; condizione; aggiornamento) istruzione, utilizzato generalmente nelle situazioni che richiedono un conteggio, racchiude tra i parametri, oltre alla condizione, anche le istruzioni di inizializzazione e aggiornamento della variabile usata per il conteggio.
    • Il ciclo do istruzione while( condizione );, prosegue fintantochè la condizione non è falsa. Si osservi che, essendo la condizione posta alla fine, il ciclo do...while è sempre eseguito almeno una volta.
    • Il ciclo while ( condizione ) istruzione è identico al precedente, se non per il fatto che la condizione è posta all'inizio, e quindi può succedere che il ciclo non venga mai eseguito.
  • Le istruzioni break e continue possono essere usate per modificare in normale corso del ciclo, in particolare:
    • break interrompe il ciclo, a prescindere dalla relativa condizione.
    • continue ripete il ciclo, ignorando le eventuali istruzioni presenti dopo essa.
  1. Introduzione
  2. Preparare l'ambiente
  3. Il primo programma
  4. La funzione printf()
  5. Le variabili
  6. Input da tastiera
  7. Esecuzione condizionale: if
  8. Cicli
  9. Esempi
  10. Bibliografia