Sei sulla pagina 1di 54

Strutture iterative

Ver. 2.4.1

© 2010 - Claudio Fornaro - Corso di programmazione in C


2

Strutture iterative
 Problema:
Visualizzare i numeri interi da 0 a 1000

Soluzione
printf("0\n");
printf("1\n");
printf("2\n");
printf("3\n");
printf("4\n");
...
Non è davvero una buona idea… ma con le
conoscenze attuali non c’è alternativa
3

Strutture iterative
 Vorremmo scrivere:
“Esegui l’istruzione:
printf("%d\n", i);
con i che assume i valori da 0 a 1000 ”
i=0

stampa i

i=i+1
percorso chiuso,
detto “anello”, V
“loop” o “ciclo” i<=1000
F
4

I cicli in C
 Per “tornare indietro” si potrebbe utilizzare
un’istruzione apposita, ma per questioni di
chiarezza si utilizzano strutture sintattiche che
fanno “tornare indietro” solo se la condizione
di ripetizione è vera
 Le strutture iterative sono comunemente dette
cicli o loop
 In C i cicli sono controllati da una condizione di
permanenza nel ciclo: fintantoché la
condizione è vera, si esegue il corpo del ciclo
(il blocco di codice da eseguire più volte)
5

Ciclo WHILE
 Fa eseguire un blocco di codice fintantoché
una certa condizione è vera
 Valuta la condizione prima di eseguire il
blocco

F
condizione
V
blocco

 Se la condizione è inizialmente falsa,


il blocco non viene eseguito neppure una volta
6

Ciclo WHILE
 while (condizione)  senza il ‘;’
blocco
 Viene valutata la condizione:
 se è vera
 esegue il blocco

 torna su a valutare nuovamente la condizione

 se è falsa
 passa ad eseguire le istruzioni successive al
blocco
 La condizione è un’espressione qualsiasi (come
quella del costrutto if) e deve sempre essere
presente (si usi 1 per avere un ciclo infinito)
7

Ciclo WHILE
 Esempio
Il seguente codice risolve il problema iniziale
di visualizzare i numeri interi da 0 a 1000
i=0;
while (i<=1000)
{
printf("%d ", i);
i++;
}
 Si noti che il valore di i terminato il ciclo è
1001
8

Ciclo WHILE
 Esempio 2
Somma dei valori introdotti finché non viene
immesso il valore 0
somma = 0;
scanf("%d", &v);
while (v != 0)
{
somma += v;
scanf("%d", &v);
}
printf("Somma: %d", somma);
9

Ciclo FOR
 Fa eseguire il blocco fintantoché la condizione
è vera (esattamente come il ciclo WHILE)
for (espr1;condizione;espr2)  senza il ‘;’
blocco
 Viene calcolata espr1 (soltanto la prima volta)
 Viene valutata la condizione:
 se è vera:
 esegue il blocco

 esegue expr2

 torna su a valutare nuovamente la condizione

 altrimenti (se è falsa):


 passa ad eseguire le istruzioni successive a blocco
10

Ciclo FOR
 La condizione può essere un’espressione
qualsiasi, se manca equivale a 1
 expr1 e/o expr2 possono mancare (ma i
separatori ‘;’ devono esserci ugualmente)
 Il flow-chart corrispondente è il seguente:
expr1
F
condizione
V
blocco
expr2
11

Ciclo FOR
 Esempio
Anche il seguente codice risolve il problema
iniziale di visualizzare i numeri interi da 0 a
1000
for (i=0; i<=1000; i++)
printf("%d ", i);
 Una variabile che, come i in questo esempio,
tiene conto del numero di iterazioni viene detta
variabile di conteggio o indice
 Notare che, nell’esempio, dopo che il ciclo è
stato eseguito completamente, i vale 1001
12

Ciclo FOR
 Esempi
La ripetizione “per N volte ” si può ottenere in
diversi modi, tra i quali i seguenti (si notino i
valori iniziali e le condizioni):
 for (i=0; i<N; i++)  metodo usuale
i va da 0 a N–1, dopo il ciclo i = N
 for (i=1; i<=N; i++)
i va da 1 a N, dopo il ciclo i = N+1
 for (i=N; i>0; i--)
i va da N a 1, dopo il ciclo i = 0
 for (i=N-1; i>=0; i--)
i va da N–1 a 0, dopo il ciclo i = –1
 for (i=0; i<=N; i++) Errore!!! (11 volte)
13

Ciclo FOR
 Il ciclo FOR è un ciclo WHILE riscritto in modo
