Fondamenti di Informatica
Dino Mandrioli
Presentazione del corso
Domandina sfiziosetta
(rivolta non solo agli ingegneri informatici)
• Questo è un corso di laurea (del Politecnico)
in Ingegneria Informatica
• Quale delle due parole del titolo prevale?
• Il sostantivo o l’aggettivo?
• Anzi, già che ci siamo …
– Parliamo un po’ dell’Ingegneria
dell’Informazione
Obiettivi e organizzazione del corso
• Fornire un’introduzione all’informatica con enfasi sulle
basi concettuali e sull’apprendimento integrato
<Concetti-sperimentazione>
• Al corso seguiranno corsi di approfondimento e
applicazione
• Integrazione <Lezioni-esercitazioni-laboratorio-verifica>
Traccia del programma
• Concetti introduttivi (e fondamentali)
• Breve panoramica dei sistemi e applicazioni
dell’informatica
• Gli elementi essenziali della programmazione
(linguaggio di riferimento: C)
• Alcuni aspetti più avanzati forse diversificati per CS
(programmazione di sistema vs. CAD)
Aspetti organizzativi
• Lezioni: tradizionali ma … interattive: concetti, esempi
base
• Esercitazioni: ulteriori esempi/esercizi
• Laboratorio: messa in pratica. Enfasi sull’atteggiamento
attivo da parte dello studente, rispetto alla didattica
tradizionale.
• Verifica “on-line” o “in itinere”:
– il laboratorio ammette all’esame e dà un punteggio (se positivo) da
1 a 5 punti
– 2 prove scritte intermedie (con eventuale orale) danno punti fino a
14 (7 è il minimo di sufficienza)
– Alla fine del corso verrà proposto un voto finale (se >= 18); il voto
proposto potrà essere rifiutato secondo modalità definite dal
docente
– Chi non è ammesso all’esame dovrà rifrequentare il corso l’anno
successivo
– Chi non avrà ottenuto un voto finale sufficiente o rifiuterà il voto
proposto dovrà sostenere un recupero negli appelli previsti. Non
sono ammessi recuperi di singole prove.
Materiale didattico
• Libro di testo:
Ceri, Mandrioli, Sbattella: “Informatica:
Programmazione”, II edizione, Mc-Graw-Hill Italia
• (Parti di)
Pelagatti: Informatica II, Sistema operativo Linux e
TCP/IP, Esculapio, 2002.
• File dei lucidi accessibile via web
• Eserciziari e temi d’esame di corsi precedenti
Testi di esercizi in C ampiamente disponibili
I concetti fondamentali dell’informatica
• Informatica: scienza della rappresentazione ed
elaborazione rigorosa - quindi potenzialmente automaticadell’informazione
• (non scienza del calcolatore né di Internet, …)
• ---->
• Informatica coeva e “sorella” della matematica … e della
filosofia (radici storiche nella cultura classica ellenistica;
importanti risultati teorici e di base all’inizio del 900)
• Tuttavia ….
• ...
• Il grande impatto applicativo, industriale, sociale
dell’informatica:
– Calcolo, gestione, informatica personale, web, controllo di
impianti, applicazioni mediche, …. Con i relativi rischi!..
• Determinato dall’evoluzione tecnologica:
– elettronica
– miniaturizzazione
– trasmissione (telecomunicazioni)
• Le “rivoluzioni qualitative” prodotte dagli enormi sviluppi
quantitativi (un moderno PC è enormemente più potente di
un calcolatore da decine di miliardi - metri cubi- degli anni
60)
– Si va verso l’informatica “globale” e … spesso “nascosta”
(embedded)
Cominciamo dall’abc (senza snobbarlo …)
• … rappresentazione (ed elaborazione) rigorosa (ossia
meccanizzabile) dell’informazione:
• Il primo tipo di informazione che si presta ad essere
rigorosamente rappresentato è l’informazione numerica:
numeri rappresentati mediante:
– aste, fagioli, …
– cifre greco-romane
– cifre decimali (o in altra base)
• Una prima fondamentale differenza quantitativa tra le
diverse tecniche di rappresentazione:
– n aste per rappresentare il numero n (numerazione unaria)
– logk(n)  + 1 cifre per rappresentare il numero n in base k
La rappresentazione dell’informazione non numerica
(Osservazioni preliminari)
• Informazione testuale (caratteri)
• Informazione grafica (pixel, ma anche forme più …
astratte)
• Informazione musicale (digitalizzata o no)
• …. Multimedialità …
• NB: Fondamentale distinguere tra rappresentazione
dell’informazione in forma analogica e digitale:
L’elettronica - e l’informatica- moderne sono ormai
assestate sulle tecniche digitali, però in un passato anche
recente ...
Ma rappresentare l’informazione non basta: occorre
elaborarla (sempre rigorosamente, ossia precisamente,
ossia … “meccanizzabilmente”)
• Ricominciamo dai numeri (45 + 25) è definita
rigorosamente …
• Un primo “calcolatore”:
(a)
(b)
Figura 1.1 Configurazione del pallottoliere (a) prima e (b) dopo l’esecuzione della somma
• Nel fare il calcolo della somma mediante il pallottoliere,
noi applichiamo un algoritmo, ossia una sequenza di passi
elementari, ben definita, precisa, eventualmente eseguibile
anche da una macchina.
• Informazione ed elaborazioni complesse vengono sempre
scomposte in elementi base semplici e aggregate mediante
composizione di elementi semplici in altri sempre più
complessi.
• Questa è l’essenza della progettazione informatica!
• Irrobustiamo ora il concetto di algoritmo mediante nuovi
esempi.
Utilizzo di un lettore portatile di CD musicali
Consideriamo un lettore di CD musicali portatile, con un
certo numero di pulsanti di controllo e un display che
indica se il lettore è in funzione e in particolare qual è il
brano che è attualmente riprodotto. Vogliamo suonare il
brano numero 13; le operazioni da eseguire per svolgere
questo compito costituiscono un algoritmo:
• Se siamo a casa ed è disponibile una presa elettrica, inseriamo
l’alimentatore nella presa.
• Se non è disponibile una presa, controlliamo che il lettore contenga
l’appropriato numero di batterie e che queste siano sufficientemente
cariche; in caso contrario inseriamo o sostituiamo le batterie con altre
nuove.
• Accendiamo il lettore.
• Inseriamo il CD nel lettore; il display indica “No disk”.
• Premiamo il pulsante “Start” e attendiamo finché il display non indica
“Disk OK”.
• Premiamo ripetutamente il pulsante “Forward” finché il display non
indica il numero del brano voluto (13).
• Indossiamo le cuffie.
Alla fine giungiamo ad ascoltare il brano prescelto.
Anche in questo caso, abbiamo costruito il nostro algoritmo combinando
opportunamente poche operazioni elementari:
inserire il disco,
premere diversi pulsanti,
controllare quanto visualizzato dal display, indossare le cuffie.
Si osservi inoltre che l’ordine in cui tali operazioni vengono eseguite può
dipendere dai risultati (parziali) dell’esecuzione stessa:
dobbiamo inserire nella presa l’alimentatore se si verifica la disponibilità di
una presa elettrica; dobbiamo ripetere l’azione di premere un pulsante finché
non è verificata una determinata condizione (il fatto che il display indichi
13). La possibilità di decidere quale operazione eseguire sulla base
dell’analisi dello stato dell’esecuzione è una caratteristica essenziale di ogni
algoritmo non banale.
Il precedente algoritmo è una versione semplificata di ciò che facciamo in
pratica e non tiene conto di numerose variabili.
Per esempio, la decisione di usare la presa elettrica piuttosto che le
batterie può dipendere da parecchi altri fattori diversi da quelli
esemplificati qui; se il CD non viene inserito correttamente nel lettore,
anche dopo aver premuto il pulsante “Start” il disco non gira come
dovrebbe e non compare l’indicazione “Disk OK”. Se succede proprio
questo, si deve ripetere il passo 5, come segue:
•Premiamo il pulsante “Start”; fintanto che il display indica “No
disk”, si ripete:
•Inseriamo nuovamente il CD nel lettore.
•Premiamo il pulsante “Start”.
...
Anche questa nuova versione dell’algoritmo è probabilmente troppo
semplificata.
In realtà, anche il più testardo di noi, dopo diversi tentativi andati male,
concluderebbe che il lettore non funziona:
Lezione #1:
gli esseri umani sono buoni esecutori di algoritmi, ma possono anche decidere
di abbandonarli (per esempio, in condizioni eccezionali) usando il buon senso.
I calcolatori, invece, non possiedono buon senso e intuizione; tutte le
situazioni fuori dal normale devono essere loro descritte, se si vogliono
ottenere reazioni appropriate.
Altri algoritmi -e lezioni- più o meno dettagliati
• La somma tra due numeri:
– in versione unaria (pallottoliere)
– in versione decimale -o, in generale, in base k > 0
• Le ricerca in uno schedario:
– non ordinato
– ordinato -e.g, in ordine alfabetico
• Lezione # 2 (e 3):
• gli algoritmi dipendono dalla rappresentazione dei dati (informazioni)
prescelte: rappresentazione ed elaborazione dell’informazione
procedono “a braccetto”;
• gli algoritmi possono essere più o meno efficienti: si pensi alla
differenza tra:
– operazioni unarie e in base k > 1;
– ricerca su schedari non ordinati e ordinati (la guida del telefono)
• In questo corso non valuteremo in modo matematico l’efficienza degli
algoritmi ma tale valutazione va tenuta presente almeno sul piano
intuitivo.
Consultazione di una carta geografica
(per decidere percorsi automobilistici)
• In genere, la selezione di un luogo di villeggiatura non obbedisce a un
rigido algoritmo.
• Al contrario, il problema di trovare la via più breve per andare in auto
da una città all’altra può essere risolto mediante un algoritmo.
1.
Si trovano tutte le sequenze di città che determinano un itinerario tra le
due città. Più precisamente, siano cp e ca rispettivamente la città di
partenza e la città di arrivo: si individuano e si memorizzano, ognuna su
un diverso pezzo di carta, tutte le sequenze {c0, c1, c2, …, ck} di nomi di
città, tali che:



