--- 03_sol2.c ------------------------------------------------------------------------------------
#include <stdio.h>
int main (void) {
int n1, n2, max, somma, i;
/* Inserimento dei numeri */
printf ("Inserisci 2 numeri: ");
scanf ("%d %d", &n1, &n2);
/* Ricerca del massimo */
if (n1 > n2)
max = n1;
else
max = n2;
printf ("Il numero maggiore inserito è %d.\n",
max);
/* Calcolo della somma richiesta */
somma = 0;
for (i = 1; i <= max; i++)
somma = somma + i;
printf ("La somma richiesta vale %d.\n", somma);
/* Fine programma */
return (0);
}
--------------------------------------------------------------------------------------------------
Quanto alla "sfida", dato il ciclo:
for (i = 0; i < 100; i = i + 1) {
/* ... */
}
una versione equivalente usando while è questa:
i = 0;
while (i < 100) {
/* ... */
i = i + 1;
}
Notate che "i++", che mi è sfuggito nella
lezione precedente, grrr, è semplicemente equivalente
a "i = i + 1", ma lo vedremo più avanti.
LEZIONE 3
I vettori... questi sconosciuti! Chiariamo subito che le
vetmucche non hanno niente a che vedere con essi ;). Seriamente
parlando, immaginiamo di voler realizzare un programma che
permetta di inserire da tastiera 5 numeri e li ristampi
alla fine, il che implica che vadano tenuti in memoria in
qualche modo. La cosa si può fare così:
--- 03_ins5.c ------------------------------------------------------------------------------------
#include <stdio.h>
int main (void) {
int n1, n2, n3, n4, n5;
printf ("Inserisci 5 numeri: ");
scanf ("%d", &n1);
scanf ("%d", &n2);
scanf ("%d", &n3);
scanf ("%d", &n4);
scanf ("%d", &n5);
printf ("I numeri che hai inserito sono: %d %d %d %d
%d.\n", n1, n2, n3, n4, n5);
return (0);
}
--------------------------------------------------------------------------------------------------
Tralasciando l'ovvia stupidità di questo esempio,
immaginiamo che i numeri su cui dobbiamo lavorare siano
10... o meglio 100... o meglio ancora 1000! Ovviamente è
assurdo pensare di scrivere 1000 scanf, anche perché
faremmo sicuramente qualche errore di battitura! Ma ecco
che accorre in nostro aiuto il concetto di vettore: immaginiamo
di avere 1000 variabili diverse, chiamate come qualcosa
che assomigli a n1... n1000: esse hanno un unico nome, in
questo caso n, seguito da un numero, che identifica una
tra le 1000 variabili. Ora modifichiamo leggermente la sintassi,
e scriviamo il numero che segue tra parentesi quadre [].
Avremo così n[1]... n[1000], ossia il nostro primo
vettore, espresso nella sintassi che richiede il C. Tuttavia,
come ogni variabile, anche i vettori necessitano di una
dichiarazione prima di poter essere usati; in questo caso
si fa così:
int n[1000];
che significa qualcosa come "Crea 1000 variabili uguali,
tutte di tipo int, ma indipendenti tra di loro, e chiamale
n". Per distinguerle usiamo il numero tra le [], che
nella fattispecie si chiama "indice", . Ad esempio,
il primo numero (che si chiama "elemento del vettore")
sarà n[1], il secondo n[2], e così via...
fino all'ultimo, che sarà n[1000].
Immagino che nasca spontanea una domanda: cosa cambia nel
programma di prima se utilizzo un vettore? Be', tutto ;)!
La caratteristica dei vettori che li rende così importanti
è che non siamo obbligati ad usare un numero come
indice, ma possiamo usare una variabile di tipo int (o un'espressione
che dia come risultato un int)! Ecco come diventa il programma
precedente realizzandolo con l'uso di un vettore:
--- 03_insv.c ------------------------------------------------------------------------------------
#include <stdio.h>
#define DIM 100
int main (void) {
int n[DIM], i;
for (i = 0; i < DIM; i++) {
printf ("Inserisci il numero %d di %d: ", i +
1, DIM);
scanf ("%d", &(n[i]));
}
printf ("I numeri che hai inserito sono:");
for (i = 0; i < DIM; i++) {
printf ("%d ", n[i]);
}
printf ("\n");
return (0);
}
--------------------------------------------------------------------------------------------------
Questo programma fa quello che fa quello di prima, ma lo
fa con 100 numeri! E se volessimo farlo con 10000 rimarrebbe
praticamente identico! Ma vediamolo istruzione per istruzione:
- #define: questà è un'altra cosa nuova:
non è molto complicata, e la sua utilità è
palese insieme ai vettori: infatti vuol dire: "PRIMA
di compilare il programma, passalo da capo a piedi e sostituisci
in tutti i posti dove trovi la parola DIM il numero 100".
Perché usare questo arcano e non scrivere semplicemente
100 nel programma? Be', guardate quante volte compare DIM
in questo semplice programmino, ben 4! Supponiamo che l'abbiate
fatto scrivendo sempre 100 al posto di DIM. Il giorno dopo
decidete che 100 numeri non vi bastano più, e ne
volete 1000. Benissimo, prendere il programma e sostituite
1000 al posto di 100 in tutte le 4 posizioni in cui compare.
Peccato che i programmi spesso siano più lunghi di
una decina di righe, e vi sfido a trovare tutti i posti
in cui dovete sostituire qualcosa! Utilizzando #define,
invece, basta cambiarlo UNA volta, e il (pre-)compilatore
farà il resto... e lui non sbaglia mai! ;)
- for: ormai dovreste conoscere i cicli (rivedete la lezione
2 in caso contrario!), qua ne abbiamo 2, esattamente identici,
nei quali i parte da 0 e arriva fino a 999. Il primo ciclo
si preoccupa di fare inserire i numeri, il secondo di visualizzarli.
Dovrebbe essere tutto abbastanza comprensibile. Unica cosa
da notare, MOLTO IMPORTANTE, è che prima ho detto
una piccola fesseria ;). In realtà, in C, i vettori
prendono la numerazione partendo DA ZERO, NON da uno. Il
che implica che il primo elemento del vettore sia n[0],
e non n[1], che è invece il secondo. Di conseguenza,
contenendo il vettore 1000 elementi, l'ultimo sarà
n[999] (ossia n[DIM - 1]), e non n[1000], che non esiste,
e se provate ad assegnargli un valore, molto probabilmente
il programma andrà in crash! Tenete bene a mente
questo, soprattutto se siete abituati al BASIC, che numera
i vettori a partire da 1 (a meno che usiate option base
0). Cosa faccia il Pascal lo ignoro, se qualcuno gentilmente
me lo facesse sapere... ;)
Nel corpo del for troviamo "n[i]": come dicevo,
possiamo usare una variabile di tipo int come indice, ed
è esattamente quel che facciamo qua: ci riferiamo
all'i-esimo elemento del vettore (ossia l'elemento in posizione
i). All'inizio del ciclo i varrà 0, quindi memorizzeremo
il primo numero letto dalla scanf in n[0]. Subito dopo i
verrà incrementato, passando così a valere
1 e allora lavoreremo su n[1], e così via... fino
a n[999], mentre quandi i varrà 1000, usciremo dal
ciclo.
I vettori ci permettono di fare veramente moooolte cose!
Ad esempio, modifichiamo il programma precedente in modo
che stampi tutti i numeri ORDINATI, e inoltre dica qual
è il minimo e qual è il massimo inserito.
--- 03_ord.c -------------------------------------------------------------------------------------
#include <stdio.h>
#define DIM 10
int main (void) {
int n[DIM], i, max, min, tmp, done;
/* Inserimento numeri */
for (i = 0; i < DIM; i++) {
printf ("Inserisci il numero %d di %d: ", i +
1, DIM);
scanf ("%d", &(n[i]));
/* Aggiorna massimo e minimo se necessario */
if (n[i] > max || i == 0)
max = n[i];
if (n[i] < min || i == 0)
min = n[i];
}
/* Ordinamento */
do {
done = 1;
for (i = 0; i < DIM - 1; i++) {
if (n[i] > n[i + 1]) {
tmp = n[i];
n[i] = n[i + 1];
n[i + 1] = tmp;
done = 0;
}
}
} while (!done);
/* Stampa dei risultati */
printf ("I numeri che hai inserito sono: ");
for (i = 0; i < DIM; i++) {
printf ("%d ", n[i]);
}
printf ("\n");
printf ("Il max numero inserito è %d e il minimo
%d.\n", max, min);
return (0);
}
--------------------------------------------------------------------------------------------------
Benvenuti al vostro primo programma corposo (TM ;)! Commentiamo:
- L'inserimento dei dati è molto simile a quello
del programma precedente, solo qua ci preoccupiamo anche
di controllare che non sia appena stato inserito il massimo
o il minimo, e aggiorniamo di conseguenza le apposite variabili.
Notate che è necessario aggiornale per forza al primo
numero inserito, in quanto, essendo esso l'unico finora,
è sicuramente sia il maggiore che il minore! Ecco
quindi il perché del controllo in OR con i == 0.
- L'ordinamento viene qua eseguito con un algoritmo che
si chiama "bubble sort". Non è il caso
che stia a spiegarvi i dettagli, li lascio all'appendice.
Vi basti sapere che esso ordina il vettore in ordine crescente.
- Il resto dovrebbe essere comprensibilissimo ormai!
Aggiungo solo più che un vettore, essendo semplicemente
una sequenza di variabili "singole", può
ovviamente essere di qualunque tipo può essere una
variabile: int, float, double, char... Ad esempio:
double antani[50];
definisce un vettore di nome antani ;) di 50 elementi (da
0 a 49), ciascuno dei quali è un double.
OK, per questa lezione è tutto... Spero di essere
stato abbastanza chiaro, dato che questo è un argomento
assolutamente fondamentale! Fate molta pratica, che vi servirà!
Vi lascio col solito esercizio e l'appendice. Alla prossima!
APPENDICE:
Parliamo un po' di algoritmi di ordinamento: ve ne sono
svariati, che differiscono in due aspetti: la loro complessità
(al livello intuitivo del codice necessario ad implementarli)
e la loro velocità (ossia il numero di operazioni/cicli
o quanto tempo impiegano per completare l'ordinamento).
I più famosi sono 2: il Bubble sort ed il Quick sort.
- Il Bubble sort è caratterizzato dall'estrema semplicità
del codice che lo implementa, e lo potete vedere nel programma
soprastante. Tuttavia esso è anche il più
lento :(. Esso funziona pressapoco così: si scandisce
il vettore dall'inizio alla fine, ed ogni volta che si trova
una coppia di elementi che non rispettano l'ordinamento
(ossia, nel caso di ordinamento crescente, l'elemento prima
è maggiore dell'elemento dopo) si scambiano, e si
passa alla coppia successiva. Finito lo scorrimento del
vettore, se si è compiuto almeno uno scambio, si
ripete il tutto dall'inizio, altrimenti gli elementi sono
ordinati.
- Il Quick sort è esattamente l'opposto: abbastanza
macchinoso da implementare (e qua il discorso si può
applicare ricorsivamente: esistono diverse varianti del
Quick sort, alcune più semplici e più lente
ed altre più complesse e più veloci, ma lasciamo
perdere) ma molto veloce (da cui il nome). Il suo funzionamento
è abbastanza complicato da spiegare, ma sostanzialmente
si basa sulla suddivisione successiva degli intervalli di
elementi da ordinare. Comunque sia, una simpatica funzione
qsort () fa parte dello standard ANSI per il C, quindi non
dovete programmarvela voi ;). Presto la vedremo all'opera...
se siete ansiosi: man qsort!
ESERCIZIO
Realizzate un programma che permetta di inserire da tastiera
10 numeri e li memorizzi in un vettore. Successivamente,
il programma deve chiedere all'utente un numero < 10
e stampare la somma degli elementi del vettore dal primo
a quello corrispondente al nunero inserito, e quella degli
elementi da quello successivo a quest'ultimo fino all'ultimo.
Ad esempio, se il vettore inserito è:
3 5 7 9 2 1 1 6 4 2
e successivamente l'utente inserisce 4, le due somme richieste
sono:
(3 + 5 + 7 + 9 + 2) = 24
e
(1 + 1 + 6 + 4 + 2) = 14
ricordandovi che l'elemento numero 4 è, in realtà,
il quinto inserito.
### FINE LEZIONE 3 - Versione 1.0 (23/01/2003) ###
|