tale da raggruppare tra le parentesi tutto ciò
che gestisce l’indice: inizializzazione (espr1),
controllo (condizione) e aggiornamento (espr2)
 for (espr1;condizione;espr2)
blocco
 Il ciclo FOR precedente equivale a:
expr1;  fuori dal corpo del ciclo!
while (condizione)
{
blocco
expr2;
}
14

Ciclo FOR
 Esempio
Questo ciclo WHILE:
i=0;
while (i<=1000)
{
printf("%d", i);
i++;
}
e questo ciclo FOR:
for (i=0; i<=1000; i++)
printf("%d", i);
sono equivalenti, ma il secondo è più compatto
15

Ciclo FOR
 Attenzione nella trasformazione di un ciclo FOR
in WHILE (o viceversa) quando si hanno
istruzioni continue:
sum=0; sum=0; i=0;
for (i=0;i<10;i++) while (i<10)
{ {
scanf("%d",&v); scanf("%d",&v);
if (i == 0) if (i == 0)
continue; continue;
sum += v; sum += v;
} i++;
}
per i=0 la continue
non fa incrementare i
16

La variabile di conteggio
 Talvolta è conveniente che il nome della
variabile di conteggio sia corto per questioni di
leggibilità del codice
 Esempio
for (i=0; scanf("%d",&v[i])!=EOF; i++)
tot += v[i]*v[i-1]*v[i+1];
totValoriLetti = i;
Qui i viene usata nel corpo del ciclo più volte,
quindi è conveniente usare questa come indice
e non la variabile totValoriLetti che
invece viene assegnata alla fine del ciclo
 La modifica della variabile di conteggio dentro
il ciclo for viene considerata pratica da evitare
in quanto può rendere il codice complesso
17

Scelta tra ciclo FOR e WHILE


 Quando il numero di iterazioni è noto a priori
(e quindi il ciclo è controllato da un indice ),
è preferibile (per chiarezza e stilisticamente)
utilizzare un ciclo FOR che raggruppa in un
punto solo l’inizializzazione, il controllo e
l’aggiornamento dell’indice
18

Ciclo DO-WHILE
 Fa eseguire un blocco di codice fintantoché
una certa condizione è vera
 Valuta la condizione dopo aver eseguito il
blocco

blocco
V
condizione
F

 Anche se la condizione è inizialmente falsa,


il blocco viene eseguito almeno una volta
19

Ciclo DO-WHILE
 Nella letteratura questo ciclo viene detto ciclo
Repeat-Until (dove però se la condizione è
vera si esce dal ciclo: non è di permanenza)
 do
{
blocco
}while (condizione);  con il ‘;’
 La condizione può essere un’espressione
qualsiasi che produce un valore
 Le graffe sono opzionali, ma consigliabili
(proprio con la graffa di chiusura subito prima
della keyword while) per distinguere
facilmente il ciclo WHILE dal ciclo DO-WHILE
20

Ciclo DO-WHILE
 Esempio
Il seguente codice risolve il problema iniziale
di visualizzare i numeri interi da 0 a 1000
i=0;
do
{
printf("%d ", i);
i++;
}while (i<=1000);
21

Ciclo DO-WHILE
 Esempio 2
Somma i valori dati finché non viene
introdotto il valore particolare 0
somma = 0;
do
{
scanf("%d", &v);
somma += v;
}while (v != 0);
Notare che il valore v viene comunque
addizionato a somma (ma in questo esempio
non causa problemi perché somma uno 0)
22

Scelta tra ciclo WHILE e DO


 Si può sempre passare da un tipo di ciclo ad
un’altro modificando (poco) il programma
 La scelta tra ciclo WHILE e ciclo DO-WHILE è
spesso ovvia e questione di preferenze
personali
23

Corpo di un ciclo
 Quando in un ciclo tutta l’elaborazione è già
contenuta nelle espressioni di controllo, il
corpo non contiene istruzioni
 Poiché il corpo deve comunque esistere, si usa
un’istruzione nulla , ossia il solo carattere ‘;’
 Non produce alcuna azione e per chiarezza è
bene sia collocato in una riga a sé stante
 Conta solo i valori introdotti (il contatore è i):
for (i=0; scanf("%d", &v)!=EOF; i++)
;
 In alternativa si possono usare una coppia di
parentesi graffe {} o un’istruzione continue
24

Programmazione strutturata
 Nasce dalla necessità di regolamentare e
standardizzare le metodologie di
programmazione
 Un linguaggio strutturato deve avere almeno i
