Ingresso interattivo (scanf(); %lf)
Se un programma deve essere eseguito una sola volta, i dati
possono essere inseriti direttamente nel programma stesso.
Tuttavia, nella maggior parte dei casi, è necessario immettere i dati in
un programma mentre esso è in esecuzione.
A questo scopo si usa la funzione scanf().
Mentre printf() visualizza una copia
del valore memorizzato in una
variabile,
scanf() consente di immettere da tastiera un valore che viene
visualizzato sullo schermo e memorizzato direttamente in una variabile.
Come printf(), anche scanf() richiede una stringa di controllo
come primo argomento dentro le parentesi del nome di funzione.
La stringa di controllo dice alla funzione il tipo dati che viene immesso
e usa le stesse sequenze di controllo di printf().
Tuttavia, a differenza della stringa di controllo usata in printf(),
quella passata a scanf() consiste tipicamente in
sole sequenze di controllo di conversione.
Inoltre, a differenza di printf(), dove la stringa di controllo può
essere seguita da un elenco di NOMI di variabile,
scanf() richiede che la stringa di controllo sia
seguita da un elenco di INDIRIZZI di variabile.
Ad es., una chiamata alla funzione scanf() potrebbe essere
l’istruzione
scanf(“%d”, &num1);
La sequenza di controllo di conversione %d è la stessa usata in
printf(), e dice a scanf() che opererà su un numero intero.
Come si osserva, in scanf() si è usato l’operatore di indirizzo &
davanti alla variabile num1 (ricordiamo che &num1 si legge
l’indirizzo di num1).
Quando incontra un’istruzione del tipo scanf(“%d”, &num1); il
computer sospende l’esecuzione del programma e scansiona in
continuazione la tastiera per ricevere dati (scanf è
un’abbreviazione per scan function).
Quando viene digitato un dato, scanf() lo memorizza nell’indirizzo
che le viene fornito. Quindi il programma continua l’esecuzione con
l’istruzione successiva a scanf().
A questo proposito consideriamo il programma seguente:
#include <stdio.h>
void main(void)
{
float num1, num2, prodotto;
printf(“Scrivi un numero: “);
scanf(“%f”, &num1);
printf(“Scrivi un altro numero: “);
scanf(“%f”, &num2);
prodotto = num1 * num2;
printf(“%f per %f uguale %f”, num1, num2, prodotto);
}
Esso produce la seguente uscita:
Scrivi un numero: 22
Scrivi un altro numero: 4
22.000000 per 4.000000 uguale 88.0000
La prima chiamata a printf() produce un prompt, cioè un
messaggio che dice all’utente cosa deve scrivere, in questo caso un
numero.
Quindi il computer esegue l’istruzione successiva, che è una chiamata
a scanf(); questa pone il computer in uno stato di attesa
temporanea (o wait) fino a che l’utente digita un valore e lo invia a
scanf() premendo il tasto <Invio>.
Il valore viene assegnato alla variabile num1, inviandolo al suo
indirizzo (&num1) che è stato passato a scanf().
Quindi il computer esce dallo stato di attesa e il programma prosegue
con l’istruzione successiva.
Questa è un’altra chiamata a printf(), che fa visualizzare il
messaggio successivo.
Di nuovo la seconda chiamata a scanf() pone il computer in uno
stato di attesa temporanea finché l’utente digita un secondo valore,
che viene inviato all’indirizzo della variabile num2.
Sebbene nel programma ogni chiamata a scanf() sia stata usata
per memorizzare un valore in una variabile, si può usare scanf()
per immettere e memorizzare tanti valori quante sono le sequenze
di controllo di conversione presenti nella sua stringa di controllo.
Ad es., l’istruzione
scanf(“%f %f”, &num1, &num2);
fa sì che due valori siano letti da tastiera e inviati agli indirizzi delle
variabili num1 e num2.
Nella stringa di controllo lo spazio tra le due sequenze di controllo di
conversione %f e %f è stato inserito solo per una migliore
leggibilità, e anche la stringa di controllo %f%f avrebbe funzionato
ugualmente.
Tuttavia, quando si immettono i numeri da tastiera, va lasciato
almeno uno spazio tra essi (per indicare la fine di un numero e
l’inizio del successivo), indipendentemente da quale stringa sia
stata usata (%f %f o %f%f).
L’unico caso in cui uno spazio influenzi i valori immessi si ha quando
scanf() si aspetta un tipo dati carattere.
Ad es., l’istruzione
scanf(“%c%c%c”, &car1,&car2,&car3);
fa sì che scanf() memorizzi i prossimi tre caratteri digitati
rispettivamente nelle variabili car1, car2, car3.
Se si scrive
x y z
x è memorizzato in car1, uno spazio vuoto è memorizzato in car2
e y in car3.
Se invece si usa l’istruzione
scanf(“%c %c %c”, &car1,&car2,&car3);
scanf() si aspetta tre caratteri separati esattamente da uno spazio.
Si tenga presente che,
se va immesso un numero in precisione doppia, con scanf()
si deve usare la sequenza di controllo di conversione %lf
Come printf(), nemmeno scanf() controlla i tipi dati dei valori
immessi, ma deve essere l’utente ad assicurarsi che tutte le
variabili siano dichiarate corettamente e che tutti numeri immessi
siano del tipo corretto.
Tuttavia, scanf() è abbastanza versatile da eseguire qualche
conversione di tipi dati.
Per esempio, se viene fornito un intero al posto di un numero in virgola
mobile o in doppia precisione, scanf() aggiunge automaticamente
il punto decimale alla fine del numero prima di memorizzarlo.
Analogamente, se viene immesso un numero in virgola mobile o in
doppia precisione quando è atteso un intero, scanf() usa solo la
parte intera del numero.
Costanti simboliche (#define). Un dato letterale è un qualsiasi
dato all’interno di un programma che identifichi esplicitamente se
stesso.
Ad es., le costanti 2 e 3.1416 nell’istruzione di assegnazione
circonf = 2 * 3.1416 * raggio
si dicono anche letterali, perché sono letteralmente inserite
direttamente nell’istruzione.
In molti casi uno stesso letterale compare più volte in un programma:
per esempio un programma che calcola gli interessi bancari
potrebbe usare spesso il tasso d’interesse.
In tale caso, se fosse necessario cambiare il valore del tasso, si
dovrebbero effettuare numerose correzioni al programma, con il
rischio di commettere errori.
Conviene allora sfruttare la possibilità, offerta dal C, di definire il
valore una sola volta, uguagliando il numero a un nome simbolico e
usando tale nome, in tutto il programma, al posto del numero.
Per uguagliare un numero a un nome simbolico si usa l’istruzione
#define, come nei due esempi seguenti:
#define TASSO 0.05
#define PIGR 3.1416
Osservazioni.
1.L’istruzione #define, detta anche
istruzione di equivalenza
non viene elaborata dal compilatore C che trasforma le istruzioni in
linguaggio macchina, ma è un’istruzione al preprocessore di C
(come indica il simbolo #), e pertanto termina senza punto e
virgola.
Essa ovviamente si deve trovare prima delle istruzioni che usano i
relativi nomi simbolici, e di solito viene scritta all’inizio del file, prima
della funzione main().
2. I nomi simbolici, detti anche costanti nominate o costanti
simboliche, sono scritti di solito in lettere maiuscole, per distinguerli
dai nomi di variabili dichiarati nelle istruzioni di dichiarazione.
Ecco un esempio di uso dell’istruzione #define:
#define TASSO 0.15
#include <stdio.h>
void main(void)
{
float netto, imposta, totale;
printf(“\nScrivi il netto da pagare: “);
scanf(“%f”, &netto);
imposta = TASSO * netto;
totale = netto + imposta;
printf(“L’imposta da pagare è E %4.2f”, imposta);
printf(“\nIl totale da pagare è E %5.2f”, totale);
}
Selezione
Espressioni relazionali. Oltre a eseguire le operazioni di addizione,
sottrazione, moltiplicazione e divisione, tutti i computer hanno la
capacità di confrontare i numeri.
Giacché molte situazioni in cui vengono prese decisioni
apparentemente “intelligenti” possono essere ridotte al livello di
scegliere tra due valori, la capacità di confronto di un computer può
essere usate per creare una prestazione più o meno simile
all’intelligenza.
Le espressioni usate per confrontare gli operandi sono dette
espressioni relazionali.
Una semplice espressione
relazionale consiste in un
operatore relazionale che collega
due operandi, che possono
essere variabili o costanti, come
Gli operatori relazionali disponibili in C sono indicati in tabella, e
possono essere usati con dati interi, in virgola mobile, doppia
precisione o caratteri.
Le espressioni relazionali sono spesso dette condizioni, e come
tutte le espressioni in C sono valutate per fornire un risultato
numerico, che può essere solamente un valore intero 0 o 1.
Una condizione interpretata come vera ha valore intero 1, e una
falsa ha valore 0. Ad esempio, dato che la relazione 3 < 4 è
sempre vera, essa ha valore 1, mentre la relazione 2.0 > 3.3,
che è sempre falsa, ha valore 0.
Ciò può essere verificato eseguendo le istruzioni
printf(“Il valore di 3 < 4 è %d”, 3<4);
printf(“\nIl valore di 2.0 > 3.3 è %d”, 2.0>3.3);
che producono appunto i risultati
Il valore di 3 < 4 è 1
Il valore di 2.0 > 3.3 è 0
Il valore di un’espressione relazionale quale ore > 0 dipende dal
valore memorizzato nella variabile ore.
In un programma C, non è tanto importante il valore di
un’espressione relazionale quanto l’interpretazione che C fornisce
del valore quando si usa l’espressione come parte di un’istruzione
di selezione.
In tali espressioni, che vedremo tra poco, C usa il valore zero per
rappresentare una condizione falsa, e qualsiasi valore diverso da
zero per rappresentare una condizione vera.
La scelta di quale azione svolgere si baserà quindi sul valore
ottenuto.
In aggiunta agli operandi numerici, con gli operatori relazionali si
possono confrontare anche dati carattere.
Ricordando che, per esempio, il codice ASCII per la lettera A è minore
di quello per la lettera B, e così via, le seguenti espressioni sono
valutate come indicato.
Confronare le lettere è essenziale quando si debbano ordinare
alfabeticamente dei nomi, o quando si usino i caratteri per
selezionare una particolare scelta nelle situazioni in cui vadano
prese decisioni.
Operatori logici. Oltre a usare come condizioni espressioni
relazionali semplici, si possono creare condizioni più complesse
usando gli operatori logici AND (&&), OR (||) e NOT (!), già visti
in precedenza.
Quando si usa l’operatore AND, &&, con due espressioni semplici,
la condizione composta è vera (ha valore 1) solo se entrambe le
espressioni sono vere.
Perciò la condizione composta
peso > 60 && peso < 70
è vera solo se la variabile peso ha un valore compreso fra 61 e 69.
Quando si usa l’operatore OR, ||, la condizione composta è vera se
almeno una delle condizioni è vera.
Perciò la condizione composta
peso < 60 || peso >70
è vera se la variabile peso ha un valore non compreso fra 61 e 69.
L’operatore NOT, !, viene usato per cambiare lo stato o valore di
un’espressione.
Cioè
se
allora
espressione
!espressione
è falsa
è vera.
Gli operatori relazionali e logici hanno una gerarchia di esecuzione
simile a quella degli operatori aritmetci.
La tabella seguente elenca la precedenza di questi operatori in
relazione agli altri che abbiamo visto.
Vediamo, come esempio, come verrebbe valutata la seguente espresione:
(6 * 3 == 36 / 2) || (13 < 3 * 3 + 4) && !((6 - 2) < 5)
( 18
==
18
)
1
1
1
|| (13 <
9
|| (13 < 13)
||
0
||
1
+ 4) && !(
&& !1
&& 0
0
4
< 5)
Istruzione if-else. Questa istruzione fa scegliere al computer una
sequenza di una o più istruzioni a seconda del risultato di un
confronto.
La sua forma generale è:
if
(condizione) istruzione1;
else istruzione2;
Prima di tutto viene valutata la condizione:
• se il suo valore è diverso da zero viene eseguita l’istruzione1
• se è uguale a zero viene eseguita l’istruzione2.
Così, a seconda del valore della condizione, viene sempre
eseguita una delle due istruzioni.
Per maggiore chiarezza, l’istruzione if-else si può scrivere anche
su quattro linee:
if (condizione)
istruzione1;
else
istruzione2;
L’uso di if-else è illustrato nel seguente programma:
#include <stdio.h>
void main(void)
{
float imponibile, tasse;
printf(“Scrivi l’importo tassabile: “);
scanf(“%f”, &imponibile);
if (imponibile <= 10000.0)
tasse = 0.2 * imponibile;
else
tasse = 0.25 * (imponibile - 10000.0) + 2000.0;
printf(“Le tasse sono E %7.2f”, tasse);
}
Ecco l’uscita
prodotta:
Istruzioni composte. Le parti if ed else della istruzione ifelse possono in realtà contenere una istruzione composta, ossia
un numero qualsiasi di istruzioni semplici racchiuse tra le parentesi
{ e }. Lo schema è il seguente:
if (condizione)
{
istruzione1;
istruzione2;
. . .
}
else
{
istruzioneA;
istruzioneB;
. . .
}
L’uso di una istruzione composta è illustrato nel programma
seguente, che converte una temperatura da gradi Celsius in gradi
Fahrenheit o viceversa, secondo la formula F = 9/5*C + 32.
#include <stdio.h>
void main(void)
{
char tipo_temp;
float temp, fahren, celsius;
printf(“Scrivi la temperatura da convertire: “);
scanf(“%f”, &temp);
printf(“Scrivi f se la temperatura è in Fahrenheit”);
printf(“\n o c se la temperatura è in Celsius ”);
scanf(“\n%c”, &tipo_temp);
if (tipo_temp == ‘f’)
{
celsius = (5.0/9.0) * (temp - 32.0);
printf(“\nLa temperatura Celsius equivalente è %6.2f”,
celsius);
}
else
{
fahren = (9.0/5.0) * temp + 32.0;
printf(“\nLa temperatura Fahrenheit equivalente è
%6.2f”, fahren);
}
}
Ecco l’uscita prodotta:
Scrivi la temperatura da convertire: 40
Scrivi f se la temperatura è in Fahrenheit
o c se la temperatura è in Celsius c
La temperatura Fahrenheit equialente è 104.00
Scrivi la temperatura da convertire: 104
Scrivi f se la temperatura è in Fahrenheit
o c se la temperatura è in Celsius f
La temperatura Celsius equivalente è 40.00
Istruzione if a una via. Una modifica talvolta utile dell’istruzione
if-else consiste nell’omettere la sua parte else.
In tale caso l’istruzione assume la forma abbreviata:
if (condizione)
istruzione;
l’istruzione che segue la condizione viene eseguita
solamente se la condizione ha valore diverso da zero (è vera).
Anche adesso l’istruzione può essere composta.
Questa forma modificata dell’istruzione if è detta istruzione if a
una via, ed è illustrata nel programma seguente, che chiede la
targa e il chilometraggio di una vettura, e stampa un messaggio se
la vettura ha percorso più di 30000 kilometri.
#define LIMITE 3000.0
#include <stdio.h>
void main(void)
{
int id_targa;
float km;
printf(“Scrivi la targa e il chilometraggio della
vettura: “);
scanf(“%d %f”, &id_targa, &km);
if(km > LIMITE)
printf(“ La vettura %d ha superato il limite.\n”,
id_targa);
printf(“Fine dell’esecuzione del programma.\n”);
}
Esso produce l’uscita seguente:
Scrivi la targa e il chilometraggio della vettura: 12345
50000
La vettura 12345 ha superato il limite.
Fine dell’esecuzione del programma.
Istruzioni if nidificate. Come abbiamo visto, un’istruzione ifelse può contenere una qualsiasi istruzione valida in C, semplice o
composta, quindi anche un’altra istruzione if-else.
Ad es., in un’istruzione if-else si può inserire una selezione a una
via come nel seguente segmento di programma:
if (ore < 9)
{
if (ore > 6)
printf(“dentro”);
}
else
printf(“fuori”);
Esso stampa la parola “dentro” se la variabile ore ha valore 7 o 8,
stampa “fuori” se ha un valore minore di 7 o maggiore di 8.
Osservazione. Le parentesi {} che racchiudono l’if a una via
interno sono essenziali, perché in loro assenza C assocerebbe else
con il più vicino if non appaiato e non con quello più esterno,
interpretando la precedente istruzione nel seguente modo:
if (ore < 9)
if (ore > 6)
printf(“dentro”);
else
printf(“fuori”);
In questo caso si avrebbe la stessa stampa di prima se ore è
minore di 9, e nessuna stampa se è uguale o maggiore di 9.
Catena if-else. Come si vede, il caso in cui l’istruzione nella parte
if di un’istruzione if-else sia un’altra istruzione if crea
confusione, e viene quindi evitato.
Tuttavia, può essere utile situare un’altra istruzione if-else nella
parte else di un’istruzione if-else.
Ciò assume la forma:
Questa costruzione è detta catena if-else, ed è ampiamente usata
nei programmi applicativi. Ogni condizione è valutata nell’ordine, e se
qualcuna è vera viene eseguita l’istruzione corrispondente e il seguito
della catena termina.
L’ultima istruzione else viene eseguita solamente se nessuna delle
condizioni precedenti è soddisfatta, ed è utile per rivelare una
condizione impossibile o di errore.
È ovvio che la catena può continuare indefinitamente, inserendo
quante si vogliano istruzioni else if prima dell’ultima else,
come pure che ciascuna istruzione individuale può essere sostituita
da un’istruzione composta, racchiusa tra le parentesi { e }.
Esercizio. Scrivere un programma che:
• chieda di immettere la lettera iniziale dello stato civile di una persona
(S=sposato/a, C/N=celibe/nubile, D=divorziato/a, V=vedovo/a;
• stampi il relativo stato civile.
Il programma seguente realizza quanto richiesto:
#include <stdio.h>
void main(void)
{
char codstatociv;
printf(“Scrivi un codice di stato civile (S, C, N,
D o V): “);
scanf(“%c”, &codstatociv);
if (codstatociv == ‘S’)
printf(“\nLa persona è sposata”);
else if (codstatociv == ‘C’ || codstatociv == ‘N’)
printf(“\nLa persona è celibe o nubile”);
else if (codstatociv == ‘D’)
printf(“\nLa persona è divorziata”);
else if (codstatociv == ‘V’)
printf(“\nLa persona è vedova”);
else
printf(“È stato scritto un codice non valido”);
}
Istruzione switch
La catena if-else è usata in
applicazioni di programmazione
dove si deve scegliere un gruppo
di istruzioni tra più alternative
possibili.
L’istruzione switch fornisce
un’alternativa alla catena ifelse nei casi in cui si confronti
il valore di un’espressione intera
con un valore specifico.
La forma generale dell’istruzione
switch è:
switch (condizione)
{
case valore_1:
istruzione1;
istruzione2;
.
break;
case valore_2:
istruzionea
.
break;
case valore_n:
istruzionex;
.
break;
default:
istruzioneaa;
.
}
.
L’istruzione switch usa quattro nuove parole chiave:
switch,
case,
default,
break.
Vediamone i compiti.
La parola chiave switch identifica l’inizio dell’istruzione. La
condizione tra parentesi che la segue viene valutata e il risultato
confrontato con i diversi valori alternativi contenuti nell’istruzione
composta.
La condizione deve avere un valore intero, altrimenti si verifica
un errore di compilazione.
All’interno dell’istruzione switch si usa la parola chiave case per
identificare o etichettare i singoli valori che vengono confrontati con
quello della condizione switch.
I valori sono confrontati nell’ordine con cui sono scritti, fino a che si
trova una corrispondenza; in tale caso l’esecuzione comincia con la
prima istruzione che segue il valore di corrispondenza.
Se il valore della condizione non corrisponde ad alcuno dei
valori case, non viene eseguita alcuna istruzione, a meno che
non s’incontri la parola chiave default.
In tale caso l’esecuzione del programma inizia dall’istruzione che
segue la parola default.
default è opzionale, e opera nella stessa maniera dell’ultima
else in un’istruzione if-else.
Una volta che l’istruzione switch abbia localizzato un punto
d’ingresso, non vengono più valutate altre espressioni case, e
vengono eseguite tutte le istruzioni che seguono all’interno delle
parentesi { e }, fino a che non si incontri l’istruzione break.
Essa quindi identifica la fine di un particolare case e determina
l’uscita immediata dall’istruzione switch.
In altri termini, come la parola case identifica i possibili punti di
partenza dell’istruzione composta, così l’istruzione break
determina i punti terminali.
Se si omettono le istruzioni break, vengono eseguite tutte le case
che seguono quella con il valore di corrispondenza, compresa la
default.
Esercizio. Scrivere un programma che:
• richieda due operandi e un codice numerico di operazione;
• a seconda del codice immesso, esegua sui due operandi una delle
operazioni di addizione, moltiplicazione o divisione.
Un programa che usa l’istruzione switch è il seguente:
#include <stdio.h>
void main(void)
{
int sceltaoper;
double num1, num2;
printf(“Scrivi due numeri: “);
scanf(“%lf %lf”, &num1, &num2);
printf(“Scrivi il codice operazione: “);
printf(“\n
1 per addizione”);
printf(“\n
2 per moltiplicazione”);
printf(“\n
3 per divisione”);
scanf(“%d”, &sceltaoper);
switch (sceltaoper)
{
case 1:
printf(“La somma dei numeri scritti è %6.3lf”, num1+num2);
break;
case 2:
printf(“Il prodotto dei numeri scritti è %6.3lf”,num1*num2);
break;
case 3:
printf(“Il quoziente dei numeri scritti è %6.3lf”,num1/num2);
break;
}
}
Scarica

Fonda13