I linguaggi di programmazione -
il linguaggio C - struttura di un programma - variabili
ed assegnamenti - costanti - incrementare
una variabile - pre e post incremento - immissione ed emissione di dati - istruzione
if - istruzione composte - l'operatore
? - cicli e istruzione while - cicli e istruzione
for - cicli e istruzione do while
I linguaggi di programmazione
I linguaggi di programmazione permettono di
scrivere algoritmi interpretabili da un sistema di elaborazione. Un algoritmo
scritto in un linguaggio di programmazione viene chiamato programma e
il processo di scrittura del programma, a partire dallalgoritmo, viene
chiamato codifica. I linguaggi di programmazione sono costituiti da un
alfabeto e da un insieme di regole che devono essere rispettate per scrivere
programmi sintatticamente corretti.
Il linguaggio macchina costituito da zero ed
uno è l'unico che pilota direttamente le unità fisiche dell'elaboratore in quanto
è lunico comprensibile dallelaboratore stesso. È però estremamente
complicato scrivere programmi in tale linguaggio naturale per la macchina ma
completamente innaturale per luomo. Per poter permettere un dialogo
più semplice con la macchina sono nati i linguaggi di programmazione.
Il più vecchio linguaggio di programmazione
è il linguaggio assembly. Il linguaggio assembly è una rappresentazione simbolica
del linguaggio macchina. La scrittura di programmi è enormemente semplificata
rispetto a quest'ultimo. Per essere eseguito dall'elaboratore un programma in
linguaggio assembly deve essere tradotto in linguaggio macchina; tale lavoro
è a carico di un programma detto assemblatore. Questi due tipi di linguaggi,
detti anche linguaggi di basso livello sono propri di ogni macchina.
I linguaggi di alto livello sono più
vicini al linguaggio naturale, sono orientati ai problemi piuttosto che all'architettura
della macchina. Non fanno riferimento ai registri fisicamente presenti sulla
macchina ma a variabili. Per essere eseguiti devono essere tradotti in linguaggio
macchina, e tale traduzione viene svolta da un programma detto compilatore.
I linguaggi di alto livello sono in larga misura
indipendenti dalla macchina, possono essere eseguiti su qualsiasi elaboratore
a patto che esista il corrispondente compilatore che ne permetta la traduzione.
I linguaggi di alto livello si caratterizzano
per essere orientati a specifiche aree applicative. Questi linguaggi vengono
anche detti della terza generazione.
Per ultimi in ordine di tempo sono arrivati
i linguaggi della quarta generazione, ancora più spiccatamente rivolti a specifiche
aree applicative e, nell'ambito del loro orientamento, utilizzabili in modo
intuivo dall'utente non esperto. Il più famoso di questi è SQL (Structured Query
Language), che opera su basi dati relazionali. I linguaggi di IV generazione
sono detti non procedurali poiché l'utente specifica la funzione che
vuole svolgere senza entrare nel dettaglio di come verrà effettivamente svolta.
Il linguaggio C
Nel 1972, presso i Bell Laboratories, Dennis
Ritchie progettava e realizzava la prima versione del linguaggio C. Ritchie
aveva ripreso e sviluppato molti dei principi e dei costrutti sintattici del
linguaggio BCPL, sviluppato da Martin Richards, e del linguaggio B, sviluppato
da Ken Thompson, l'autore del sistema operativo Unix. Successivamente gli stessi
Ritchie e Thompson riscrissero in C il codice di Unix.
Il C si distingueva dai suoi predecessori per
il fatto di implementare una vasta gamma di tipi di dati (carattere, interi,
numeri in virgola mobile, strutture) non originariamente previsti dagli altri
due linguaggi. Da allora ad oggi il C ha subito trasformazioni: la sua sintassi
è stata affinata, soprattutto in conseguenza della estensione object-oriented
(C++). Nel 1983, lIstituto Nazionale Americano per gli Standard (ANSI)
ha costituito un comitato per una definizione del linguaggio C non ambigua
e non dipendente dalla macchina: il risultato è lo standard ANSI per il
C. A questo standard si farà riferimento in questi appunti.
Struttura di un programma
Iniziamo esaminando il programma del seguente
listato.
#include<stdio.h>
main()
{
printf("abc");
printf("def");
printf("ghi");
printf("lmn");
printf("opqrs");
printf("tuvz");
}
Eseguendolo verrà visualizzata la stringa delle
lettere dellalfabeto italiano:
abcdefghilmnopqrstuvz
Dalla parola main, seguita da parentesi tonda
aperta e chiusa, inizia l'esecuzione del programma. Il corpo del programma,
che comincia dalla parentesi graffa aperta e finisce alla parentesi graffa chiusa,
è composto da una serie di istruzioni printf che verranno eseguite sequenzialmente.
L'istruzione printf permette la stampa su video
di ciò che è racchiuso tra parentesi tonde e doppi apici. Per esempio
printf("abc");
visualizza
abc
Ogni istruzione deve terminare con un carattere di punto e virgola.
Per poter utilizzare printf, come le altre funzioni
di entrata/uscita, si deve inserire all'inizio del testo la linea
#include <stdio.h>
che awerte il compilatore di includere i riferimenti
alla libreria standard di input/output (stdio sta per standard input/output).
Il C distingue tra lettere maiuscole e minuscole;
dunque occorre fare attenzione, se si scrive MAIN() o Main() non si fa riferimento
a main().
La struttura del programma C che abbiamo usato nell'esempio
è:
inclusione_librerie
main()
{
istruzione1
istruzione2
istruzione3
........
istruzioneN
}
Il punto e virgola conclude listruzione.
Se si desidera che l'uscita di ogni istruzione
printf venga prodotta su una linea separata, si deve inserire n al termine
di ogni stringa e prima della chiusura dei doppi apici, come nel seguente Listato.
#include <stdio.h>
main()
{
printf("abcn");
printf("defn");
printf("ghin");
printf("lmnn");
printf("opqrsn");
printf("tuvzn");
}
Eseguendo il programma si otterrà la visualizzazione
delle seguenti stringhe di caratteri
abc
def
ghi
lmn
opqrs
tuvz
In questo caso la prima stringa (abc) viene
stampata su video a partire dalla posizione attuale del cursore. Se si vuole
cominciare la stampa delle stringhe da una nuova riga basta inserire n anche
allinizio come in:
printf("nabcn");
Qui prima si passa ad una nuova riga,
poi si stampa la stringa specificata e quindi si posiziona il
cursore in una nuova riga.
In generale è bene tenere presente che ogni
printf stampa a partire dalla posizione in cui si trovava il cursore (in generale
a destra dellultima stampa). Per poter modificare tale comportamento è
necessario inserire gli opportuni caratteri di controllo. In effetti la sequenza
n corrisponde ad un solo carattere, quello di nuova linea (newline).
Nella printf possono inserirsi altri caratteri
di controllo di cui si forniscono di seguito quelli che possono avere utilizzo
più frequente:
- n porta il cursore allinizio della riga successiva
- t porta il cursore al prossimo fermo di tabulazione (ogni
fermo di tabulazione è fissato ad 8 caratteri)
- a (alert) fa emettere un suono dallo speaker
- stampa un apice
- " stampa le virgolette
Variabili e assegnamenti
Supponiamo di voler calcolare l'area di un rettangolo
i cui lati hanno valori interi. Entrano in gioco due variabili, la base e l'altezza,
il cui prodotto è ancora un valore intero, l'area appunto.
#include <stdio.h>
/* Calcolo area rettangolo */
main(){
int base;
int altezza;
int area;
base = 3;
altezza = 7;
area = base*altezza;
printf("%d",area);
}
Per rendere evidente la funzione espletata dal
programma si è inserito un commento:
/* Calcolo area rettangolo */
I commenti possono estendersi su più linee e
apparire in qualsiasi parte del programma, devono essere preceduti da /* e seguiti
da */, tutto ciò che appare nelle zone così racchiuse non viene preso in considerazione
dal compilatore e non ha alcuna influenza sul funzionamento del programma, è
però importantissimo per chi legge il programma: è infatti nelle righe di commento
che viene specificato il senso delle istruzioni che seguiranno. Cosa, questa,
non immediatamente comprensibile se si leggono semplicemente le istruzioni del
linguaggio.
Subito dopo main() sono presenti le dichiarazioni
delle variabili intere necessarie:
int base; int altezza; int area;
La parola chiave int specifica che l'identificatore
che lo segue si riferisce ad una variabile di tipo intero; dunque base, altezza
e area sono variabili di questo tipo.
Anche le dichiarazioni così come le altre istruzioni
devono terminare con un punto e virgola. Nel nostro esempio alla dichiarazione
del tipo della variabile corrisponde anche la sua definizione che fa sì che
le venga riservato uno spazio in memoria centrale.
Il nome di una variabile la identifica, il suo
tipo ne definisce la dimensione e l'insieme delle operazioni che vi si possono
effettuare. La dimensione può variare rispetto all'implementazione; molte versioni
del C, come quelle sotto i sistemi operativi MS-DOS e Unix, riservano per gli
int uno spazio di due byte, il che permette di poter lavorare su interi che
vanno da -32768 a +32767. Tra le operazioni permesse fra int vi sono: la somma
(+), la sottrazione (-), il prodotto (*) e la divisione (/).
Effettuata la dichiarazione, la variabile può
essere utilizzata. L'istruzione
base = 3;
assegna alla variabile base il valore 3; cioè
inserisce nello spazio di memoria riservato a tale variabile il valore indicato.
Effetto analogo avrà altezza=7. L'assegnamento è dunque realizzato mediante
l'operatore = .
Nel linguaggio C è possibile assegnare lo stesso
valore a più variabili contemporaneamente. Per esempio se le dimensioni
riguardavano un quadrato, si sarebbe potuto scrivere:
base = altezza = 5;
In questo caso prima verrebbe assegnato il valore
5 alla variabile altezza e quindi, il risultato dellassegnazione (cioè
5), viene assegnato alla variabile base.
L'istruzione:
area = base * altezza;
assegna alla variabile area il prodotto dei
valori di base e altezza .
L'operatore asterisco effettua l'operazione
di prodotto tra la variabile che lo precede e quella che lo segue, è dunque
un operatore binario.
L'ultima istruzione
printf("%d",area);
visualizza 21, il valore della variabile area.
Tra i doppi apici, il simbolo di percentuale % specifica che il carattere che
lo segue definisce il formato di stampa della variabile area; d (decimale)
indica che si tratta di un intero del quale si desidera la visualizzazione nel
sistema decimale.
Le dichiarazioni delle variabili dello stesso
tipo possono essere scritte in sequenza separate da una virgola:
int base,altezza,area;
Dopo la dichiarazione di tipo sono specificati
gli identificatori di variabile, che possono essere in numero qualsiasi, separati
da virgola e chiusi da un punto e virgola. In generale quindi la dichiarazione
di variabili ha la seguente forma:
tipo lista_di identificatori;
Esistono inoltre delle regole da rispettare
nella costruzione degli identificatori: devono iniziare con una lettera o con
un carattere di sottolineatura _ e possono contenere lettere, cifre e _. Per
quanto riguarda la lunghezza occorre tenere presente che soltanto i primi trentadue
caratteri sono significativi, anche se nelle versioni del C meno recenti questo
limite scende a otto caratteri. Sarebbe comunque opportuno non iniziare il nome
della variabile con il carattere di sottolineatura ed è bene tenere presente
che le lettere accentate, permesse dalla lingua italiana, non sono considerate
lettere ma segni grafici e le lettere maiuscole sono considerate diverse dalle
rispettive minuscole.
Oltre a rispettare le regole precedentemente
enunciate, un identificatore non può essere una parola chiave del linguaggio,
né può essere uguale ad un nome di funzione libreria o scritta dal programmatore.
Allo scopo di rendere più chiaro l'effetto ottenuto
dal programma dell'esempio precedente, si possono visualizzare i valori delle
variabili base e altezza.
printf("%d ",base);
printf("%d ",altezza);
printf("%d",area);
Nelle prime due printf si è inserito all'interno
dei doppi apici, di seguito all'indicazione del formato di stampa %d, uno spazio,
in modo che venga riportato, in fase di visualizzazione, dopo il valore della
base e dell'altezza, così da ottenere
3 7 21
e non
3721
È opportuno, per motivi di chiarezza, far precedere
la visualizzazione dei valori da una descrizione. In questo caso è sufficiente
inserirla tra doppi apici.
printf("Base: %d ",base);
printf("Altezza: %d ",altezza);
printf("Area: %d",area);
Quello che si ottiene in esecuzlone è
Base: 3 Altezza: 7 Area: 21
Per ottenere una distanza maggiore tra il valore
di base e altezza e le seguenti descrizioni si possono aggiungere ulteriori
spazi
printf("Base: %d",base);
printf("tAltezza: %d",altezza);
printf("tArea: %d",area);
così da avere
Base: 3 Altezza: 7 Area: 21
Per far in modo che ad ogni visualizzazione
corrisponda un salto riga si deve inserire n prima della chiusura dei doppi
apici:
printf("Base: %dn",base);
printf("Altezza: %dn",altezza);
printf("Area: %dn",area);
Si possono effettuare più output con la stessa
istruzione. Per esempio:
printf("Base: %d Altezza: %d Area: %d",base,altezza,area);
visualizzerebbe:
Base: 3 Altezza: 7 Area: 21
In questo caso figura una lista di variabili
da mandare in output. In sede di esecuzione ogni simbolo di formato si riferisce
ad una variabile (il primo %d si riferisce a base, il secondo ad altezza ecc
).
Ovviamente si sarebbero potuti pure inserire diversi caratteri di formato per
formattare, a seconda delle preferenze, loutput (per es n per andare
a capo e t per distanziare).
Osserviamo nel listato il programma C modificato
seguendo alcune delle caratteristiche introdotte.
#include <stdio.h>
/* Calcolo area rettangolo */
main(){
int base,altezza,area;
base = 3;
altezza = 7;
area = base*altezza;
printf("Base: %d Altezza: %dn Area: %d",base,altezza,area);
}
L'esecuzione del programma avrà il seguente
effetto:
Base: 3 Altezza: 7
Area: 21
Mentre int è una parola chiave del C e fa parte
integrante del linguaggio, base, altezza e area sono identificatori di
variabili scelti a nostra discrezione. Lo stesso effetto avremmo ottenuto utilizzando
al loro posto altri nomi generici quali x, y e z solo che il programma
sarebbe risultato meno comprensibile.
La forma grafica data al programma è del tutto
opzionale; una volta rispettata la sequenzialità e la sintassi, la scrittura
del codice è libera. In particolare più istruzioni possono essere scritte sulla
stessa linea. E indubbio che il programma risulterà notevolmente meno leggibile
del precedente.
Lo stile grafico facilita enormemente il riconoscimento
dei vari pezzi di programma e consente una diminuzione di tempo nelle modifiche,
negli ampliamenti e nella correzione degli errori. In generale è inoltre bene
dare alle variabili dei nomi significativi, in modo che, quando si debba intervenire
a distanza di tempo sullo stesso programma, si possa facilmente ricostruire
l'uso che si è fatto di una certa variabile.
Costanti
Nel programma visto precedentemente i valori
di base e altezza sono costanti, dato che non variano durante l'esecuzione del
programma. Evidentemente avremmo potuto scrivere direttamente
area = 3 * 7;
Quando un certo valore viene utilizzato in modo
ricorrente è opportuno rimpiazzarlo con un nome simbolico; per farlo dobbiamo
definire, all'inizio del programma, mediante l'istruzione define un identificatore
di costante in corrispondenza del valore desiderato.
#define BASE 3
Grazie a questa istruzione potremo utilizzare,
all'interno del programma, BASE al posto del valore intero 3.
La stessa definizione di costante implica che
il suo valore non può essere modificato: BASE può essere utilizzata in un'espressione
a patto che su di essa non venga mai effettuato un assegnamento.
Vediamo nel Listato come viene modificato il
programma del paragrafo precedente con l'utilizzazione delle costanti.
#include <stdio.h>
#define BASE 3
#define ALTEZZA 7
/* Calcolo area rettangolo */
main(){
int area;
area = BASE * ALTEZZA;
printf("Base: %dn",BASE);
printf("Altezza: %dn",ALTEZZA);
printf("Area: %dn",area);
}
Il nome di una costante può essere qualsiasi
identificatore valido in C, comunque abbiamo scelto di utilizzare esclusivamente
caratteri maiuscoli per le costanti e caratteri minuscoli per le variabili per
distinguere chiaramente le une dalle altre. Le costanti BASE e ALTEZZA vengono
considerate di tipo intero in quanto il loro valore è costituito da numeri senza
componente frazionaria.
Invece di utilizzare direttamente i valori,
è consigliabile far uso degli identificatori di costante che sono descrittivi
e quindi migliorano la leggibilità dei programmi. Inoltre, se ci si rende conto
che un certo valore utilizzato più volte deve essere cambiato, nella prima ipotesi,
è necessario ricercalo con attenzione all'interno del testo e modificarlo dov'è
il caso, nella seconda ipotesi è sufficiente intervenire sulla sua definizione.
Per esempio, per fare in modo che il programma precedente calcoli l'area del
rettangolo con base 102 e altezza 34, è sufficiente modificare le linee dov'è
presente l'istruzione define.
#define BASE 102
#define ALTEZZA 34
In sintesi, l'uso delle costanti migliora due
parametri classici di valutazione dei programmi: flessibilità e possibilità
di manutenzione.
La define è in realtà una macroistruzione (brevemente,
macro) del precompilatore C che offre altre possibilità oltre a quella
di definire delle costanti.
Incrementare una variabile
Ogni nuova assegnazione ad una variabile, distrugge
un valore precedentemente contenuto nella variabile stessa. Per cui, nel successivo
esempio:
...
voto = 3;
...
voto = 6;
la variabile voto varrà 6. E ciò qualunque sia
stato il valore precedente.
Per aggiungere uno al valore contenuto
in una variabile si deve assegnare alla variabile il valore precedente più
uno. Ad esempio, questa istruzione aggiunge uno al contenuto della variabile
n:
n = n+1;
Difatti il C calcola per prima cosa il valore
dell'espressione posta a destra del segno di uguale, cioè n+1. Se ad esempio
n vale 5, n+1 vale 6. Questo valore viene poi assegnato alla variabile indicata
a sinistra, cioè alla stessa n. Quindi n, che prima valeva 5, dopo l'esecuzione
dell'istruzione vale 6.
Questo si chiama incrementare una variabile,
cioè appunto aggiungere un nuovo valore al valore precedente (nel caso esaminato
si aggiunge 1). L'operazione opposta (togliere, per esempio, 1 al valore della
variabile) è chiamata decrementare la variabile. Ovviamente si può usare
lo stesso sistema per compiere qualunque operazione sul contenuto della variabile:
area = area*2; /* raddoppia larea */
segmento = segmento/k; /* divide il segmento
per k */
Il linguaggio C dispone di un operatore speciale
per incrementare una variabile di una unità. Scrivere:
contakm++;
equivale a scrivere:
contakm = contakm+1;
Cioè ad incrementare di una unità il valore
della variabile contakm. L'operatore ++ è l'operatore di autoincremento.
L'operatore reciproco -- (due simboli meno) decrementa di una unità il
valore di una variabile:
altezza--; /* riduce laltezza di 1 */
L'operatore -- è quindi l'operatore di autodecremento.
Si sono viste, quindi, due tecniche per aggiungere
uno al valore contenuto in una variabile:
| fogli = fogli+1;
|
fogli++; |
Esiste anche una terza tecnica:
fogli += 1;
La combinazione += è un esempio di operatore
di assegnazione. L'istruzione:
km += 1;
si può leggere: aggiungi uno al valore corrente
della variabile km. L'operatore += non è limitato ad aggiungere 1 ma somma
il valore alla sua destra alla variabile alla sua sinistra. Ad esempio:
| km += 37; |
k1 += k2; |
a += (b/2); |
equivalgono rispettivamente a:
| km = km+37; |
k1 = k1+k2; |
a = a+(b/2); |
L'operatore += non è l'unico operatore di assegnazione,
si possono usare tutti gli operatori aritmetici:
km -= 6; /* toglie 6 ai km percorsi */
lato *= 2; /* moltiplica il lato per 2 */
volume /= 3; /* divide il volume per 3 */
...
Nel seguito di questi appunti si converrà di
utilizzare:
- gli operatori di autoincremento e di autodecremento tutte
le volte che una variabile dovrà essere aggiornata con lunità
- il doppio operatore (es. +=, -= ecc
) tutte le volte
che si parlerà di aggiornamento generico di una variabile (per es. negli accumulatori)
- loperatore di assegnamento generico (cioè =) in tutti
gli altri casi.
Pre e post-incremento
Per aggiungere uno alla variabile z si può scrivere
in due modi:
cioè mettere l'operatore ++ prima o dopo del
nome della variabile.
In generale, le due forme sono equivalenti.
La differenza importa solo quando si scrive una espressione che contiene
z++ o ++z.
Scrivendo z++, il valore di z viene prima
usato poi incrementato:
int x,z; /* due variabili intere */
z = 4; /* z vale 4 */
x = z++; /* anche x vale 4 ma z vale 5 */
Difatti, prima il valore di z (4) è stato
assegnato ad x, poi il valore di z è stato incrementato a 5.
Scrivendo ++z, il valore di z viene prima
incrementato e poi usato:
int x,z; /* due variabili intere */
z = 4; /* z vale 4 */
x = ++z; /* ora x vale 5 come z */
Difatti, prima il valore di z (4) è stato
incrementato a 5, poi il nuovo valore di z (5) è stato assegnato ad x.
Immissione ed emissione di dati
Il programma scritto non calcola l'area di un
qualsiasi rettangolo ma soltanto di quello che ha per base 3 e per altezza 7,
supponiamo centimetri.
Per esempio, per trovare l'area di un rettangolo
di base 57 e altezza 20 si deve intervenire sul programma stesso, scrivendo
#define BASE 57
#define ALTEZZA 20
Oppure nel caso della versione del programma
dove non si sono utilizzate le costanti ma le variabili, dovremmo scrivere
base = 57;
altezza = 20:
Dopo aver effettuato nuovamente la compilazione,
la successiva esecuzione restituirà 1140, cioè l'area del rettangolo in centimetri
quadri.
Per rendere il programma più generale, si deve
permettere a chi lo sta utilizzando di immettere i valori della base e dell'altezza;
in questo modo l'algoritmo calcolerà l'area di un qualsiasi rettangolo.
scanf("%d",&base)
L'esecuzione di questa istruzione fa sì che
il sistema attenda l'immissione di un dato da parte dell'utente.
Analogamente a quello che accadeva in printf,
%d indica che si tratta di un valore intero in formato decimale, tale valore
verrà poi assegnato alla variabile base .
Si presti attenzione al fatto che in una istruzione
scanf il simbolo & (e commerciale, ampersand) deve precedere immediatamente
il nome della variabile; &base sta ad indicare l'indirizzo di memoria in
cui si trova la variabile base. È infatti noto che le locazioni di memoria in
cui sono conservati i dati, sono distinguibili una dallaltra mediante
un indirizzo. Il nome della variabile è una etichetta che viene assegnata, per
nostra comodità, ad una o più locazioni di memoria consecutive. L'istruzione
scanf("%d",&base);può allora essere così interpretata: leggi
un dato intero e collocalo nella posizione di memoria il cui indirizzo è &base
(praticamente lindirizzo corrispondente alla variabile base).
Durante l'esecuzione di un programma può essere
richiesta all'utente l'immissione di più informazioni, perciò è opportuno visualizzare
delle frasi esplicative; a tale scopo facciamo precedere le istruzioni scanf
da appropriate printf.
printf("Valore base: ");
scanf("%d", &base);
L'argomento di printf è semplicemente una costante,
quindi deve essere racchiuso tra doppi apici. Non è necessario neanche richiedere
un salto a linea nuova mediante un n, in quanto l'immissione può avvenire sulla
stessa riga.
Quello che apparirà all'utente in fase di esecuzione
del programma sarà
Valore base: _
In questo istante l'istruzione scanf attende
l'immissione di un valore. Se l'utente digita 15 seguito da <Invio>.
Valore base: 15 <Invio>
questo dato verrà assegnato alla variabile base
Analogamente possiamo modificare il programma
per l'immissione dell'altezza e magari aggiungere un'intestazione che spieghi
all'utente cosa fa il programma, come nel listato seguente:
#include <stdio.h>
/* Calcolo area rettangolo */
main(){
int base,altezza,area;
printf("Calcolo AREA RETTANGOLO n n");
printf("Valore base: ");
scanf("%d",&base);
printf("nValore altezza: ");
scanf("%d",&altezza);
area = base*altezza;
printf("nBase: %d Altezza: %dn",base,altezza);
printf("Area: %d",area);
}
Vediamo l'esecuzione del programma nell'ipotesi
che l'utente inserisca i valori 10 e 13.
Calcolo AREA RETTANGOLO
Valore base: 10 <Invio>
Valore altezza: 13 <Invio>
Base: 10 Altezza: 13
Area: 130
Notare luso del newline per controllare
il modo in cui il programma visualizzerà i suoi risultati. I due n della prima
printf, per esempio, servono: il primo per passare ad una nuova linea, il secondo
per lasciare una linea vuota.
Con una sola istruzione di input è possibile
acquisire più di un valore, per cui i due input dellesempio precedente,
avrebbero potuto essere sostituiti da:
printf("Introdurre Base e Altezza separati
da una virgolan");
scanf("%d,%d",&base,&altezza);
In questo caso, quando il programma viene eseguito,
alla richiesta di input si risponderà con due numeri (che saranno assegnati
rispettivamente a base e ad altezza), separati da una virgola. Il motivo della
presenza della virgola è dovuto alla specifica presente nella scanf: infatti
ci sono due %d separati da una virgola.
Istruzione if
Quando si desidera eseguire un'istruzione al
presentarsi di una certa condizione, si utilizza l'istruzione if.
Per esempio, se si vuole visualizzare il messaggio
"il valore attuale di i è minore di 100" solamente nel caso in cui
il valore della variabile intera i è minore di 100, si scrive:
if(i<100) printf("n il valore attuale
di i è minore di 100");
La sintassi dell'istruzione if è:
if(espressione)
istruzione
dove la valutazione di espressione controlla
l'esecuzione di istruzione: se espressione è vera viene eseguita istruzione.
Nell'esempio seguente il programma richiede
un numero all'utente e, se tale numero è minore di 100, visualizza un messaggio.
#include <stdio.h>
/* Esempio utilizzo if */
main(){
int i;
scanf("%d",&i);
if (i<100)
printf("n minore di 100");
}
L' espressione i<100 è la condizione logica
che controlla l'istruzione di stampa e pertanto la sua valutazione potrà restituire
soltanto uno dei due valori booleani vero o falso che in C corrispondono
rispettivamente ai valori interi uno e zero.È appunto per tale
ragione che l'assegnamento a=i<100, è del tutto lecito. Viene infatti valutata
l'espressione logica i<100, che restituisce 1 (vero) se i è minore di 100
e 0 (falso) se i è maggiore uguale a 100: il risultato è dunque un numero intero
che viene assegnato alla variabile a.
L'operatore != corrisponde a diverso da,
per cui l'espressione a!=0 significa: il valore di a è diverso da zero.
Chiedersi se il valore di a è diverso da zero
è lo stesso che chiedersi se il valore di a è vero, il che, in C, corrisponde
al controllo eseguito per default (effettuato in mancanza di differenti
indicazioni), per cui avremmo anche potuto scrivere
scanf("%d",&i);
a = i<100;
if (a)
printf("n minore di 100");
La sintassi completa dell'istruzione i f è la
seguente:
if(espressione)
istruzione1
[else
istruzione2]
dove la valutazione di espressione controlla
l'esecuzione di istruzione1 e istruzione2: se espressione è vera viene eseguita
istruzione1, se è falsa viene eseguita istruzione2.
Nell'esempio anteriore è stato omesso il ramo
else: il fatto è del tutto legittimo in quanto esso è opzionale, come evidenziato
dalle parentesi quadre presenti nella forma sintattica completa.
La tabella seguente mostra gli operatori utilizzabili
nell'istruzione if :
| Operatore
|
Esempio
|
Risultato
|
| ! |
!a |
(NOT logico)
1 se a è 0, altrimenti 0 |
| < |
a<b |
1 se a<b,
altrimenti 0 |
| <= |
a<=b |
1 se a<=b,
altrimenti 0 |
| > |
a>b |
1 se a>b,
altrimenti 0 |
| >= |
a>=b |
1 se a>=b,
altrimenti 0 |
| == |
a==b |
1 se a è uguale
a b, altrimenti 0 |
| != |
a!=b |
1 se a non
è uguale a b, altrimenti 0 |
| &&
|
a&&b
|
(AND logico)
1 se a e b sono veri, altrimenti 0 |
| || |
a||b |
(OR logico)1
se a è vero, (b non è valutato), 1 se b è vero, altrimenti 0 |
È opportuno notare che, nel linguaggio C, il
confronto delleguaglianza fra i valori di due variabili viene effettuato
utilizzando il doppio segno ==. Si confrontino i seguenti due frammenti:
| Programma
A |
Programma
B |
|
|
| if (a==b)
|
If (a=b) |
| printf("Sono
ugualin"); |
printf("Valore
non zeron"); |
Il programma A confronta il contenuto
della variabile a ed il contenuto della variabile b: se sono uguali stampa la
frase specificata.
Il programma B assegna ad a il valore
attualmente contenuto in b e verifica se è diverso da zero: in tal caso stampa
la frase specificata.
Una ulteriore osservazione va fatta a proposito
degli operatori logici ! (NOT logico), && (AND logico) e || (OR logico)
che vengono usati per mettere assieme più condizioni. Es.
| if (a>5
&& a<10) |
if (a<2
|| a>10) |
| Printf("a
compreso fra 5 e 10"); |
printf("a
può essere <2 oppure >10"); |
Istruzioni composte
L'istruzione composta, detta anche blocco,
è costituita da un insieme di istruzioni inserite tra parentesi graffe che il
compilatore tratta come se fosse un'istruzione unica.
Un'istruzione composta può essere scritta nel
programma dovunque possa comparire un'istruzione semplice. Si noti la
differenza di esecuzioni dei due frammenti di programma seguenti:
| Programma
A |
Programma
B |
|
|
| if (a>100)
|
if (a>100)
{ |
| printf("Prima
frase n"); |
printf("Prima
frase n"); |
| printf("Seconda
frase n"); |
printf("Seconda
frase n"); |
|
};
|
Il programma A visualizzerà "Prima
frase" solo se a è maggiore di 100, "Seconda frase" verrà visualizzato
in ogni caso: la sua visualizzazione prescinde infatti dalla condizione.
Il programma B, qualora a non risulti
maggiore di 100, non visualizzarà alcuna frase: le due printf infatti sono raggruppate
in un blocco la cui esecuzione è vincolata dal verificarsi della condizione.
Un blocco può comprendere anche una sola istruzione.
Ciò può essere utile per aumentare la chiarezza dei programmi: listruzione
compresa in una if può essere opportuno racchiuderla in un blocco anche se è
una sola. In tal modo risulterà più evidente la dipendenza dellesecuzione
della istruzione dalla condizione.
loperatore ?
Loperatore "?" ha la seguente
sintassi:
espr1 ? espr2 : espr3
Se espr1 è vera restituisce espr2 altrimenti
restituisce espr3.
Si può utilizzare tale operatore per assegnare,
condizionatamente, un valore ad una variabile. In questo modo può rendere un
frammento di programma meno dispersivo:
| Programma
A |
Programma
B |
|
|
| if (a>100)
|
sconto=(a>100
? 10 : 5); |
| sconto=10;
|
|
| else |
|
| sconto=5;
|
|
Cicli e istruzione while
Le strutture cicliche assumono nella scrittura
dei programmi un ruolo fondamentale, non fosse altro per il fatto che, utilizzando
tali strutture, si può istruire leleboratore affinché esegua azioni ripetitive
su insiemi di dati diversi: il che è, tutto sommato, il ruolo fondamentale dei
sistemi di elaborazione.
È in ragione delle suddette considerazioni che
i linguaggi di programmazione mettono a disposizione del programmatore vari
tipi di cicli in modo da adattarsi più facilmente alle varie esigenze di scrittura
dei programmi. La prima struttura che prendiamo in considerazione è il ciclo
while (ciclo iterativo con controllo in testa):
while(esp)
istruzione
Viene verificato che esp sia vera, nel qual
caso viene eseguita istruzione. Il ciclo si ripete fintantoché esp risulta
essere vera.
Naturalmente, per quanto osservato prima, istruzione può essere
un blocco e, anche in questo caso, può essere utile racchiudere listruzione
in un blocco anche se è una sola.
Scriviamo, a titolo di esempio di uso del ciclo
while, un frammento di programma che calcola la somma di una serie di valori
positivi immessi dall'utente:
...
somma = 0;
printf("n Inserisci un intero positivo:");
scanf("%d", &numero);
while(numero) {
somma += numero;
printf("n Inserisci un intero positivo:");
scanf("%d", &numero);
}
In questo caso il controllo allinizio
del ciclo garantisce la fine della elaborazione non appena la variabile numero
assume valore zero: il ciclo viene ripetuto mentre numero è diverso da zero.
Linput, fuori ciclo, del primo numero da elaborare permette di impostare
la condizione di controllo sul ciclo stesso. Il totalizzatore somma cumula
tutti i valori provenienti da input.
Cicli e istruzione for
Listruzione for viene utilizzata tradizionalmente
per codificare cicli a contatore: istruzioni cicliche cioè che devono essere
ripetute un numero definito di volte. Il formato del costrutto for è il seguente:
for(esp1; esp2; esp3)
istruzione
Si faccia attenzione ai punti e virgola posti
tra parentesi. Il ciclo inizia con l'esecuzione di esp1 (inizializzazione
del ciclo) la quale non verrà più eseguita. Quindi viene esaminata esp2
(condizione di controllo del ciclo). Se esp2 risulta vera, viene eseguita
istruzione, altrimenti il ciclo non viene percorso neppure una volta. Successivamente
viene eseguita esp3 (aggiornamento) e di nuovo valutata esp2 che se risulta
essere vera dà luogo ad una nuova esecuzione di istruzione. Il processo si ripete
finché esp3 risulta essere falsa.
Se supponiamo di voler ottenere la somma di
tre numeri interi immessi dall'utente, si può scrivere:
...
somma = 0;
for(i = 1; i <= 3; i++) {
scanf("%d", &numero);
somma += numero;
}
Il programma per prima cosa assegna il valore
1 alla variabile i (la prima espressione del for), si controlla se il valore
di i è non superiore a 3 (la seconda espressione) e poiché lespressione
risulta vera verranno eseguite le istruzioni inserite nel ciclo (linput
di numero e laggiornamento di somma). Terminate le istruzioni che compongono
il ciclo si esegue laggiornamento di i così come risulta dalla terza espressione
contenuta nel for, si ripete il controllo contenuto nella seconda espressione
e si continua come prima finché il valore di i non rende falsa la condizione.
Questo modo di agire del ciclo for è quello
comune a tutti i cicli di questo tipo messi a disposizione dai compilatori di
diversi linguaggi di programmazione. Il linguaggio C mette a disposizione delle
opzioni che espandono abbondantemente le potenzialità del for generalizzandolo
in maniera tale da comprendere, per esempio, come caso particolare il ciclo
while. Il frammento di programma per la somma di una serie di numeri positivi,
scritto in precedenza, potrebbe, per esempio, essere riscritto:
...
printf("n Inserisci un intero positivo:");
scanf("%d", &numero);
for(somma=0;numero;) {
somma += numero;
printf("n Inserisci un intero positivo:");
scanf("%d", &numero);
}
Il ciclo esegue lazzeramento di somma
(che verrà eseguito una sola volta) e subito dopo il controllo se il valore
di numero è diverso da zero e, in questo caso, verranno eseguite le istruzioni
del ciclo. Terminate le istruzioni, poiché manca la terza espressione del for,
viene ripetuto il controllo su numero.
Linizializzazione di somma avrebbe potuto
essere svolta fuori dalla for: in tal caso sarebbe mancata anche la prima espressione.
Poiché, nel linguaggio C, ogni ciclo while può
essere codificato utilizzando un ciclo for e viceversa, è bene tenere presente
che la scelta del tipo di codifica da effettuare va sempre fatta in modo da
ottenere la massima chiarezza e leggibilità del programma.
Cicli e istruzione do-while
L'uso della istruzione while prevede il test
sulla condizione all'inizio del ciclo stesso. Ciò vuol dire che se, per esempio,
la condizione dovesse risultare falsa, le istruzioni facenti parte del ciclo
verrebbero saltate e non verrebbero eseguite nemmeno una volta.
Quando l'istruzione compresa nel ciclo deve
essere comunque eseguita almeno una volta, è più comodo utilizzare il costrutto:
do
istruzione
while(espr);
In questo caso viene eseguita istruzione e successivamente
controllato se espr risulta vera, nel qual caso il ciclo viene ripetuto.
Come sempre l'iterazione può comprendere una
istruzione composta.
È bene precisare che in un blocco for, while
o do...while, così come nel blocco if, può essere presente un numero qualsiasi
di istruzioni di ogni tipo ivi compresi altri blocchi for, while o do...while.
I cicli possono cioè essere annidati.