seguenti 3 tipi di strutture:
 La sequenza: ossia la possibilità di definire un
blocco di istruzioni (le graffe in C, ma anche il
semplice elenco di istruzioni)
 L’ alternativa: costrutti di selezione (if e switch)
 L’ iterazione: costrutti per ripetere uno stesso
blocco di istruzioni (for, while, do-while)
25

Programmazione strutturata
 Le strutture di un linguaggio strutturato
devono avere le seguenti caratteristiche:
 ogni struttura (compresi i blocchi controllati) deve
avere un unico punto di ingresso e un unico punto
di uscita (così da non avere altre interazioni con
l’esterno e poter essere considerata come un’unica
macro-istruzione)
 ogni struttura può avere nel blocco controllato altre
strutture (di ogni tipo)
 Un programma è strutturato se usa solo le
strutture indicate nei modi indicati sopra
 Il linguaggio C è strutturato, ma permette
anche di scrivere codice non strutturato
26

Programmazione strutturata
 In Linguaggio C si ha programmazione non
strutturata quando si usano le istruzioni:
 goto
 break
 continue
 return/exit multipli
 Dette istruzioni possono talvolta produrre
vantaggi anche non marginali per chiarezza e
velocità di esecuzione
 Quando si richiede una programmazione
strutturata tutte le istruzioni precedenti non
sono permesse
27

Break
 Per uscire da un ciclo immediatamente, senza
aspettare la valutazione della condizione, si
può utilizzare l’istruz. non strutturata break
 Dopo il break, l’esecuzione continua dalla
prima riga successiva al blocco
 while (condizione)
{ istruzioni...
if (condizione_particolare)
break;
istruzioni...
}
 Il break può essere usato per gestire condizioni
particolari e infrequenti (non deve essere il
metodo normale di terminazione del ciclo)
28

Break
 Esempio
Fa la media di fino a 10 valori dati in input.
Per introdurre meno valori, introdurre 0
double v, somma = 0;
for (i=0; i<10; i++)
{
scanf("%lf", &v);
if (v == 0)
break;
somma += v;
}
printf("Media = %f\n", somma/i);
29

Break
 La formulazione equivalente strutturata è:
int esci = NO;
double v, somma = 0;
for (i=0; i<10 && esci==NO; i++)
{
scanf("%lf", &v);
if (v == 0)
esci=SI;
else
somma += v;
}
i--;  i viene incrementata comunque
printf("Somma = %f\n", somma/i);
30

Continue
 Per passare immediatamente all’iterazione
successiva, si può utilizzare l’istruzione
non strutturata continue
 Per effetto dell’istruzione continue:
 vengono saltate tutte le istruzioni dalla continue
fino alla parentesi di terminazione del corpo del ciclo
 se si tratta di un ciclo for, viene eseguita expr2
 l’esecuzione riprende dalla valutazione della
condizione
31

Continue
 Schema con ciclo while
while (condizione)
{
istruzioni...

if (condizione_particolare)
continue;

istruzioni saltate se eseguito continue

}
32

Continue
 Schema con ciclo while
for (espr1; condizione;espr2)
{
istruzioni...

if (condizione_particolare)
continue;

istruzioni saltate se eseguito continue

}
33

Continue
 Schema con ciclo do-while
do
{
istruzioni...

if (condizione_particolare)
continue;

istruzioni saltate se eseguito continue

}while (condizione);
34

Continue
 Esempio
Somma i valori dati finché non viene
introdotto il valore 0, ignorando i negativi.
somma = 0;
do
{
scanf("%d", &v);
if (v < 0)
continue;
somma += v;
}while(v != 0);
35

Continue
 La formulazione equivalente strutturata è in
questo caso più chiara:

somma = 0;
do
{
scanf("%d", &v);
if (v >= 0)
somma += v;
}while(v != 0);
36

Lettura di valori
 Quando non si può sapere a priori il numero
di valori che verranno introdotti dall’utente si
deve trovare un modo per stabilire la fine
dell’input:
 Si chiede all’utente quanti valori verranno introdotti
 Si prevede un valore particolare che quando
introdotto indica la fine dell’input, tale valore è
detto sentinella (es. lo 0 degli esempi precedenti)
 Si chiede all’utente di segnalare la fine dell’input
mediante l’immissione di un codice di controllo
detto End Of File (EOF) che viene riconosciuto e
segnalato dalle stesse funzioni di input (mentre la
sentinella viene riconosciuta dopo l’input)
37

Lettura di valori
 La costante EOF è un valore intero definito in
