Ripetizione
La vera potenza dei programmi per computer risiede nella capacità di
ripetere lo stesso calcolo o sequenza di istruzioni più volte, ogni volta
usando dati diversi, senza la necessità di fare ripartire il programma
per ogni nuovo insieme di dati.
Le istruzioni di C che rendono ciò possibile sono:
while
for
do-while.
L’istruzione while realizza il diagramma di flusso già visto:
La sua forma generale è:
while (condizione) istruzione;
La condizione racchiusa tra parentesi è valutata esattamente
come quella contenuta in un’istruzione if-else, ma è usata in
modo differente.
Come abbiamo visto, quando la condizione è vera (ha un valore
diverso da zero) in un’istruzione if-else, l’istruzione che la
segue viene eseguita una volta.
In un’istruzione while l’istruzione che segue la condizione
viene eseguita ripetutamente fintanto che la condizione viene
valutata a un valore diverso da zero.
Naturalmente ciò significa che in qualche punto dell’istruzione
while ci debba essere un’istruzione che modifichi il valore della
condizione valutata.
Il procedimento usato dal computer per valutare un’istruzione
while è il seguente:
1. valuta la condizione
2. if la condizione ha un valore diverso da zero (vero)
a. esegue l’istruzione che segue la parentesi
b. torna al punto 1.
else
esce dall’istruzione while
Si noti che il passo 2.b. forza a trasferire il controllo del programma
indietro al passo 1.
Tale trasferimento del controllo all’inizio dell’istruzione while al
fine di ricalcolare la condizione è chiamato ciclo o loop di
programma.
L’istruzione while letteralmente torna indietro su se stessa per
ricontrollare la condizione fino a che la valuta a zero (diventa
falsa).
Il fatto che un’istruzione while fornisca la ripetizione di una
singola istruzione non impedisce di aggiungere un’ulteriore
istruzione che cambi il valore di una variabile che controlla il ciclo.
Ciò si ottiene sostituendo l’istruzione singola con una composta, per
esempio:
{
printf(“%d “, cont);
++cont;
}
Esercizio. Scrivere un programma che, utilizzando l’istruzione
while, produca la seguente uscita:
1 2 3 4 5 6 7 8 9 10
Ecco una possibile risposta:
#include <stdio.h>
void main(void)
{
int cont;
cont = 1;
/* inizializza cont */
while (cont <= 10)
{
printf(“%d “, cont);
++cont;
/* incrementa cont */
}
}
Osservazione. In linea di principio l’istruzione ++cont può essere
sostituita con una qualsiasi che cambi il valore di cont: per
esempio con un’istruzione del tipo cont = cont + 2, che
farebbe stampare un intero ogni due.
Tuttavia ci si deve accertare che cont venga cambiata in modo tale
da consentire un’uscita normale da while.
Ad es., se sostituissimo l’espressione ++cont con --cont, il
valore di cont non raggiungerebbe mai 11 e si creerebbe un loop
infinito.
Esercizio. Utilizzando l’istruzione
while, scrivere un programma
che stampi la seguente tabella:
Ecco una possibile risposta:
#include <stdio.h>
void main(void)
{
int num;
printf(“NUMERO QUADRATO CUBO\n”);
printf(“------ -------- ----\n”);
num = 1;
while (num < 6)
{
printf(“%3d
%3d
%4d\n”,num,num*num,num*num*num);
++num;
}
}
Si noti che l’espressione num < 6, essendo di tipo intero, è del tutto
equivalente all’espressione num <= 5.
Esercizio. Scrivere un programma che converta i gradi Celsius in
gradi Fahrenheit applicando la solita formula F = 9/5*C + 32,
stampando la seguente tabella:
Ecco una possibile risposta:
#include <stdio.h>
void main(void)
{
int celsius;
float fahren;
printf(“ GRADI
GRADI\n”);
printf(“CELSIUS FAHRENHEIT\n”);
printf(“------- ----------\n”);
celsius = 5;
while (celsius <= 30)
{
fahren = (9.0/5.0) * celsius + 32.0;
printf(“%5d%11.2f\n”, celsius, fahren);
celsius = celsius + 5;
}
}
scanf() all’interno di un ciclo while
La combinazione della funzione scanf() con le possibilità di
ripetizione dell’istruzione while produce programmi adattabili e utili.
Al riguardo, consideriamo il programma seguente, che usa un’istruzione
while per accettare e quindi visualizzare quattro numeri immessi
dall’utente, uno alla volta.
#include <stdio.h>
void main(void)
{
int cont;
float num;
printf(“Questo programma chiede di scrivere 4 numeri.\n”);
cont = 1;
while (cont <= 4)
{
printf(“\nScrivi un numero: “);
scanf(“%f”, &num);
printf(“Il numero scritto è %f”, num);
++cont;
}
}
Esso produce la seguente tabella:
Questo programma chiede di scrivere 4 numeri.
Scrivi un
Il numero
Scrivi un
Il numero
Scrivi un
Il numero
Scrivi un
Il numero
numero:
scritto
numero:
scritto
numero:
scritto
numero:
scritto
3
è
5
è
8
è
9
è
3.000000
5.000000
8.000000
9.000000
Esercizio. Anziché visualizzare semplicemente i numeri immessi,
modifichiamo il programma precedente in modo che utilizzi i
numeri immessi, sommandoli e stampandone la somma.
Ecco una possibile risposta:
#include <stdio.h>
void main(void)
{
int cont;
float num, somma;
printf(“Questo programma chiede 4 numeri e li somma.\n”);
cont = 1;
somma = 0;
while (cont <= 4)
{
printf(“Scrivi un numero: “);
scanf(“%f”, &num);
somma = somma + num;
++cont;
}
printf(“\nLa somma è %f”, somma);
}
Esso produce la seguente tabella:
Sentinelle. Come abbiamo già visto, quando si immettono dei valori
da tastiera, non è in genere noto a priori il loro numero. In tale
caso si fa continuare il ciclo fino a che non venga immesso un
valore speciale, detto sentinella, che di sicuro non si può
presentare tra i valori immessi.
Il programma seguente traduce l’algoritmo già visto che chiede di
scrivere dei numeri da tastiera, termina quando si scrive 0 e
fornisce il massimo dei numeri scritti.
#include <stdio.h>
void main(void)
{
int max, dato;
max = 0;
printf("Scrivi un numero (0 per finire)\n");
scanf("%d", &dato);
while (dato !=0)
{
if (dato > max)
max = dato;
scanf("%d", &dato);
}
printf("Il massimo è %d", max);
}
Esso produce la
seguente uscita:
Scrivi un numero (0 per finire)
4
9
2
6
0
Il massimo è 9
Anche ora si può modificare il programma in modo che esegua la
somma dei numeri immessi.
Il programma seguente chiede in continuazione dei valori fino a che si
immette un numero maggiore di 100, quindi ne stampa la somma.
#include <stdio.h>
void main(void)
{
float voto, somma;
voto = 0;
somma = 0;
printf(“\nPer terminare, scrivi un numero > 100\n”);
while (voto <= 100)
{
printf(“Scrivi un voto: “);
scanf(“%f”, &voto);
somma = somma + voto;
}
printf(“\nLa somma dei voti è %f”, somma-voto);
}
Si noti che il programma termina quando si immette un voto >100.
Dato però che anche tale voto-sentinella viene aggiunto alla somma,
la successiva istruzione di stampa, esterna al ciclo, lo sottrae dalla
somma che visualizza.
Un’utile sentinella fornita da C è la costante EOF (da End Of File), il
cui valore effettivo dipende dal particolare compilatore usato, ma
alla quale è sempre assegnato un codice diverso da quelli degli
altri caratteri.
Nei sistemi operativi UNIX e LINUX il codice di EOF si ottiene con
la pressione simultanea dei tasti Ctrl+D, in Windows con
Ctrl+Z, e può essere usato in tutti i programmi nei quali sia
incluso il file stdio.h.
Perciò il programma precedente si può riscrivere come segue:
#include <stdio.h>
void main(void)
{
float voto, somma = 0;
printf(“\nPer terminare, premi F6 o Ctrl+z\n\n”);
printf(“Scrivi un voto: “);
while (scanf(“%f”, &voto) != EOF)
{
somma = somma + voto;
printf(“Scrivi un voto: “);
}
printf(“\nLa somma dei voti e’ %f”, somma);
}
L’espressione scanf(“%f”, &voto) != EOF utilizza il fatto che la
funzione scanf() fornisce un valore EOF se si tenta di leggere un
contrassegno End Of File, e questo è generato dalla pressione
contemporanea dei tasti <Ctrl>+Z.
Un vantaggio di questo programma, rispetto al precedente, è che il
valore della sentinella non viene sommato al totale, cosicché non
deve essere successivamente sottratto.
Istruzioni break e continue. Due utili istruzioni che si possono
impiegare nelle istruzioni di ripetizione sono break e continue.
L’istruzione break (che abbiamo già visto nell’istruzione switch),
provoca l’uscita immediata dal tutti i cicli
switch, while, for e do-while.
Essa è utile per uscire da un ciclo al verificarsi di una condizione
inusuale.
while(cont <= 10)
Ad es., l’esecuzione del
{
seguente ciclo termina
printf(“Scrivi un numero”);
immediatamente se si
scanf(“%f”, &num);
immette un numero
maggiore di 76:
if (num > 76)
{
printf(“Hai perso!”);
break;
}
else
printf(“Continua pure!”);
}
L’istruzione continue è simile alla break, ma si applica solo ai
cicli creati dalle istruzioni
while, for e do-while.
Essa è utile per saltare dei dati che non vanno elaborati, rimanendo
all’interno di un ciclo.
Ad es., il seguente segmento di programma esegue la somma di 30
voti, ignorando quelli non validi (negativi o maggiori di 100) e
aggiungendo al totale solo quelli validi:
while (cont < 30)
{
printf(“Scrivi un voto: “);
scanf(“%f”, &voto);
if(voto < 0 || voto > 100) continue;
somma = somma + voto;
cont = cont + 1;
}
Istruzione null. Un punto e virgola senza alcunché che lo preceda è
un’istruzione valida, detta istruzione nulla.
Essa è usata quando la sintassi richieda un’istruzione, ma non si
debba compiere alcuna azione, tipicamente con le istruzioni while
o for.
Ad es., se in un programma C si inseriscono le istruzioni seguenti
float cont
. . . . . .
while (scanf(“%f”, &cont) != EOF)
;
il programma si pone in uno stato di attesa che termina quando si
premono i tasti <Ctrl>+Z o F6.
Questa tecnica permette di lanciare da Windows un programma in C,
e di vedere lo schermo del Dos (che altrimenti scomparirebbe
appena eseguito il programma) finché non si premano i tasti
<Ctrl>+Z o F6.
Istruzione for
L’istruzione for esegue gli
stessi compiti dell’istruzione
while, ma usa una forma
differente.
In molte situazioni, specie quelle
che usano una condizione di
conteggio fissa, il formato
dell’istruzione for è più
semplice da usare del suo
equivalente while.
Il suo diagramma di flusso è
quello già visto:
La forma sintattica dell’istruzione for è:
for (inizializzazione; condizione; variazione)
istruzione;
Nella parentesi vi sono tre voci, separate da punto e virgola, che
corrispondono a quelle dell’istruzione while; ciascuna di esse è
opzionale, ma i punti e virgola devono essere presenti.
La voce centrale della parentesi, condizione, è una qualsiasi
espressione valida in C, e viene usata nello stesso modo che
nell’istruzione while.
In entrambe le istruzioni, fin tanto che la condizione ha un valore
diverso da zero (vero) la istruzione che segue la parentesi
viene eseguita.
Ciò significa che prima del primo controllo della condizione si
devono assegnare i valori alle sue variabili che saranno testate.
Prima che la condizione sia valutata di nuovo, ci devono essere
una o più istruzioni che alterino questi valori.
Ricordiamo che la disposizione generale di queste istruzioni in un
ciclo while segue lo schema seguente:
istruzioni di inizializzazione;
while (condizione)
{
istruzioni del ciclo;
.
.
istruzioni che modificano l’espressione;
}
La necessità di inizializzare variabili o compiere qualche altra valutazione
prima di entrare in un ciclo ripetitivo è così comune che l’istruzione
for consente di raggruppare insieme tutte le istruzioni di
inizializzazione come primo gruppo di voci dentro le sue parentesi.
Le voci di questo elenco di inizializzazione sono eseguite una sola volta,
prima che la condizione sia valutata per la prima volta.
L’istruzione for fornisce anche un singolo posto per tutte le
istruzioni che modificano la condizione.
Esse vanno situate nell’ultima voce all’interno della parentesi for,
e sono eseguite alla fine del ciclo, subito prima che la
condizione sia valutata di nuovo.
La seguente sezione di codice confronta la corrispondenza tra le
istruzioni for e while.
Come si vede, l’unica differenza è la posizione delle espressioni
equivalenti.
Il raggruppamento di inizializzazione, condizione,
variazione nell’istruzione for è molto conveniente, specie nel
caso di cicli con numero fisso di ripetizioni.
Un altro confronto si può eseguire tra il programma che stampava i
primi 10 numeri interi e il seguente, che producono entrambi la
stessa uscita.
#include <stdio.h>
void main(void)
{
int cont;
for (cont = 1; cont <= 10; cont = cont + 1)
printf("%d ", cont);
}
Tuttavia, come accennato, dentro le parentesi non sono obbligatorie
né l’istruzione di inizializzazione né quella di variazione
dei valori nelle espressioni delle istruzioni, ma solo la condizione
e i due punti e virgola.
Quindi, il programma precedente potrebbe essere variato
inizializzando la variabile cont al di fuori delle parentesi:
o anche portando fuori dalle parentesi anche la variazione:
È possibile anche questa versione:
nella quale:
• tutte le voci sono racchiuse all’interno delle parentesi, cosicché non è
necessaria alcuna istruzione dopo la parentesi chiusa
(l’istruzione null soddisfa la richiesta sintattica che una istruzione
segua la parentesi del for)
• la variazione è costituita dalle ultime due voci in parentesi,
separate da una virgola (essa è necessaria sia nella variazione,
sia nella inizializzazione se esse sono costituite da più di una
voce).
scanf() all’interno di un ciclo for. Una chiamata alla funzione
scanf() all’interno di un ciclo for produce lo stesso effetto della
sua chiamata all’interno di un ciclo while.
Ad es., il programma seguente usa una chiamata a scanf() per
immettere 5 numeri, aggiungendo ciascuno di essi a un totale.
Quando si esce dal ciclo for, viene calcolata e visualizzata la media.
#include <stdio.h>
void main(void)
{
int cont;
float num, totale, media;
totale = 0.0;
for (cont = 0; cont < 5; ++cont)
{
printf("\nScrivi un numero: ");
scanf("%f", &num);
totale = totale + num;
}
media = totale / cont;
printf("\n\nLa media dei numeri immessi è %f", media);
}
Istruzione do
Entrambe le istruzioni while e for valutano una condizione
all’inizio del ciclo di ripetizione. Tuttavia vi sono dei casi in cui è più
conveniente controllare la condizione alla fine del ciclo.
Ad es., supponiamo di avere costruito il seguente ciclo while per
calcolare le imposte sulle vendite:
printf(“Scrivi un prezzo:”);
scanf(“%f”, &prezzo);
while (prezzo != SENTINEL)
{
impovendite = TASSO * prezzo;
printf(“L’imposta sulle vendite è E%5.2f”, impovendite);
printf(“\nScrivi un prezzo:”);
scanf(“%f”, &prezzo);
}
In questo caso l’uso dell’istruzione while richiede:
• o di duplicare il prompt e la chiamata alla funzione scanf() prima
del ciclo e quindi al suo interno, come abbiamo fatto,
• o di ricorrere a qualche altro artificio per forzare l’esecuzione iniziale
delle istruzioni dentro il ciclo.
L’istruzione do, come implica il suo nome, consente di eseguire alcune
istruzioni prima che una condizione sia valutata.
In molte situazioni ciò può essere usato per eliminare la duplicazione
illustrata nell’esempio precedente.
La forma generale dell’istruzione do è:
do
istruzione;
while (espressione);
Come sempre nel C, l’istruzione singola in do può essere
sostituita con una composta.
Il diagramma di flusso dell’istruzione do è quello già visto:
Pertanto, tutte le istruzioni del ciclo sono eseguite almeno una volta
prima che la condizione sia valutata; quindi, se essa risulta diversa
da zero, sono eseguite di nuovo. Il processo continua fino a che la
condizione non raggiunga il valore zero.
Come esempio consideriamo l’istruzione do seguente:
do
{
printf(“\nScrivi un prezzo: “);
scanf(“%f”, &prezzo);
impovendite = TASSO * prezzo;
printf(“L’imposta sulle vendite è €5.2f”,impovendite);
}
while (prezzo != SENTINEL);
Esercizio. Scrivere un programma che, usando scanf() all’interno
di un ciclo do-while, chieda di scrivere 5 numeri interi e li
sommi.
Ecco una possibile risposta:
#include <stdio.h>
void main(void)
{
int s, k, x;
s = k = x = 0;
do
{
printf("Scrivi il numero da sommare: ");
scanf("%d", &x);
++k;
s = s + x;
}
while (k!= 5);
printf("La somma è: %d", s);
}
Controlli di validità. L’istruzione do è particolarmente utile nel
filtrare i dati immessi, fornendo un controllo sulla loro validità.
Ad es., supponiamo che si debba immettere un numero di
identificazione compreso tra 1000 e 1999; un numero esterno a
questo intervallo deve essere respinto, e viene fatta una nuova
richiesta di un numero valido.
La seguente sezione di codice fornisce il filtro necessario per
verificare l’immissione di un numero d’identificazione valido.
do
{
printf(“\nScrivi un numero d’identificazione: “);
scanf(“%f”, &num_id);
}
while (num_id < 1000 || num_id > 1999);
Questo codice ripete la richiesta di un numero d’identificazione fino a
che viene scritto un numero valido, ma non informa l’operatore della
causa di una nuova richiesta di dato, né consente un’uscita
prematura dal ciclo se non si può trovare un numero
d’identificazione valido.
Una possibilità di eliminare il primo inconveniente è la seguente.
do
{
printf(“\nScrivi un numero d’identificazione: “);
scanf(“%f”, &num_id);
if (num_id < 1000 || num_id > 1999)
{
printf(“\nE’ stato scritto un numero sbagliato”);
printf(“\nControlla il numero ID e riscrivilo”);
}
else
break; /*prosegue se il num_id scritto è valido*/
}
while (1); /* questa espressione è sempre vera */
Qui si è usata un’istruzione break per uscire dal ciclo. Dato che
l’espressione valutata dall’istruzione do è sempre 1 (vera), si è
creato un loop infinito da cui si esce solo quando s’incontra
l’istruzione break.
Scarica

Fonda14