2.
3.
nessuna città compaia due volte nella sequenza;
il primo elemento della sequenza, c0, coincida con cp, e l’ultimo, ck,
coincida con ca;
per ogni coppia di elementi contigui, <ci, ci+1>, esista un tratto di strada
che li unisce; il tratto di strada non può ovviamente attraversare alcuna
città.
Per ogni sequenza, si calcola la somma delle distanze dei vari tratti di
strada e la si memorizza accanto alla sequenza.
Si individua la sequenza per cui il valore della somma delle distanze è
minimo e la si sceglie come strada più breve. Se per caso si trovasse più di
un itinerario con la stessa distanza totale tra cp e ca, se ne sceglierebbe uno
arbitrariamente (per esempio il primo trovato). Se non si trova alcun
itinerario, per esempio perché cp e ca sono separate dal mare, non esiste
alcuna soluzione.
Le operazioni utilizzate nei precedenti punti 1, 2, 3
(individuare sequenze di tratti di strada, calcolare la
sommatoria di varie distanze ecc.) sono piuttosto complesse, e
forse non sufficientemente dettagliate.
Tuttavia, non dovrebbe essere un esercizio troppo difficile
precisare meglio almeno i punti 2 e 3 facendo uso di
operazioni più elementari, fino ad arrivare a una formulazione
che sia così precisa da poter essere eseguita.
Il punto 1 merita invece un esame più approfondito. Trovare
tutti gli itinerari che vanno da una città all’altra è a sua volta un
problema che va risolto mediante un opportuno algoritmo:
possiamo chiamarlo un sottoproblema del problema principale,
perché la sua soluzione è necessaria per costruire la soluzione
di quest’ultimo. Questo sottoproblema, a sua volta, può essere
risolto mediante un algoritmo:
Se n è il numero di città riprodotte nella carta geografica, un
itinerario che unisca cp a ca senza passare due volte (inutilmente!)
dalla stessa città, non può essere costituito da un numero di tratti
elementari superiore a n1:
Basta quindi costruire tutti gli itinerari che partono da cp e hanno
un numero di tratti non superiore a n1, e scegliere tra questi
quelli che terminano in ca.
Supponiamo di aver già trovato tutti gli itinerari che partono da cp
e sono lunghi r–1 tratti; otterremo gli itinerari lunghi r creando
molte copie degli itinerari lunghi r–1 e aggiungendo a ciascuna
copia un tratto ulteriore, che collega l’ultima città, cr–1, a tutte le
città direttamente collegate a essa, purché tali città non facciano
già parte della sequenza lunga r1.
Consideriamo come itinerario di lunghezza 0 un itinerario fittizio,
che parte e arriva nella città cp.
A questo punto, gli itinerari di lunghezza 1 saranno tutti quelli che
collegano cp alle città limitrofe, cioè a quelle città direttamente
collegate a cp da una strada. Abbiamo così definito i primi due
passi di un algoritmo, che in n passi ci porta a generare tutti i
percorsi lunghi n1 che partono da cp, e quindi a risolvere il punto
1 dell’algoritmo generale.
Questo ragionamento è un primo esempio di un potentissimo
procedimento matematico-informatico che ci servirà per risolvere i
più svariati problemi: l’induzione.
Otteniamo dunque una nuova formulazione più completa e più
precisa del punto 1 del nostro algoritmo:
1.
2.
Partiamo da una sequenza iniziale, trascritta su un foglio di carta, che
comprende la sola città cp; questa sequenza ha lunghezza 0.
Esaminiamo le sequenze fin qui costruite, di lunghezza r1. Chiamiamo S
una generica sequenza {cp, c1, c2, …, cr–1}.
Per ciascuna sequenza S si costruiscono nuove sequenze di lunghezza r
come segue:
 sia cr–1 l’ultima città della sequenza S. Si trovano tutte le città collegate a cr–1