stdio.h (in genere vale –1)
 Viene prodotto dall’utente premendo:
 Windows  Control-Z e poi INVIO
 Linux/Unix  Control-D
 Le funzioni scanf e getchar restituiscono
EOF quando l’utente indica la fine dell’input
 In modo analogo gets restituisce NULL
 N.B. Le combinazioni di tasti Control-Z e
Control-D spesso vengono scritte ^Z e ^D, ma
NON si ottengono con il carattere ^ : si deve
invece premere il tasto Control e poi la lettera
38

Lettura di valori
 Esempio di lettura di sequenza di lunghezza
ignota di valori dalla tastiera, la lettura
termina con l’introduzione di un EOF
printf("Terminare con EOF\n");
while (scanf("%d", &a) != EOF)
somma += a;
printf("Somma=%d\n", somma);
 Esempio di input:
12
22
34
^Z
Somma=68
39

Cicli annidati
 Un ciclo può essere collocato
(completamente) nel corpo di un altro ciclo
 In genere, nel caso di cicli FOR ogni ciclo
deve avere una variabile di conteggio diversa
 Il ciclo esterno controlla quello interno
 Il ciclo interno ricomincia sempre da capo (ad
esempio l’inizializzazione dell’indice di un ciclo
FOR interno ad un altro ciclo viene eseguita
ogni volta)
40

Cicli annidati
 Esempio Blocco ciclo
for (i=1; i<=7; i+=3) esterno
{
for (j=2; j<5; j++)
printf("%d,%d ", i, j);
printf("\n");
} Blocco ciclo
printf("%d,%d ", i, j); interno
produce il seguente output:
1,2 1,3 1,4
4,2 4,3 4,4
7,2 7,3 7,4
10,5  notare i valori di uscita
41

Uscita da cicli annidati


 Nel caso di cicli annidati, break fa uscire solo
da un livello; per uscire contemporaneamente
da tutti i cicli annidati si può usare una goto
for (i=0; i<10; i++)
for (j=0; j<10; j++)
{ scanf("%d", &v);
if (v == 0)
goto fuori;
somma += v;
}
fuori:
printf("Somma = %d\n",somma);
42

Uscita da cicli annidati


 Per evitare di avere codice non strutturato e a
scapito di un po’ di efficienza si può scrivere:
esci = NO;
for (i=0; i<10 && esci==NO; i++)
for (j=0; j<10 && esci==NO; j++)
{ scanf("%d", &v);
if (v == 0)
esci = SI;
else
somma += v;
}
printf("Somma = %d\n",somma);
43

Etichette
 Una label (etichetta) viene usata per dare un
nome ad una riga, viene in genere posizionata
all’inizio della riga stessa senza indentazione
ed è terminata da un carattere ‘:’, esempio:
fuori:
 Tutte le label devono avere nomi diversi
(stesse regole sintattiche degli identificatori)
 Una label è visibile solo dall’interno della
funzione dove è definita
 Una label non può essere l’ultima istruzione di
un blocco, se necessario farla seguire da un ;
44

Salti
 Un “salto” fa continuare l’esecuzione di un
programma da un altro punto del codice
 Il salto incondizionato in C si ha per mezzo
dell’istruzione goto
 Sintassi:
goto label;  label senza il carattere ‘:’
Quando viene eseguita, il programma salta
alla riga con quella label e continua da lì
 Una label può essere collocata in una riga
precedente o successiva quella con la goto
 Una label può essere usata da più goto
 Non si può saltare dentro ad una funzione (le
etichette sono visibili solo dentro la funzione)
45

Salti
 L’utilizzo di goto produce sempre codice non
strutturato e quindi potenzialmente (ma non
necessariamente) più difficile da comprendere
e da manutenere
 In particolare, i maggiori problemi sono dovuti
al fatto che la goto permette:
 di saltare in avanti e indietro nel codice, rendendolo
molto intricato e quindi difficile da seguire nel
controllo del flusso (“spaghetti code”)
 di entrare in una stessa sezione di codice da punti
diversi (entry point), tipicamente per sfruttare una
parte di codice di un blocco più ampio per non
doverlo riscrivere
46

Salti
 I vecchi linguaggi di programmazione non
disponevano di costrutti strutturati e l’uso del
goto era indispensabile
 Se il linguaggio dispone di adeguati costrutti
strutturati si può sempre evitare di usare le
goto. Il linguaggio C ha questi costrutti.
 In pochi casi la goto può essere utile per
