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 n1: Basta quindi costruire tutti gli itinerari che partono da cp e hanno un numero di tratti non superiore a n1, 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 r1. 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 n1 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 r1. 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)s2(n-127) 1.m – se n=0 e m≠0 allora v=(-1)s2-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 = (az)/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