da una strada: siano esse l’insieme di città {a1, a2, …, as};
 si escludono dall’insieme {a1, a2, …, as} tutte le città che già fanno parte della
sequenza S. Restano così {a1, a2, …, at} città, con t  s;
 si costruiscono t sequenze di lunghezza r, ottenute aggiungendo a S una
diversa città ai scelta fra le t città individuate al passo precedente.
3
Si ripete l’esecuzione del passo precedente fino a quando non si verifica la
condizione r = n. A questo punto vengono estratte (da tutte le sequenze
costruite di lunghezza minore di n) le sequenze che hanno ca come ultima
città, e l’algoritmo che identifica tutti i possibili tragitti termina. Se per
caso non ve ne fossero, si può terminare a questo punto l’intero algoritmo,
stabilendo che “il problema non ha soluzione”.
C
3
D
2
1
3
A
F
3
4
1
1
B
2
E
G
Città collegate ad A da una strada
Città collegate a B da una strada
Città collegate a C da una strada
Città collegate a D da una strada
Città collegate a E da una strada
Città collegate a F da una strada
Città collegate a G da una strada
{B}
{A, E, F}
{D, E, F}
{C, F}
{B, C, G}
{B. C, D, G}
{E, F}
Percorsi di 0 tratti che partono da E
Percorsi di 1 tratto che partono da E
Percorsi di 2 tratti che partono da E
Percorsi di 3 tratti che partono da E
{E}
{E, B} {E, C} {E, G}
{E, B, A} {E, B, F} {E, C, D} {E, C, F} {E, G, F}
{E, B, F, C} {E, B, F, D} {E, B, F, G} {E, C, D, F} {E, C, F, B}
{E, C, F, D} {E, C, F, G} {E, G, F, B} {E, G, F, C} {E, G, F, D}
{E, B, F, C, D} {E, B, F, D, C} {E, C, D, F, B} {E, C, D, F, G}
{E, C, F, B, A} {E, G, F, B, A} {E, G, F, C, D} {E, G, F, D, C}
Percorsi di 4 tratti che partono da E
Lunghezza del percorso {E, C, D}
Lunghezza del percorso {E, B, F, D}
Lunghezza del percorso {E, C, F, D}
Lunghezza del percorso {E, G, F, D}
Lunghezza del percorso {E, B, F, C, D}
Lunghezza del percorso {E, G, F, C, D}
=
=
=
=
=
=
6
7
6
3
11
7
Percorso più breve
Lezioni ricavate:
• Certamente noi non consultiamo le carte geografiche -solo- in questa
maniera
• Un problema complesso si può risolvere scomponendolo in problemi
meno complessi fino ad arrivare a problemi elementari
• Quali siano i problemi elementari (risolvibili immediatamente) dipende
da chi/ che cosa è il risolutore
• Trovato un algoritmo non è detto che questo sia l’unico né tantomeno
il migliore per risolvere il nostro problema (nel caso specifico ne
esistono certamente di migliori da vari punti di vista).
• Quando potremo stabilire che l’algoritmo da noi elaborato è eseguibile
da una macchina? ---> Dobbiamo conoscere, almeno un po’, la
macchina!
La “macchina” da calcolo -in senso lato e astratto(cominciando dal basso)
• L’essenza dell’informatica sta nello scomporre
l’informazione in “pezzi” elementari e la sua elaborazione
in operazioni elementari:
• Che cosa più elementare del bit, ossia di due possibili
valori dell’informazione: {0,1} o {Vero, Falso}, …?
• Tutta l’informazione -discreta- può essere scomposta ossia rappresentata come una opportuna sequenza di 0 e 1
(e quella continua può essere approssimata in questa
maniera)
Primi esempi di rappresentazione di informazioni
componendo bit
•
•
•
•
•
•
•
Byte: sequenza di 8 bit: (00000000, 00000001, 00000010, …, 11111111).
Un byte può rappresentare i numeri naturali da 0 a 255 (= 28 –1):
zero = 00000000; 8 = 00001000; …; 255 = 11111111.
… e i numeri interi compresi fra –127 e 127, ossia fra –(2(8–1)  1) e (2(8–1) 
1) (primo bit = 0: numero positivo, primo bit = 1: numero negativo; 0 =
00000000 e 0 = 10000000
(nei prossimi lucidi vedremo una rappresentazione più efficace)
I numeri reali: numeri razionali contenenti una parte intera e una frazionaria
che approssimano il numero reale con precisione arbitraria. Notazione in
virgola fissa: si codificano separatamente la parte intera e la parte frazionaria:
ad esempio:
8.345 ( in base due):
primo byte (rappresentazione dell’intero 8) = 00001000
secondo byte (rappresentazione della parte frazionaria 0.345) = 01011000
(anche qui vedremo ulteriori rappresentazioni in seguito)
• I caratteri ASCII (American Standard Code for Information
Interchange):
• sette bit usati per rappresentare 128 caratteri (ottavo per controllo).
• a ogni lettera (le maiuscole da A a Z, le minuscole da a a z), cifra (da 0
a 9) o separatore (usato per la punteggiatura o come operatore
aritmetico) viene assegnato un numero naturale rappresentabile in
forma binaria.
• Ad esempio “A” viene codificata in ASCII come numero 65 e la sua
forma binaria è 01000001; il separatore “;” viene codificato come 59
e la sua forma binaria è 00111011.
• NB: la stessa stringa di bit ha diversi significati, a seconda del tipo di
informazione rappresentata!
Dall’informazione elementare alle operazioni elementari:
• Inversione di un bit: 0 ---> 1, 1 ---> 0
• “somma” di due bit: 0+0 = 0, 1+0 = 1, 0+1 = 1, 1+1 = … 1
• “prodotto” di due bit 0*0 = 0, 1*0 = 0, 0*1= 0, 1*1 = 1
• Se interpretiamo 0 come Falso (False, F) e 1 come Vero (True, T), le
operazioni di dui sopra possono essere viste come operatori logici:
• Inversione = negazione, (NOT, )
• Somma logica, (OR, )
• Prodotto logico, (AND, )
• Mediante le suddette operazioni si possono elaborare sequenze di bit in
modo arbitrario ----> operazioni base per costruire algoritmi
• Oltre all’intuitiva interpretazione logica queste operazioni sono
facilmente realizzabili mediante dispositivi elettronici (a loro volta
elementari e combinabili in circuiti integrati)
Un piccolo approfondimento: l’aritmetica binaria
• Il fatto che l’informazione base sia il bit porta a codificare i numeri
come sequenze di bit
• Ne consegue l’adozione della numerazione in base 2:
– 0 = 000; 1 = 001; 2 = 010; 3 = 011; ….
– algoritmi di conversione: 102 : 10/2 = 5; resto = 0
–
5/2 = 2; resto = 1
–
2/2 = 1; resto = 0
–
1/2 = 0; resto = 1
–
102 : = 1010
– (1010)10 = 1. 23 + 0. 22 + 1. 21 + 0. 20 = 8 + 0 + 2 + 0 = 10
• Da base due a base 8 (o 16) e viceversa + facile. Perché?
0
1
0
1
Aritmetica binaria: la somma cifra per cifra
riporto
risultato
11100
00110000000100
8731
10001000011011
5698
01011001000010
14429
11100001011101
Aritmetica binaria realizzata mediante operatori logici
•
•
•
•
•
•
•
8 + 9 = 7, “riporto” 1
1 + 0 = 1, riporto 0
1 + 1= 0, riporto 1
riporto 1 + 7 + 6 = 4,
riporto 1 + 0 + 0 = 1,
riporto 1 + 1 + 0 = 0,
riporto 1 + 1 + 1 = 1,
riporto 1
riporto 0
riporto 1
riporto 1
Risultato
B1
B1
Half adder
B2
B2
Risultato
Full adder
Riporto
Riporto
Riporto
Half adder: Risultato = (NOT(B1) AND B2) OR (B1 AND NOT(B2))
Riporto = B1 AND B2
Torniamo ai numeri negativi
• … e i numeri interi compresi fra –127 e 127, ossia fra –(2(8–1)  1) e
(2(8–1)  1) (primo bit = 0: numero positivo, primo bit = 1: numero
negativo; 0 = 00000000 e 0 = 10000000
• a parte lo “spreco” di due rappresentazioni diverse per un solo numero
• la realizzazione delle varie operazioni (e.g. differenza) richiede
algoritmi nuovi e non “leggerissimi”
•  una tecnica di rappresentazione più efficace: il complemento a due:
• Usando m bit, -N rappresentato come 2m – N: Esempio, m = 3
–
–
–
–
–
–
–
–
- 4 = 100
- 3 = 101
- 2 = 110
- 1 = 111
0 = 000
1 = 001
2 = 010
3 = 011
(NB: non rappresentabile con modulo e segno)
Somma e sottrazione in complemento a due
• Passare da N a – N in complemento a due:
• Scambio 1  0; sommo 1 al risultato (facile ed efficiente realizzazione
con operazioni logiche):
– 3  - 3: 011  100  101
– -2  2: 110  001  010
– …
• M - N = M + (-N) (occhio all’overflow!)
+ 5
0000101
+ 5
0000101
+ 8
0001000
-8
1111000
+ 13
0001101
-3
1111101
-5
1111011
- 64
1000000
+8
0001000
-8
1111000
+3
(1) 0000011
- 72
[1] (1) 0111000
I numeri frazionari
•
•
•
0.58710 = (5  10–1 + 8  10–2 + 7  10–3)
0.10112 = (1  2–1 + 0  2–2 + 1  2–3 + 1  2–4) 10 = 0.687510
I numeri frazionari possono introdurre approssimazioni, dovute alla presenza di un
numero limitato di cifre dopo la virgola; l’approssimazione è comunque inferiore a
p–n, ove n è il numero di cifre utilizzate.
•
•
•
•
•
•
•
•
•
•
Rappresentazione binaria del numero frazionario 0.58710:
0.587  2 = 1.174: parte frazionaria 0.174 e parte intera 1
0.174  2 = 0.348: parte frazionaria 0.348 e parte intera 0
0.348  2 = 0.696: parte frazionaria 0.696 e parte intera 0
0.696  2 = 1.392: parte frazionaria 0.392 e parte intera 1
0.392  2 = 0.784: parte frazionaria 0.784 e parte intera 0
0.784  2 = 1.568: parte frazionaria 0.568 e parte intera 1
0.568  2 = …
= 0.1001 (con quattro cifre binarie dopo la virgola) o
= 0.100101 (con sei cifre binarie dopo la virgola).
I numeri reali
• Approssimati da razionali
• Dalla virgola fissa:
– 00101001011.10110 = 331.6875 in base 10
• alla virgola mobile:
–
–
–
–
–
–
mantissa e esponente o caratteristica:
r = m  bn
esempi: con b = p = 10:
-331.6875 viene rappresentato in virgola mobile con
m = 0.3316875 e n = 3
con b = p = 2, bit di segno della mantissa 0, mantissa 1011 e
caratteristica 01010
– viene interpretato in base decimale come:
– 0.6875  210 = 0.6875  1024 = 704.01
• Un numero in virgola mobile si dice
normalizzato se la virgola della mantissa è
posizionata subito a sinistra della prima
cifra diversa da 0.
– +0.45676  102 normalizzato
– +0.0456  104 non normalizzato.
– i numeri non normalizzati rischiano di
“sprecare” cifre:
• 456.7682 con cinque cifre decimali per la mantissa:
– 456.76  101
– 4.5676  102
– 0.0456  104
Lo standard IEEE per la rappresentazione in virgola mobile
• 4 formati per la rappresentazione dei numeri
reali che differiscono per il numero totale di
bit utilizzati. I più diffusi:
– a precisione singola, con 32 bit
– a doppia precisione, con 64 bit
Rappresentazione IEEE in singola precisione.
• L’interpretazione abbastanza particolare:
– lo standard deve permettere di rappresentare
non solo un insieme di numeri reali quanto più
ampio possibile, con la massima precisione
possibile,
– ma anche valori speciali quali
• NaN – “Not a Number” : risultato di operazioni non
ammesse quali la divisione per zero,
• +∞ e -∞
• Interpretazione della caratteristica n
– valore compreso tra 0 e 255 (8 bit)
– i valori estremi (0 e 255) sono interpretati in una
maniera speciale,
– i valori compresi tra 1 e 254 (inclusi) sono interpretati
come esponente del numero razionale sottraendo 127 al
valore effettivamente rappresentato:
• se n=131 allora l’esponente del numero rappresentato vale 4,
mentre se n=125 l’esponente vale -2. In questo modo è
possibile rappresentare tanto esponenti positivi quanto
negativi, interpretando il numero effettivamente rappresentato
come un intero senza segno (cosa che facilita i confronti).
• Interpretazione della mantissa
– rappresentata in forma normalizzata su 23 bit
ignorando il primo bit:
– il primo bit non può che valere 1 (come
vedremo il valore 0 ha una sua
rappresentazione specifica).
– Trascurando quindi il primo bit e
rappresentando solo la parte frazionaria della
mantissa, si ottiene di fatto l’equivalente di 24
bit.
• Casi speciali:
– Il valore 0 viene rappresentato ponendo a 0 tanto la
caratteristica quanto la mantissa (a seconda del valore
del bit del segno è possibile rappresentare i valori -0 e
+0).
– Il valore 255 per la caratteristica viene utilizzato per
rappresentare il valore “infinito” (positivo o negativo a
seconda del valore del segno) se la mantissa vale 0, o il
valore speciale NaN se la mantissa è diversa da 0.
– Se la caratteristica vale 0 e la mantissa è diversa da 0,
quest’ultima viene interpretata come se si trattasse di
una parte puramente frazionaria (primo bit uguale a 0,
quindi non normalizzato) e l’esponente viene posto per
convenzione a -126.
• Riassumendo:
– detto s il bit del segno, n la caratteristica, ed m la mantissa, valgono le
seguenti regole per il calcolo del valore v rappresentato:
– se n=255 e m≠0 allora v=NaN
– se n=255 e m=0 e s=0 allora v=+∞
– se n=255 e m=0 e s=1 allora v=-∞
– se 0<n<255 allora v=(-1)s2(n-127) 1.m
– se n=0 e m≠0 allora v=(-1)s2-126 0.m
– se n=0 e m=0 e s=0 allora v=+0
– se n=0 e m=0 e s=1 allora v=-0
• 1.m denota il numero binario razionale composto da un 1, seguito dal
punto decimale, seguito dai bit presenti nella parte riservata alla mantissa
• 0.m denota il numero composto da uno 0, seguito dal punto decimale,
seguito dai bit che compongono la mantissa.
• La rappresentazione in doppia precisione è del tutto analoga
ma usa 11 bit per la caratteristica e 52 bit per la mantissa.
Un calcolatore, o meglio, un sistema informatico
• Altro non è che un enorme “tritabit”
• Solo che i bit sono organizzati in “pacchetti di informazioni” (byte,
“parole” e via di seguito)
• e … viaggiano all’interno del sistema; il quale sistema può essere un
semplice “chip” o l’intera rete Internet.
• Cerchiamo di capire a grandissime linee come ciò accade per poter
meglio fornire disposizioni al sistema (algoritmi) su come elaborare
l’informazione che esso riceve.
• Lo studio più sistematico del sistema di calcolo sarà oggetto del corso
successivo (Informatica 2)
La classica “architettura” -molto astratta- della macchina di
von Neumann
NASTRO DI LETTURA
....
MEMORIA
M1
M2
M3
ACCUMULATORE
UNITA`
ARITMETICA
UNITA` CENTRALE
....
NASTRO DI SCRITTURA
.
.
Il programma: ovvero il modo con cui un algoritmo viene
comunicato a un calcolatore
• Per comunicare (tra uomini o macchine) occorre un linguaggio
• Se Maometto non va alla montagna …
• L’uomo deve usare un linguaggio accessibile alla macchina, ossia
rigoroso, preciso …: così non è il linguaggio naturale
• Il linguaggio della macchina di von Neumann:
– Un programma è una sequenza di istruzioni
– Un’istruzione è costituita da un campo codice operativo (CO) e da un
(eventuale) campo operando
– Il CO dice alla macchina che operazione deve eseguire
– L’operando dice a che cosa va applicata l’operazione
Tanto per cominciare:
• L’istruzione READ:
NASTRO
LETTURA
12
....
NASTRO
LETTURA
12
....
DI
DI
MEMORIA
M1
MEMORIA
M1
M2
M2
M3
M3
12
67
UNITA`
ARITMETICA
UNITA`
CENTRALE
.
.
UNITA`
CENTRALE
....
NASTRO
SCRITTURA
UNITA`
ARITMETICA
.
.
DI
....
NASTRO
SCRITTURA
DI
Altre istruzioni: trasferiscono informazioni da un punto
all’altro della macchina (sistema)
• WRITE
• LOAD xxx: il contenuto della cella # xxx viene trasferito nell’accumulatore
• STORE xxx: il contenuto dell’accumulatore viene trasferito nella cella # xxx
• Un primo programma (legge due numeri e li riscrive in ordine inverso):
•
•
•
•
•
•
READ
STORE 30
READ
WRITE
LOAD 30
WRITE
Altre istruzioni eseguono operazioni aritmetiche (applicando
opportuni algoritmi “cablati” nell’unità aritmetica)
• ADD, SUB, MULT, DIV (divisione intera)
• ADD
X: [ACC] + [X] ---> [ACC]
• Un secondo programma (legge due numeri e ne stampa la somma):
•
•
•
•
•
READ
STORE 30
READ
ADD
30
WRITE
Altre istruzioni controllano il flusso dell’esecuzione:
Una caratteristica fondamentale degli algoritmi è la possibilità
di decidere il da farsi in funzione dei dati acquisiti (letti o
prodotti)
• Nella macchina di von Neumann questa caratteristica è ottenuta mediante
istruzioni di salto:
• BR
xx : la macchina salta direttamente ad eseguire l’istruzione xxesima, indipendentemente dall’istruzione attuale
• BEQ
xx : salto condizionato: la macchina salta direttamente ad eseguire
l’istruzione xx-esima, se il contenuto dell’accumulatore è 0, altrimenti
prosegue con l’istruzione successiva
• BGE, BG, …: altre forme di salto condizionato dipendenti dal contenuto
dell’accumulatore
• Indirizzamento immediato:
• ADD= 13: viene sommato il valore 13 al contenuto dell’accumulatore, non il
contenuto della cella 13!
Un terzo programma: Sommatoria di n numeri
•
In primo luogo il programma legge il numero n, che è scritto nella prima
cella del nastro di lettura, e lo memorizza nella cella 101: questa ci
servirà per contare quanti numeri dobbiamo ancora leggere e sommare
prima di terminare; ovviamente il suo valore iniziale è proprio n. Inoltre
si assegna il valore iniziale 0 (in gergo “si inizializza”) alla cella 102 che
utilizzeremo per contenere il risultato della sommatoria. Si noti che in
questo modo si definisce che la sommatoria di 0 elementi è 0.
•
•
•
•
0
1
2
3
•
A questo punto ci si domanda se il contenuto della cella 101 è 0.
•
•
4
5
READ
STORE 101
LOAD= 0
STORE 102
LOAD
BEQ
101
13
Se ci sono ancora dati da leggere e sommare, si legge il prossimo
elemento del nastro di lettura e lo si somma al contenuto della cella 102,
destinata a contenere il risultato, rimettendo poi il risultato della somma
nella stessa cella.
6
7
8
READ
ADD 102
STORE 102
Successivamente si diminuisce di un’unità il contenuto della cella 101 (il
contatore del numero di operazioni lettura-somma ancora da eseguire).
9
10
11
LOAD 101
SUB= 1
STORE 101
A questo punto non c’è che domandarsi di nuovo se i dati da leggere sono
finiti e procedere esattamente come prima, ma il codice per fare ciò è già
stato scritto e inizia dall’istruzione numero 4; basta dunque scrivere:
12
BR
4
Infine dobbiamo pensare a scrivere il risultato e a terminare la
esecuzione, quando finalmente la cella contatore conterrà il valore 0.
13
14
15
LOAD 102
WRITE
END
Riscrittura di una sequenza di numeri -diversi da 0 e “chiusa”
da uno 0-in ordine inverso:
Serve qualcosa di nuovo:
Indirizzamento indiretto:
LOAD@
xxx: viene trasferito in accumulatore il contenuto
della cella il cui indirizzo è a sua volta contenuto nella cella
xxx
Quarto programma:
Cella 101:
Cella 102:
0
1
2
3
contatore numeri letti
indirizzo celle contenenti i numeri letti
(indirizzo di partenza: 110)
LOAD= 0
STORE 101
LOAD= 110
STORE 102
inizializzazione del contatore
inizializzazione indirizzo di memorizzazione
Inizia la fase di lettura. Ogni dato letto, se è diverso da 0, viene memorizzato
nella cella indicata dal valore contenuto in 102. Successivamente il contenuto
della cella 102 viene incrementato.
4
5
6
7
8
9
10
11
12
13
READ
BEQ
14
STORE@102
LOAD
101
ADD=
1
STORE 101
LOAD
102
ADD=
1
STORE 102
BR
4
salto alla fine del ciclo
incremento del contatore
incremento dell’indirizzo
ritorno all’inizio del ciclo
NB: Al momento della scrittura dell’istruzione 5 non si conosce l’indirizzo
dell’istruzione cui saltare!
Una volta terminata la lettura, cioè dopo aver letto il dato 0, iniziamo la fase di
scrittura. La struttura di questa parte del programma è simile alla precedente.
Questa volta il test di controllo non è più effettuato sul valore letto, ma sul
contenuto del contatore 101 che contiene il numero di dati da scrivere. Ogni
volta che viene scritto un numero, il contenuto di 101 deve essere decrementato
di 1 fino al valore 0. Anche il contenuto di 102 deve essere decrementato di 1 per
ogni dato scritto per permettere di accedere al dato successivo.
14
15
16
17
18
19
20
21
22
23
24
25
LOAD 101
BEQ
25
LOAD 101
SUB= 1
STORE 101
LOAD 102
SUB= 1
STORE 102
LOAD@ 102
WRITE
BR
14
END
salto alla fine del ciclo
decremento del contatore
decremento dell’indirizzo
ritorno all’inizio del ciclo
Nulla di magico avviene nella macchina di von Neumann: guardiamoci
dentro un po’ meglio (il seguito al corso successivo):
NB:
• Noi, per intenderci, abbiamo usato una
formulazione simbolica del codice e
decimale degli indirizzi.
• In realtà la memoria reale della macchina
sarà qualcosa del genere:
0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 leggi un valore in ingresso e ponilo nella cella numero 16 (variabile a)
0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 1 leggi un valore e ponilo nella cella numero 17 (variabile b)
0 1 0 0 0 0 0 0 0 0 0 1 0 0 1 0 leggi un valore e ponilo nella cella numero 18 (variabile c)
0 1 0 0 0 0 0 0 0 0 0 1 0 0 1 1 leggi un valore e ponilo nella cella numero 19 (variabile d)
0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 carica il registro A con il contenuto della cella 16 (valore di a)
0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 1 carica il registro B con il contenuto della cella 17 (valore di b)
0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 somma i contenuti dei registri A e B
0 0 1 0 0 0 0 0 0 0 0 1 0 1 0 0 copia il contenuto del registro A nella cella numero 20 (risultato parziale)
0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 carica il registro A con il contenuto della cella 18 (valore di c)
0 0 0 1 0 0 0 0 0 0 0 1 0 0 1 1 carica il registro B con il contenuto della cella 19 (valore di d)
0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 somma i contenuti dei registri A e B
0 0 0 1 0 0 0 0 0 0 0 1 0 1 0 0 carica il registro B con il contenuto della cella 20 (risultato parziale)
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 moltiplica i contenuti dei registri A e B
0 0 1 0 0 0 0 0 0 0 0 1 0 1 0 0 copia il contenuto del registro A nella cella numero 20 (risultato)
0 1 0 1 0 0 0 0 0 0 0 1 0 1 0 0 scrivi in output il contenuto della cella numero 20 (risultato)
1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 arresta l’esecuzione del programma
…………………………………. spazio per la variabile a (cella 16)
…………………………………. spazio per la variabile b (cella 17)
…………………………………. spazio per la variabile c (cella 18)
…………………………………. spazio per la variabile d (cella 19)
…………………………………. spazio per il risultato (cella 20)
(a)
0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 cella numero 0
0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 1 cella numero 1
0 1 0 0 0 0 0 0 0 0 0 1 0 0 1 0 cella numero 2
0 1 0 0 0 0 0 0 0 0 0 1 0 0 1 1 cella numero 3
0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 cella numero 4
0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 1 cella numero 5
0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 cella numero 6
0 0 1 0 0 0 0 0 0 0 0 1 0 1 0 0 cella numero 7
0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 cella numero 8
0 0 0 1 0 0 0 0 0 0 0 1 0 0 1 1 cella numero 9
0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 cella numero 10
0 0 0 1 0 0 0 0 0 0 0 1 0 1 0 0 cella numero 11
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 cella numero 12
0 0 1 0 0 0 0 0 0 0 0 1 0 1 0 0 cella numero 13
0 1 0 1 0 0 0 0 0 0 0 1 0 1 0 0 cella numero 14
1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 cella numero 15
1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 cella numero 16 riservata per la variabile a
1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 cella numero 17 riservata per la variabile b
1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 cella numero 18 riservata per la variabile c
1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 cella numero 19 riservata per la variabile d
1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 cella numero 20 riservata per il risultato
1101000000000000
1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 celle di memoria libere
1101000000000000
1101000000000000
1101000000000000
(b)
• …E l’evoluzione dello stato della macchina
qualcosa del genere:
Uno sguardo panoramico all’intero “sistema informatico” e
alle sue applicazioni
• Un po’ di terminologia di largo uso (e consumo):
– Hardware:
•
•
•
•
CPU, Memoria centrale o RAM, memoria di massa, periferica
Terminale, stampante
CD-ROM, hard e floppy disk,
bus
– Software
• Software di base, sistema operativo, software applicativo, software di
utilità personale (spreadsheet, word-processor, “navigatori”, database
personali, e-mail,…)
– file
– sensori e attuatori
– Personal computer (PC)
• Le reti:
– Reti locali (LAN):
– Reti geografiche (WAN)
• L’ambiente di programmazione:
–
–
–
–
–
editor
compilatore
interprete
linker (collegatore)
debugger
• Le (innumerevoli) applicazioni dell’informatica:
–
–
–
–
–
–
Calcolo numerico e scientifico
Applicazioni gestionali
Servizi telematici
Automazione industriale
Realtà virtuale (dai giochi ai simulatori di volo alla cinematografia, …:
sfruttamento della multimedialità)
I linguaggi di programmazione di “alto” livello
(ovvero: sia la macchina a fare la fatica di “studiare” un linguaggio più
vicino all’uomo, ma sempre rigoroso!)
Alcuni difetti del linguaggio di von Neumann
Meglio questo:
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
READ
STORE
LOAD=
STORE
LOAD
BEQ
READ
ADD
STORE
LOAD
SUB=
STORE
BR
LOAD
WRITE
END
101
0
102
101
13
102
102
101
1
101
4
102
... o questo?
READ
STORE
LOAD=
STORE
CICLO_SOMMA: LOAD
BEQ
READ
ADD
STORE
LOAD
SUB=
STORE
BR
STAMPA_FINALE: LOAD
WRITE
END
CONTATORE
0
SOMMA
CONTATORE
STAMPA_FINALE
SOMMA
SOMMA
CONTATORE
1
CONTATORE
CICLO_SOMMA
SOMMA
Altri aspetti “sgradevoli” del linguaggio di von Neumann:
•
•
•
•
•
•
•
(a+b)*(c+d) ---->
LOAD
A
ADD
B
STORE
TEMP
LOAD
C
ADD
D
MULT
TEMP
•
“RIPETI LA SEQUENZA DI OPERAZIONI FINCHE’ LA SEQUENZA DEI
DATI NON E’ ESAURITA”
---->
BEQ ….
•
•
La controversa scelta del linguaggio: C
Il nucleo di C
• La macchina astratta del (nucleo) di C
Elementi -e terminologia- essenziali
•
•
•
•
•
Standard Input, Standard Output (vedi “nastri” di v.N.) e la memoria sono
divisi in celle elementari, contenenti ciascuna un dato.
Per il momento, non poniamo limiti fisici alla dimensione della memoria né
dei supporti di ingresso e uscita: numero di celle illimitato e ogni singola cella
può contenere un qualsiasi valore numerico (sia intero sia reale) o un qualsiasi
carattere, il che richiede un numero variabile di bit
Stringa: una successione finita di caratteri (per esempio “Giorgio, ieri, alfabeta…”. E’ immagazzinata in celle consecutive, ciascuna contenente un
singolo carattere della stringa.
Le celle di memoria vengono chiamate anche variabili (  dall’omonimo
concetto matematico!)
Le variabili, le istruzioni e altri elementi del programma che saranno introdotti
più avanti sono indicati tramite identificatori simbolici.
•
•
•
•
•
•
•
•
•
identificatore simbolico: una successione di lettere e cifre, in cui al primo
posto vi è una lettera. Il carattere speciale “_” viene considerato come cifra
Esempi di identificatori C:
a, x, alfa, pippo, a1, xy23, Giuseppe, DopoDomani….
le lettere maiuscole sono distinte dalle corrispondenti lettere minuscole (Var1,
var1 e VAR1 sono tre diversi identificatori
Per evitare ambiguità non è possibile usare lo stesso identificatore per indicare
diversi elementi né usare diversi identificatori per lo stesso elemento.
Alcuni identificatori sono predefiniti e riservati, nel senso che sono associati
a priori a qualche elemento del linguaggio e pertanto non possono essere usati
dal programmatore con significati differenti da quello predefinito.
Per esempio, scanf e printf indicano operazioni elementari di ingresso/uscita e
non possono essere impiegate in un programma C per indicare, per esempio,
una variabile.
Parola chiave: parola predefinita del linguaggio di programmazione;
anch’essa riservata; non può fungere da normale identificatore.
Per comodità -di lettura umana, non del calcolatore!- le parole chiave saranno
scritte in neretto .
• Struttura sintattica di un programma C:
•
•
•
•
Un programma C è composto da:
un’intestazione seguita da
una sequenza di istruzioni racchiusa tra i simboli { e }.
L’intestazione è costituita dall’identificatore predefinito main seguito
da una coppia di parentesi ( ) (per il momento vuote)
• Le istruzioni sono frasi del linguaggio di programmazione; ognuna di
esse termina con il simbolo ‘;’.
Le principali istruzioni del C
• 1. Istruzione di assegnamento
•
•
•
•
•
•
•
•
•
Assegna a una variabile il valore di un’espressione
consiste nel simbolo = preceduto dall’identificatore di una cella di memoria e
seguito da un’espressione che definisce un valore.
L’espressione può essere costituita da valori costanti, identificatori di variabili
o una loro combinazione ottenuta mediante i normali operatori aritmetici (+,
, *, /) e parentesi, come nelle consuete espressioni aritmetiche.
Esempi :
x = 23;
w = 'a';
y = z;
r3 = (alfa*43–xgg)*(delta–32*ijj);
x = x+1;
Se la cella a contiene il valore 45 e la cella z il valore 5, l’istruzione
x = (a–z)/10
fa sì che nella cella x venga immagazzinato il valore 4.
NB: per distinguere il valore carattere a dall’identificatore della variabile a, il
primo viene indicato tra apici (similmente per le stringhe -vedremo tra breve).
• 2. Istruzioni di ingresso e uscita
•
•
Consistono negli identificatori predefiniti scanf o printf seguiti da una coppia
di parentesi che racchiude l’identificatore di una variabile.
Determinano la lettura o scrittura del valore di una variabile dallo Standard
Input o sullo Standard Output in modo del tutto identico alle corrispondenti
istruzioni del linguaggio macchina.
•
•
•
•
•
Alcune comode abbreviazioni:
printf((a–z)/10);
abbreviazione per:
temp = (az)/10; printf(temp);
dove temp denota una variabile non usata altrimenti nel programma.
•
•
•
printf("alfa");
abbreviazione per:
printf('a'); printf('l'); printf('f'); printf('a');
•
differenza tra l’istruzione printf(2) e l’istruzione printf('2')?
• 3. Istruzioni composte
•
•
Le istruzioni composte producono effetti diversi a seconda che siano verificate o
meno certe condizioni sul valore delle variabili.
Condizione (o espressione booleana): un’espressione il cui valore può essere vero
o falso. Essa è costruita mediante
– i normali operatori aritmetici,
– gli operatori di relazione (==, !=, <, >, <=, >=),
– gli operatori logici (!, ||, &&), corrispondenti, nell’ordine, alle operazioni logiche NOT,
OR, AND.
•
•
•
•
•
•
Esempi di condizioni:
x == 0
alfa > beta && x != 3
!((a + b)*3 > x || a < c)
Se le variabili x, alfa, beta, a, b e c contengono rispettivamente i valori 0, 1, 2, 3, 4
e 5, la valutazione delle tre condizioni precedenti dà come risultato, rispettivamente:
V, F e F.
regole di precedenza tra gli operatori logici; per esempio, nell’espressione
x > 0 || y == 3 && z > w
l’operatore && deve essere eseguito prima dell’operatore ||, (in analogia con
a + b * c)
• Tornando alle istruzioni composte: esse sono di due tipi
fondamentali
• Istruzione condizionale:
• consente di eseguire in alternativa, durante l’esecuzione di un
programma, due diverse sequenze di istruzioni sulla base del valore di
verità di una condizione.
• è costituita dalla parola chiave if, seguita da
–una condizione racchiusa tra parentesi tonde,
–dalla prima sequenza di istruzioni racchiusa in parentesi graffe,
–dalla parola chiave else,
– dalla seconda sequenza di istruzioni racchiusa in parentesi graffe.
–Il “ramo else” dell’istruzione può essere assente.
–Le parentesi graffe vengono in genere omesse quando la successione di
istruzioni si riduce a un’istruzione singola.
• Esempi di istruzioni condizionali:
•
if(x == 0) z = 5; else y = z + w*y;
if(x == 0) {z = 5;} else {y = z + w*y;}
if ((x+y)*(z-2) > (23+v)) {z = x + 1; y = 13 + x;}
if ((x == y && z >3) || w != y) z = 5; else {y = z + w*y; x = z;}
•
istruzioni scorrette:
•
if (x == 0) else y = z; y = 34;
if (x == 0) a; else b + c;
•
Esecuzione di un’istruzione condizionale:
•
la macchina valuta la condizione, cioè stabilisce se il suo valore è vero o falso
– nel caso “vero” esegue solamente la prima sequenza di istruzioni,
–nel caso “falso” esegue la seconda sequenza di istruzioni.
–se manca il ramo else e la condizione è falsa, la macchina prosegue con l’istruzione
successiva all’istruzione condizionale.
• L’istruzione iterativa (ciclo o loop).
•
•
•
Permette la ripetizione dell’esecuzione di una sequenza di istruzioni ogni volta
che una certa condizione è verificata.
È costituita dalla parola chiave while, seguita dalla condizione racchiusa tra
parentesi tonde, come per l’istruzione condizionale, e da una sequenza di
istruzioni fra parentesi graffe (la sequenza di istruzioni è detta corpo del
ciclo).
Esempi:
•
while (x >= 0) x = x – 1;
while (z != y) {y = z – x; x = x*3;}
•
Esecuzione di un’istruzione iterativa:
–valutazione della condizione
–se questa è falsa non viene eseguito il corpo del ciclo e si passa direttamente
all’istruzione successiva.
–Altrimenti si esegue una prima volta il corpo del ciclo; si valuta ancora la condizione
e, nuovamente, si esegue il corpo del ciclo se essa è risultata vera. Quando la
condizione risulta falsa si esce dal ciclo, ovvero si passa all’istruzione successiva
all’istruzione iterativa. Il ciclo viene ripetuto finché la condizione rimane vera.
• Alcune osservazioni importanti
• In un’istruzione ciclica, l’esecuzione potrebbe non
terminare mai!
–L’istruzione condizionale e l’istruzione iterativa sono
dette istruzioni composte perché esse sono costruite
componendo istruzioni più semplici; contengono quindi
altre istruzioni al proprio interno.
–caratteristica profondamente diversa dal linguaggio di
von Neumann;
–molto utile per la costruzione di programmi complessi
(vedremo in seguito).
–un’istruzione composta può contenere al suo interno una
qualsiasi altra istruzione, eventualmente essa stessa
composta.
• Le macchine reali sono però costruite sullo
schema di von Neumann:
• Chi si occupa di “colmare il gap” tra la
macchina astratta C e la macchina reale -del
tipo di v.N.?
• Il software di base. Più precisamente:
– il compilatore, o, più raramente,
– l’interprete
• Nulla di magico
macchina C:
neanche
• Diamo una breve occhiata al compito del
compilatore
nella
Le principali funzioni del
compilatore
• Dal simbolico al binario
• Dall’aritmetica infissa all’aritmetica della
ALU
• Dalle istruzioni composte al program
counter
• …Successivamente:
• La gestione della memoria
1. Dal simbolico al binario
• Basta qualche tabella:
LOAD
STORE
BR
…
00110
00111
00001
….
A
100000
B
x
alfa
….
ciclo1
etichetta
…
100001
100010
100011
110000
110100
…
2. Dall’aritmetica infissa all’aritmetica della ALU
–
–
–
–
–
–
–
(a+b)*(c+d) ---->
LOAD
A
ADD
B
STORE
TEMP
LOAD
C
ADD
D
MULT TEMP
• …
• Algoritmi di traduzione non del tutto banali
3. Dal controllo mediante istruzioni
composte al controllo mediante salti
• if (cond) S1 else S2;
• Istruzioni che lasciano in accumulatore il valore 0
se cond è falsa, 1 se cond è vera
• BEQ
• Istruzioni che traducono S1
• BR
• Istruzioni che traducono S2
• while (cond) S;
• Istruzioni che lasciano in accumulatore il
valore 0 se cond è falsa, 1 se cond è vera
• BEQ
• Istruzioni che traducono S
• BR
NB: il meccanismo può essere ripetuto arbitrariamente in
maniera “annidata” (istruzioni composte che contengono altre
istruzini composte)
Siamo finalmente pronti per scrivere i primi programmi in “quasi” C
(l’I/O dovrà ancora essere “assestato)
/*Programma NumeroMaggiore – prima versione */
main()
{
scanf(x);
scanf(y);
if (x > y) z = x; else z = y;
printf(z);
}
/*Programma NumeroMaggiore – seconda versione */
main()
{
scanf(x);
scanf(y);
if (x > y) printf(x); else printf(y);
}
NB: Commenti e “pretty printing”
/*ProgrammaCercaIlPrimoZero */
main()
{
uno = 1;
scanf (dato);
while (dato !=0) scanf(dato);
printf(uno);
}
Si consideri il problema di calcolare la sommatoria di una sequenza di numeri
maggiori di 0, terminante con uno 0. Il programma seguente inizializza la
variabile somma a 0; quindi comincia a leggere i dati e, finché il numero letto
è diverso da 0, lo addiziona al valore corrente della variabile somma; alla
fine, quando il valore letto è 0, stampa il valore di somma sullo Standard
Output.
/*ProgrammaSommaSequenza */
main()
{
somma = 0;
scanf(numero);
while (numero != 0)
{
somma = somma + numero;
scanf(numero);
}
printf(somma);
}
NB: spiegazione informale (+commenti) affiancata al codice