questioni di efficienza e chiarezza:
 per uscire da cicli annidati
 per uscire da un ciclo contenente uno switch (dove
il break farebbe uscire solo dallo switch)
 Si eviti l’istruzione goto in tutti gli altri casi
47

Salti
 La liceità di utilizzo dell’istruzione di salto
incondizionato è oggetto di diatribe, con
autorevoli sostenitori di entrambe le parti
(Dijkstra/contrario, Knuth/a favore)
 Con la goto sono “condannati” anche gli altri
pseudo-salti, sebbene controllati: break,
continue e return/exit multipli
 Alcuni linguaggi moderni (es. Java) non hanno
goto, ma dispongono di costrutti aggiuntivi
(es. break con etichetta) proprio per uscire
da cicli annidati
48

Salti
 Quando si usa una goto per uscire da due o
più cicli annidati, è buona norma collocare
l’etichetta subito sotto il corpo del ciclo più
esterno (senza istruzioni intermedie) ed è
preferibile che sia allineata verticalmente con
la keyword del ciclo più esterno da cui uscire
for (… Allineati verticalmente
{
for (…
{ … if (condizione speciale)
goto fuori;…
} Etichetta subito sotto il corpo
}
del ciclo più esterno
fuori:
49

Esercizi
1. Scrivere un programma che calcoli la media
(con parte frazionaria) di 100 valori interi
introdotti dalla tastiera.
2. Scrivere un programma che chieda quanti
siano i valori che verranno introdotti dalla
tastiera, li chieda tutti e ne stampi la somma
e la media.
3. Scrivere un programma che calcoli la media di
tutti i valori introdotti dalla tastiera finché non
ne viene introdotto uno non compreso tra 18
e 30, ad esempio 9999 (provare proprio
questo valore!). La visualizzazione della media
deve avvenire solo alla fine (ossia non ogni
volta che un valore viene introdotto).
50

Esercizi
4. Scrivere un programma che richieda N numeri
da tastiera e ne calcoli il valore massimo.
5. Scrivere un programma che richieda N numeri
da tastiera e ne calcoli il valore massimo, il
valore minimo, la somma e la media.
6. Si scriva un programma che calcoli il fattoriale
di un numero intero N dato dalla tastiera. Si
ricordi che il fattoriale di un numero n (simbolo
n!) viene calcolato con la seguente formula:
n! = n ·(n–1)·(n–2)· ... ·2 ·1.
51

Esercizi
7. Scrivere un programma che calcola i primi N
numeri di Fibonacci, con N introdotto dalla
tastiera. I numeri di Fibonacci sono una
sequenza di valori interi che inizia con i due
valori fissi 1 e 1 e ogni successivo valore è la
somma dei due precedenti.
Ad esempio i primi 10 numeri di Fibonacci
sono: 1 1 2 3 5 8 13 21 34 55.
8. Scrivere un programma che calcoli i primi
numeri di Fibonacci minori o uguali a N, con N
introdotto dalla tastiera.
Ad esempio i primi numeri di Fibonacci minori
o uguali a 10 sono: 1 1 2 3 5 8.
52

Esercizi
9. Si scriva un programma per calcolare ex
mediante il suo sviluppo in serie:
2 3
x x x
e = 1+ +
x
+ + ...
1! 2! 3!
Ogni frazione aggiunge precisione al risultato,
per cui conviene usare valori di n
adeguatamente elevati, ad esempio compresi
tra 30 e 40. Si verifichi che i risultati calcolati
in questo modo siano coerenti con quelli
forniti dalla funzione intrinseca exp
calcolando la differenza dei valori.
53

Esercizi
10. Siscriva un programma dove il calcolatore
determini casualmente un numero intero
compreso tra 0 e 99 e chieda all’utente di
trovare il numero stesso. Ad ogni input
dell’utente il calcolatore risponde con “troppo
alto” o “troppo basso”, finché non viene
trovato il valore corretto. Per generare valori
casuali si utilizza la funzione rand.
11. Siscriva un programma per calcolare la radice
quadrata mediante la formula
1 A
iterativa di Newton: xi +1 =  xi + 
2  xi 
54

Esercizi
(Continuazione)
Dato il valore A, se ne vuole calcolare la
radice quadrata x. La formula data calcola
valori di x sempre più precisi.
Inizialmente si considera x i=0 = A, ricavando
un valore x1 che approssima molto
grossolanamente il valore della radice
quadrata.
Si riinserisce x1 nella formula (al posto di xi)
ottenendo un x2 che è un’approssimazione
migliore della precedente.
Si continua in questo modo finché il risultato
non varia più (cioè xi = xi +1).