Title: Introduzione alla programmazione
1Introduzione alla programmazione
- Dott. Ing. Leonardo Rigutini
- Dipartimento Ingegneria dellInformazione
- Università di Siena
- Via Roma 56 53100 SIENA
- Uff. 0577233606
- rigutini_at_dii.unisi.it
- http//www.dii.unisi.it/rigutini/
2Algoritmo
3Che cosè linformatica
- Linformatica è lo studio sistematico degli
algoritmi che descrivono e trasformano
linformazione la loro teoria, analisi,
progetto, efficienza, realizzazione (ACM ?
Association for Computing Machinery) - Nota È possibile svolgere unattività
concettualmente di tipo informatico senza
lausilio del calcolatore, per esempio nel
progettare ed applicare regole precise per
svolgere operazioni aritmetiche con carta e
penna lelaboratore, tuttavia, è uno strumento
di calcolo potente, che permette la gestione di
quantità di informazioni altrimenti intrattabili
4Algoritmo
- Algoritmo sequenza di istruzioni attraverso le
quali un operatore umano è capace di risolvere
ogni problema di una data classe non è
direttamente eseguibile dallelaboratore - Programma sequenza di operazioni atte a
predisporre lelaboratore alla soluzione di una
determinata classe di problemi - Il programma è la descrizione di un algoritmo in
una forma comprensibile allelaboratore - Lelaboratore è una macchina universale
cambiando il programma residente in memoria, è in
grado di risolvere problemi di natura diversa
(una classe di problemi per ogni programma)
5Algoritmi nella realtà
- Come abbiamo già detto un algoritmo può essere
descritto in maniera informale come una sequenza
di passi, definiti con precisione, che portano
alla realizzazione di un compito - Es
- Le istruzioni di montaggio di un elettrodomestico
- Il calcolo del MCD (massimo comune divisore)
- Il prelievo di denaro dal bancomat
- Inserimento tessera
- Inserimento codice segreto
- .ecc
- Molte attività umane sono algoritmi
6Algoritmo
- Un algoritmo deve essere
- comprensibile
- Altrimenti lesecutore non può capirlo
- non-ambiguo
- Altrimenti vi potrebbero essere diverse valide
interpretazioni dello stesso algoritmo - corretto
- Ovviamente dovrebbe effettivamente fare quello
per cui è stato pensato - efficiente (utile ma non vincolante)
- Dovrebbe eseguire il suo compito nel modo più
veloce possibile
7Codifica di un algoritmo
- Quindi per risolvere un problema è necessario
esprimerlo sotto forma di algoritmo - Una volta descritto in termini di passi più o
meno elementari è possibile codificare questi
passi utilizzando il linguaggio di programmazione
prescelto - Ricordiamo anche che per un dato problema possono
esistere molteplici algoritmi che lo risolvono e
che possono distinguersi per la migliore o
peggiore complessità - Luso di un linguaggio di alto livello permette
di utilizzare nomi per le variabili e per le
funzioni molto vicini alla loro funzione - tassoInteresse per una variabile che indica il
tasso dinteresse, ecc
8Schema dellalgoritmo
- Solitamente la codifica di un problema in
algoritmo avviene utilizzando i diagrammi di
flusso, ovvero diagrammi grafici che permettono
di visualizzare il flusso delle operazioni da
compiere per arrivare alla soluzione - Un diagramma di flusso schematizza lordine delle
operazioni più o meno semplici richieste per
risolvere un problema - Ogni operazione di assegnamento è racchiusa in un
rettangolo mentre le operazioni di verifica sono
visualizzate in rombi
9Diagrammi di flusso
- Esempio di codifica di un banale algoritmo con un
diagramma di flusso - Rappresentare un grosso programma tramite un
unico diagramma di flusso diventa comunque
impossibile - Solitamente un algoritmo viene scomposto in unità
funzionali distinte (funzioni, procedure o
moduli) - Ogni unità dovrebbe essere così semplice da poter
essere facilmente schematizzabile tramite un
diagramma di flusso
leggi x e memorizzalo in a
a gt 100
Fai qualcosa
Stampa errore
true
false
10Linguaggi
- Il linguaggio utilizzato per descrivere
lalgoritmo deve essere interpretabile
dallesecutore dellalgoritmo - Se abbiamo le istruzioni di montaggio di un
elettrodomestico in cinese, non siamo in grado di
seguire i passi necessari a terminare il lavoro
(a meno che non si conosca il cinese!!) - In informatica
- I calcolatori sono esecutori di algoritmi
- Gli algoritmi sono descritti tramite programmi,
cioè sequenze di istruzioni scritte in un
opportuno linguaggio, comprensibile al
calcolatore - Compito dellesperto informatico
- Produrre algoritmi per un dato problema
- Codificarli in programmi (renderli comprensibili
al calcolatore)
11Precisione
- Il calcolatore esegue un programma passo-passo,
in modo preciso, veloce e potente, senza deviare
dal flusso delle istruzioni anche se esso è
palesemente sbagliato - Questo perché il calcolatore è privo di
intelligenza, non può decidere se una
istruzione è sbagliata, se cè, la esegue. - Questo richiede quindi che le istruzioni siano
puntuali e che lalgoritmo sia chiaro e puntuale - per fare la torta di mele occorrono 3 Kg di
mele, 3 uova e 0,5 Kg di farina ? è
sufficientemente precisa - legate larrosto e salatelo ? non è
sufficientemente precisa poiché per esempio non
specifica la quantità di sale da utilizzare
12Correttezza
- Un algoritmo è corretto se esso perviene alla
soluzione del compito per cui è stato sviluppato,
senza difettare di alcun passo. - Se nellalgoritmo di montaggio di un mobile viene
omessa listruzione di stringere una vite, il
risultato finale può non essere corretto perché
il mobile molto probabilmente non starà in piedi
(dipende dalla importanza della vite!!) - Sarà necessario individuare lerrore e ripararlo
per avere il risultato corretto - Un algoritmo non deve tralasciare nessun passo
per giungere alla soluzione, altrimenti il suo
risultato potrebbe essere non corretto.
13Efficienza
- Un algoritmo dovrebbe raggiungere la soluzione
nel minor tempo possibile e/o utilizzando la
minima quantità di risorse, compatibilmente con
la sua correttezza. - efficienza in tempo e spazio (risorse)
- Es.
- Non è efficiente un algoritmo in cui prima viene
regolato un dispositivo e poi, nel passo
successivo, tale impostazione viene modificata,
se poi deve essere riportata a come era prima - Un buon informatico cercherà di ottimizzare un
algoritmo il più possibile per cercare di
arrivare alla soluzione il più veloce possibile e
con lutilizzo di minor risorse possibili
14Complessità
15Complessità
- La bontà di un programma viene valutata
analizzando due fattori - Quanto è veloce il programma (complessità in
tempo) - Quante risorse richiede il programma (complessità
in spazio) - Solitamente, a meno di richieste esagerate, la
complessità spaziale non è un problema, quindi si
guarda molto più a quanto tempo ci vuole per
eseguire un compito
16Complessità
- La complessità temporale di un programma però non
è di semplice valutazione - Tempo di esecuzione
- dipende dalla potenza della macchina su cui è
eseguito - dipende dalla dimensione dellinput (molti dati ?
molto più tempo) - La soluzione è stata di assegnare un costo alle
operazioni e calcolare quindi la complessità
totale come costo complessivo - Loperazione di confronto ha costo uno
- Tutte le altre hanno costo nullo (non è vero, ma
hanno un costo irrisorio rispetto al confronto)
17Complessità
- Inoltre non è possibile dare una misura puntuale
del costo di un algoritmo, poiché molte volte
esso dipende dallo spazio dellinput - Si calcola quindi il costo in funzione della
dimensione dellinput costo f(n) , dove n è la
dimensione dei dati su cui andiamo a lavorare - Infine è necessario calcolare il costo nel caso
migliore e nel caso peggiore - o(f(n)) per la complessità nel caso migliore
(costo minimo) - O(f(n)) per la complessità nel caso peggiore
- Il limite inferiore (o() ) non ha molto
significato
18Complessità
- Es.
- Problema
- Dati n numeri, trovare il più grande
- Algoritmo
- MAXVAL primo numero
- Per ogni numero seguente, se è maggiore di
quello in MAXVAL, memorizzalo in MAXVAL - Complessità
- nel caso peggiore, il valore massimo si trova in
ultima posizione in questo caso sono necessari
n-1 confronti. Quindi la complessità
dellalgoritmo è O(n). Tale funzione (f(n)n) è
detta anche lineare, dato che cresce
linearmente con n.
19Complessità
- In realtà, lesempio precedente, avrebbe
complessità O(n-1), ma poiché per n gtgt1 ?
O(n-1)O(n), viene utilizzato O(n) come valore di
complessità - Questo vale per tutte le funzioni f(n) indicanti
la complessità di un algoritmo - O(2n4) , per ngtgt1 ? O(n)
- O(2n2n4), per ngtgt1 ? O(n2)
- Ecc..
- cioè viene individuata la funzione maggiorante
20Complessità
- La complessità dà unidea della fattibilità
dellalgoritmo. Supponiamo il costo di una
operazione pari ad 1 secondo, il tempo necessario
per un algoritmo con le varie complessità è il
seguente - Come si vede, un problema con complessità
polinomiale diventa molto velocemente
intrattabile
LogaritmicaO(log(n)) Lineare O(n) QuadraticaO(n2) PolinomialeO(2n)
10 3 sec 10 sec 1,5 min 17 min
20 4-5 sec 20 sec 7 min 291 giorni
30 5 sec 30 sec 15 min 191 anni
21Esempio un algoritmo più complicato
- Gestione di una biblioteca
- Ogni libro si trova su uno scaffale e può essere
preso in prestito ed in seguito restituito. - La biblioteca è dotata di uno schedario ed ogni
scheda contiene - Nome,cognome dellautore (o autori)
- Titolo
- Data di pubblicazione
- Numero scaffale in cui si trova
- Numero dordine della posizione
- Le schede sono disposte in ordine alfabetico in
base allautore ed al titolo.
22La biblioteca
- Algoritmo di ricerca di un libro in biblioteca
- Ricerca della scheda nello schedario
- Lettura del numero di scaffale e posizione del
libro - Ricerca dello scaffale indicato
- Prelievo del libro e scrittura sulla scheda delle
informazioni sul prestito data,nome ecc.. - Ogni passo può (deve) essere descritto più
dettagliatamente sotto-algoritmo - Passo1
- 1.1 Lettura della prima scheda dello schedario
- 1.2 Se il nome dellautore ed il titolo
coincidono, ricerca conclusa - 1.3 Si ripete il passo 2 fino trovare la scheda
cercata o a terminare le schede - 1.4 Se non viene trovata la scheda, la ricerca
termina in modo infruttuoso
23La biblioteca
- Il sotto-algoritmo per il passo 1, è corretto ma
non efficiente - Ha una complessità lineare con il numero di
schede, ossia nel caso peggiore tutte le schede
nello schedario verranno analizzate - E possibile definire un altro algoritmo per il
passo 1 più efficiente - 1.1 Se lo schedario è vuoto ricerca infruttuosa
- 1.2 Lettura scheda centrale
- 1.3 Se è quella cercata, ricerca conclusa con
successo - 1.4 Se il nome dellautore e/o titolo è
precedente, si ripete lalgoritmo sulla prima
metà dello schedario - 1.5 Se il nome dellautore e/o titolo è
successivo, si ripete lalgoritmo sulla seconda
metà dello schedario - Nel caso peggiore, questo algoritmo analizza
log2(n) schede - 16000 schede ? alg1 16000 confronti
- 16000 schede ? alg2 14 confronti
24La biblioteca
- Nel secondo algoritmo presentato per
automatizzare il passo 1, lalgoritmo richiama se
stesso su un differente set di dati fino ad una
terminazione positiva o negativa - Un algoritmo di questo tipo si dice ricorsivo
- ovvero il problema viene scomposto in
sottoproblemi uguali ma su set di dati più
piccoli, fino ad arrivare o alla soluzione o alla
impossibilità di proseguire - condizioni di stop
- Il sottoinsieme su cui viene richiamato
lalgoritmo è vuoto (1.1) - La ricerca è andata a buon fine (1.3)
- Vedremo più avanti in dettaglio quali tipi di
algoritmi esistono
25Livelli di astrazione e storia dei linguaggi di
programmazione
26Programmazione di basso livello
- Al livello più basso le istruzioni di un computer
sono estremamente elementari. - Il processore esegue le istruzioni macchina
- Sequenze di bytes i codici delle operazioni e le
locazioni in memoria - Le CPU di fornitori diversi (Intel, SUN, Mac)
hanno insiemi differenti di istruzioni macchina - Instruction set
27Linguaggio binario
- Scrivere programmi direttamente in linguaggio
macchina però risulta essere molto complicato - Sequenze di numeri con poca espressività
- Es
- Carica il contenuto della posizione di memoria
40 - Carica il valore
- Se il primo valore è maggiore del secondo
continua con listruzione in posizione 240
21 40 16 100 163 240
28Assembler
- Il primo passo fu di assegnare nomi abbreviati ai
comandi - iLoad ? carica un numero intero
- bipush ? inserisci una costante numerica
- if_icmpgt ? se il numero intero è maggiore
- Lesempio può essere riscritto così
- iLoad 40
- bipush 100
- if_icmpgt 240
- Similmente in seguito furono assegnati dei nomi
anche alle locazioni di memoria (variabili) - iLoad intRate
- bipush 100
- if_icmpgt intError
ASSEMBLER
29Assemblatore
- Tale metodo di programmazione richiedeva quindi
un software che traducesse il codice dal
linguaggio ASSEMBLER al linguaggio macchina - ASSEMBLATORE
- La nuova forma di programmazione continua ad
avere corrispondenza uno-a-uno con il linguaggio
macchina - Ad ogni istruzione ASSEMBLER corrisponde una
istruzione del Instruction Set - Rimane però molto difficile sviluppare grandi
programmi utilizzando istruzioni di così basso
livello - If_cmpgt, iLoad, ecc lavorano su indirizzi in
memoria, su registri della CPU ed anche un
semplice confronto tra due variabili richiede una
sequenza di alcune istruzioni assembler
30Linguaggi di alto livello
- Furono teorizzati allora linguaggi di
programmazione che astraevano dalla architettura
del calcolatore (indipendenza dall Instruction
Set) e permettevano costrutti molto più vicini al
pensiero umano - se EXPR allora FAI QUESTO, altrimenti FAI
QUESTALTRO - ripeti L1 fino a che NON ACCADE QUESTO
- Questi costrutti permettevano di racchiudere una
sequenza di istruzioni macchina in una
descrizione molto più vicina allessere umano
31La compilazione
- Questi linguaggi richiedono luso di un
compilatore che traduca il codice di alto livello
in codice macchina - Ogni linguaggio ha quindi il suo compilatore (o
più di uno) - Inoltre, dato che ciò che il compilatore genera è
il codice macchina e questultimo è strettamente
dipendente dalla macchina (Hardware/SO) su cui
gira, la compilazione traduce il codice da un
linguaggio astratto in una forma dedicata e
strettamente dipendente dalla macchina che si sta
adoperando
32La compilazione
- Quindi
- È il compilatore che si occupa di tradurre un
dato linguaggio (es. C) nel codice macchina
adatto alla macchina su cui dovrà essere eseguito - Se un programma C viene compilato su ambiente
Windows, sarà creato un programma che girerà
solo su ambienti windows similarmente, se lo
stesso codice viene compilato su Linux, sarà
possibile eseguire lapplicazione solo su una
macchina Linux - Un linguaggio di programmazione di alto livello
deve rispettare RIGOROSE convenzioni affinché sia
interpretabile dal compilatore
33Linguaggi alto livello
se il tasso di interesse è maggiore di 100
scrivi a video un messaggio di errore
Sviluppo
If (intRategt100) printf(Error)
C
Compilazione
21 40 16 100 163 240
34La compilazione
- Il processo di compilazione però peggiora le
prestazioni rispetto ad un programma scritto
direttamente in linguaggio macchina - Il tempo richiesto dal traduttore per generare il
codice macchina - Il codice oggetto generato dal compilatore
tipicamente è meno ottimizzato rispetto a
quello generato direttamente dallessere umano - In realtà
- Anche considerando il tempo di traduzione del
compilatore, il tempo necessario allo sviluppo
del programma rimane comunque decisamente
inferiore a quello necessario a sviluppare
direttamente in codice oggetto - Il codice generato dal compilatore è sì difficile
che sia ottimizzato, ma comunque la perdita di
prestazione risulta essere accettabile - Oggi inoltre tutti i compilatori hanno la
possibilità di ottimizzare il codice - E comunque non è detto che un essere umano
riuscirebbe a generare codice ottimizzato per
programmi molto complicati come quelli che si
possono scrivere utilizzando linguaggi di alto
livello
35Un po di storia dei linguaggi di alto livello
- A seguito di queste teorie nacquero i primi
linguaggi di alto livello - FORTRAN (FORmula TRANslator) il primo linguaggio
di alto livello sviluppato per descrivere formule
matematiche - COBOL (Common Business Oriented Language) il
primo linguaggio orientato alle applicazioni
gestionali - Per molto tempo questi due linguaggi rimasero i
linguaggi più diffusi fino a quando non fu
studiata una categoria di linguaggi più
rigorosamente basati su uno studio dei principi
della programmazione - Il capostipite di questa famiglia fu ALGOL60
(ALGOrithmic Language) che pur non essendo mai
stato utilizzato nella pratica, è famoso per
questo motivo.
36Un po di storia dei linguaggi di alto livello
- Dopo lALGOL60, furono sviluppati molti nuovi
linguaggi di programmazione orientati ad una
programmazione generale - PASCAL, oggi molto diffuso per la didattica
- C ,tra i più potenti linguaggi di programmazione,
divenuto molto popolare grazie al fatto che il
sistema Unix fu interamente sviluppato
utilizzando questo linguaggi di programmazione - ADA, Basic, Perl, Phyton, ecc.
- Anche questi però ad un certo punto furono
superati da un nuovo paradigma la programmazione
ad oggetti - C, C e Java derivati dal C
- Eiffel e Delphi derivati dal Pascal
- VisualBasic ecc
37Programmazione Procedurale
38Linguaggio procedurale
- La programmazione classica segue il paradigma
di programmazione procedurale o imperativo - Cosa significa
- Il problema è scomposto in molti sottoproblemi
che possono essere risolti in maniera
semi-indipendente per poi essere aggregati per
ottenere il risultato finale - Ogni sotto-problema viene assegnato ad un
sottoprogramma che si occupa di eseguire le
operazioni per restituire il risultato (o
effettuare il compito per cui è stato creato) - Lapplicazione stessa è vista come un
sottoproblema - Assemblare i sotto-problemi individuati in
maniera da ottenere il risultato
39Linguaggio procedurale
- Quindi seguendo questa idea, il programma è
costituito da diverse funzioni (procedure o
subroutines) ognuna disegnata per uno scopo - Il corpo del programma è esso stesso una
funzione, con la particolarità però di avere una
forma (prototipo) standard - la funzione main()
- Quando un programma procedurale è compilato ed
avviato, ciò che viene lanciato è la procedura
main - Tutto ciò che deve essere eseguito dal programma
deve essere implementato nella procedura main,
ricorrendo a tutte le chiamate di funzione e dati
necessari
40Linguaggio procedurale
Dato A
funzione1
Dato B
funzione2
int main(int,int)
Dato N
funzioneM
avvio dellapplicazione
41Variabili e tipi di dato
42Variabili
- Una variabile rappresenta unarea di memoria
riservata dal compilatore per memorizzare dati - Questarea di memoria è riferita tramite un nome
univoco - Questo nome permette di accedere alla memoria per
leggere/scrivere informazioni - In fase di programmazione, quando viene definita
ed utilizzata la variabile non ha un valore, ma
rappresenta tutti i valori che ciò che essa
rappresenta potrà assumere - Il valore è assegnato a run-time
43Nomi di variabili
- Ogni variabile (e funzione che vedremo in
seguito) è riferita tramite un nome - Un nome in un linguaggio di programmazione è
inteso come una etichetta che rispetta alcune
regole fissate dal linguaggio stesso - Deve essere costituita da un singolo token.
Etichette formate da più parole separate non sono
accettate (il compilatore non può sapere che le
due parole vanno interpretate insieme!!) - Non può essere una delle parole riservate del
linguaggio (if, else, while, return ecc) - Deve contenere solo caratteri alfa-numerici più
il carattere _ questa condizione implica la
prima poiché il carattere spazio non è
permesso - Non può comunque iniziare con un carattere
numerico
44Dichiarazione di variabili
- Dichiarare una variabile significa avvertire il
compilatore che riservi unarea di memoria perché
in futuro essa verrà utilizzata con quel nome - Processo di allocazione della memoria
- Ogni variabile, prima di essere utilizzata, deve
essere dichiarata - In caso contrario il compilatore genera un
errore, non trovando la corrispondenza del nome
con un area di memoria nella tabella simboli - Quando una variabile viene dichiarata, deve
perciò essere specificato sia il nome della
variabile, sia il tipo di dato che tale variabile
potrà assumere - Se una variabile viene dichiarata di tipo T,
necessariamente essa potrà memorizzare valori di
tipo T
45Tipi di dato
- I tipi di dato si distinguono in tipi semplici e
tipi strutturati - tipi di dato semplici sono tipi di dato a cui
può essere associato un singolo valore (numerico
o stringa) ed un riferimento alla variabile è un
riferimento al contenuto - tipi di dato strutturati sono tipi di dato
composti da più campi, da cioè uno o più altri
tipi di dato a loro volta semplici o strutturati - Ogni linguaggio di programmazione mette a
disposizione una serie di tipi di dati
predefiniti - Tipi di dato semplici ? bool, int, char, double,
ecc - Tipi di dato strutturati ? array
46Tipi semplici predefiniti
- Tutti i linguaggi di programmazione mettono a
disposizione un numero di tipi di dato semplice
built-in
bool Valori logici true e false
char Interi su 8 bit utilizzati per rappresentare numeri e caratteri (ASCII) signed/unsigned
int Interi su 16 bit signed/unsigned
long Interi su 32 bit signed/unsigned
float Numeri in virgola mobile a 32 bit
double Numeri in virgola mobile a 64 bit
47Operatori
- Per questi tipi di dato semplici sono forniti dal
linguaggio anche gli operatori - Assegnamento ()
- Addizione ()
- Sottrazione ()
- Moltiplicazione ()
- Divisione (/)
- Modulo () , ritorna il resto della divisione.
Es. 53 2 - Uguaglianza () e disuguaglianza (!), ritornano
vero o falso - Minore o maggiore (lt, lt, gt, gt) , ritornano vero
o falso
48Il tipo char
- Il tipo char è un tipo semplice particolare
- Il nome deriva dallabbreviazione di character
ed infatti è utilizzato per rappresentare
linsieme finito dei caratteri (la tabella ASCII) - Ogni carattere ha un codice numerico compreso tra
0 e 127 (7 bit), più un extended set compreso
tra 128 e 255 (8 bit)
49Il tipo char e le sequenze di escape
- Alcuni codici sono riservati per i caratteri di
controllo e solitamente sono indicati utilizzando
il carattere \ come carattere di escape - Molte volte è necessario utilizzare particolari
sequenze di caratteri per rappresentare caratteri
che altrimenti non sarebbe possibile visualizzare - Es
- Le stringhe sono sequenze di caratteri
delimitate da . Se vogliamo inserire un
carattere allinterno di una stringa dobbiamo
utilizzare la sequenza di escape \ per informare
il compilatore che non delimita la fine della
stringa - Di solito le sequenze di escape sono utilizzate
per i caratteri di controllo new line? \n,
tab ? \t, ecc.
50Caratteri di controllo
escape ASCII codice Descrizione
0 NULL Carattere nullo
\b 8 BS Back Space
\t 9 HT Horizontal Tab
\n 10 LF Line Feed
\r 13 CR Carriage Return
- La rappresentazione dei caratteri tramite un
codice numerico permette un ordinamento tra gli
stessi caratteri e quindi la possibilità di
operazioni
51Valori esadecimali
- È inoltre possibile assegnare valori in base 16
utilizzando la convenzione - 0xNNNN
- dove NNNN è il numero in base 16
- Es
- int y0x0A ? assegna 10 alla variabile y
52Definizione di nuovi tipi semplici
- Oltre ai tipi di dato semplici predefiniti, è
possibile definirne di nuovi - Ridefinizione ? typedef il nuovo tipo di dato è
solamente un alias di uno già esistente. Per
esempio, listruzione C typedef int
T1definisce un tipo di dato T1 che altro non è
che un altro nome per un intero (int) - Enumerazione ? enum il nuovo tipo di dato può
assumere solamente uno dei valori (numerici o
meno) contenuti in una lista di enumerazione. Per
esempio, listruzione C enumA,B,C D1
definisce una variabile D1 che può assumere
solamente i valori A,B, o C. In questo
esempio il tipo di dato è enum. per avere un
tipo di dato T1 è necessario utilizzare il
costrutto typedef typedef enumA,B,C
T1 ? tipo di dato T1 T1 D1 ? crea una
variabile D1 di tipo T1
53Tipi strutturati predefiniti
- I linguaggi di programmazione forniscono anche
dei tipi strutturati predefiniti - array ? questo tipo di dato corrisponde ad una
sequenza di celle consecutive di memoria di
lunghezza fissata (e fornita al momento della
dichiarazione della variabile di quel tipo).
Tutte le celle devono necessariamente contenere
lo stesso tipo di dato. Per esempio, in C,
listruzione int list20 dichiara la
variabile list come una sequenza di 20 interi - I recenti linguaggi di programmazione forniscono
altri dati strutturati predefiniti per esempio
date, record, ecc
54Creazione di nuovi tipi strutturati
- Oltre ai tipi built-in è possibile create nuovi
tipi di dato strutturati tramite appositi
costrutti del linguaggio - Es. in C (o C)
- struct
- int giorno
- int mese
- int anno
- D1
- Lesempio mostra una situazione tipica in cui è
preferibile utilizzare un dato strutturato - La data infatti può essere vista come una tripla
di interi, ma gestire tre variabili intere
separate per tutto il programma può essere
contro-intuitivo per il programmatore. Risulta
migliore, come si può immaginare, creare un dato
D1 come aggregazione dei tre interi. Anche in
questo esempio per definire un tipo di dato T1 si
può utilizzare la funzione typedef
55Dichiarazione ed inizializzazione
- Una volta dichiarata, la variabile può essere
utilizzata nel programma - La dichiarazione crea una istanza di quel tipo di
dato con il suo relativo nome (normalmente si
parla di variabile istanziata) - Notare che la dichiarazione non inizializza la
variabile ad un valore, ma semplicemente riserva
uno spazio in memoria per quella variabile un
successivo accesso alla variabile non
inizializzata, può restituire un valore non
valido - Linizializzazione di una variabile è fatta con
un istruzione di assegnamento - Può essere fatta al momento della dichiarazione
- Può essere fatta in un secondo momento
56Cast
- Se memorizziamo un dato in una variabile di tipo
diverso, è solitamente avviene unoperazione di
casting, ovvero il compilatore traduce
automaticamente il dato nella forma richiesta dal
tipo della variabile - int ? double ( 54 ? 54,00) non si ha perdita
di precisione - double ? int (54,23451 ? 54) si perde la parte
decimale - signed char ? unsigned char (-1 ? 255) vengono
utilizzati tutti i 28 valori positivi generabili
con 8 bit. - Infatti
- signed char -27,27-1, di cui 1xxxxxxx sono i
valori negativi - -128,-1 ? 10000001 , 11111111
- se non consideriamo il segno,
- 10000001 ? 129 e 11111111 ? 255
- quindi -1 ? 255 nel passaggio da signed ad
unsigned (e viceversa)
57Cast
- Il cast avviene in maniera implicita se è
previsto dal linguaggio di programmazione (int ?
char, double ? int, ecc) altrimenti è necessario
farlo in maniera esplicita definendo unoperatore
di casting - Per i tipi di dato semplici predefiniti il
casting è quasi sempre fornito dal linguaggio di
programmazione stesso
58Dichiarazione delle costanti
- Una dichiarazione di costante associa
permanentemente un valore ad un identificatore - La dichiarazione di una costante è praticamente
la dichiarazione con assegnamento di una
variabile con laggiunta della parola riservata
const davanti, a specificare il fatto che quella
cella di memoria conterrà il valore assegnato
senza possibilità di modifica - ES
- const int numAlunni 25 ? numALunni conterrà
25 per tutto il suo ciclo di vita - const char nome16leonardo ? nome conterrà
leonardo senza possibilità di
modifiche - const date data 5,10,2005 ? data conterrà
5/10/2005 -
59Istruzioni e controllo del flusso
60Istruzioni
- Le istruzioni sono frasi proprie del linguaggio
di programmazione delimitate dal simbolo - Es. read(x) è una istruzione senza il simbolo
terminatore, il compilatore non capirebbe dove
termina listruzione - Molte volte è necessario che sequenze di
istruzioni vengano eseguite sequenzialmente - Corpo del programma, corpo di una funzione, corpo
di un ciclo - Una lista di istruzioni è delimitata dai simboli
e - inizia una lista di istruzioni
- termina la lista
- E possibile innestare più liste di comandi
61Struttura di un semplice programma
- Un programma è organizzato in maniera sequenziale
(non è più tanto vero) - La prima parte è lintestazione, ovvero una
etichetta seguita da una coppia di parentesi
tonde () - Il significato di queste parentesi sarà spiegato
in seguito - A seguire inizia una lista di istruzioni,
delimitate quindi da e - Ma che tipo di istruzioni prevede un linguaggio?
62Istruzione di assegnamento
- Questa istruzione fondamentale viene utilizzata
per assegnare a una variabile il valore di una
espressione - Consiste nel simbolo preceduto dal nome della
variabile a cui deve essere assegnato il valore - Alla destra del simbolo viene specificate
lespressione che genera il risultato da
assegnare alla variabile - Lespressione può essere costituita da un valore
costante (numero) , identificatori di altre
variabili, espressioni aritmetico/logiche di
combinazioni di valori e variabili e funzioni - x 23
- wa
- y z
- alfa x y
- r3(alfa 43 xgg)(delta-32ijj)
- x x 1
63Istruzione di assegnamento
- Lesecuzione di una istruzione di assegnamento
comporta la valutazione della espressione a
destra del simbolo e la sostituzione del valore
nella cella di memoria individuata dal nome della
variabile a sinistra del simbolo - Si noti che un valore cotante può essre un numero
ma anche un carattere - Nellesempio wa, assegna il carattere a alla
variabile w
64Controllo del flusso
- Il flusso delle istruzioni può cambiare a seconda
della valutazione di una espressione - Può essere necessario per esempio effettuare una
lista di operazioni L1 se una condizione è vera,
una lista L2 altrimenti - Oppure può essere necessario ripetere una lista
di istruzioni fino a che non è verificata una
condizione - Le istruzioni che permettono queste scelte sono
dette di controllo del flusso e come si evince
dai due esempi qui sopra sono divise in due
gruppi - Istruzioni condizionali
- Istruzioni iterative
65Istruzione condizionale
- Consente di valutare una espressione e di
eseguire due sequenze distinte di operazioni a
seconda del risultato ritornato dalla espressione - Sintassi
- if (E1) L1 else L2
- Se E1 è vera verrà eseguita la lista di
istruzioni L1, altrimenti sarà eseguita la lista
L2 - Il cosiddetto ramo else non è obbligatorio e
può essere omesso nel caso in cui non vi sia
alcuna lista L2 - Se L1 e L2 sono composta da ununica istruzione
anche le parentesi graffe possono essere omesse - Es.
- if (E1) I1 else I2
66Castello di if
- Se è necessario verificare più espressioni in
maniera innescata, è necessario fare attenzione a
quale if fa riferimento ogni else - if (E1)
- if (E2)
- L2
- else
- if (E3)
- L3
-
- ? chiude lelse di E2
- else
- L1
- ? chiude lelse di E1
67Istruzione iterativa while
- Consente di ripetere la lista di istruzioni
(iterare) più volte fino a che non risulta vera
una condizione - Sintassi
- while (E1) L1
- Lespressione E1 viene valutata e se è vera,
viene eseguito il corpo del ciclo. Terminato il
corpo, viene di nuovo valutata E1, e se continua
ad essere vera, il corpo è di nuovo eseguito.
Questo è ripetuto fino a che la valutazione di E1
non ritorna false - È possibile utilizzare allinterno del ciclo
while listruzione break che interrompe
incondizionatamente il ciclo, come se la
condizione E1 fosse verificata
68Istruzioni derivate strutture di controllo
- Teorema di Bohem-Jacopini Le istruzioni di
controllo viste finora if else e while
sono equivalenti alle strutture di controllo del
linguaggio assemblatore basate sulla diretta e
condizionale manipolazione del registro contatore
di programma tramite esse è possibile esprimere
ogni qualsiasi altra struttura di controllo di
ogni qualsiasi altro linguaggio di
programmazione - Questo teorema ci dice che sebbene sia possibile
pensare a molte altre possibili strutture di
controllo di alto livello, per ognuna di esse si
può sempre trovare una forma equivalente in
termini di ifelse o di ciclo while - La specifica di nuove strutture di controllo
rimane comunque utile per fornire ai
programmatori strumenti più vicini ai casi
concreti
69Istruzioni condizionali derivate else if
- Quando sono necessarie valutazioni di più
espressioni su di una variabile è possibile
utilizzare il costrutto else if -
- if (E1)
- L1
- else if (E2)
- L2
-
- .
- else if (En-1)
- Ln-1
-
- else
- Ln
-
- In alcuni linguaggi else if è stato condensato
in un comando unico elseif
70Istruzioni condizionali derivate switchcase
- Molti linguaggi per evitare enormi castelli di
if, mettono a disposizione un costrutto derivato
switch case - Sintassi
- switch (E1)
- case A
- LA
- break
- case B
- Lb
- break
- .
- default
- Ldefault
-
71Istruzioni condizionali derivate switchcase
- Listruzione switch-case, valuta lespressione E1
e suppone che siano possibili vari casi - Per ognuno dei casi viene fornita una lista di
istruzioni, seguita dal comando break - Nel caso si verifichi una condizione non
specificata da alcun case , viene fornita una
scelta di default - Il comando break non è obbligatorio, serve
solamente per evitare che nel caso A, il
programma prosegua effettuando anche le
istruzioni relative al caso B, caso C, e così via
fino al caso di default. Se allinterno di un
particolare algoritmo è richiesto un tale
comportamento, il comando break può essere
rimosso - NB il costrutto switch-case lavora solamente su
valori numerici - La valutazione di E1 deve ritornare quindi un
valore numerico
72Istruzioni iterative derivate for
- Consente di ripetere la lista di istruzioni
(iterare) un numero prefissato di volte - Sintassi
- for (START EXPR STEP) L1
- START è la condizione iniziale da cui partire
EXPR è la espressione che viene valutata ad ogni
iterazione per decidere se interrompere o meno il
ciclo STEP è la modifica che viene effettuata ad
ogni passo - Es.
- for (i0iltNi) L1
- per i che va da 0 a N, con passo 1, fai L1.
Questo ciclo esegue N volte la lista di
istruzioni L1.
73Istruzioni iterative derivate for
- Listruzione for è in realtà un modo diverso per
scrivere la forma while. Essa può essere infatti
sostituita con - i0while (iltN)
- L1
- ii1
-
- dove N è conosciuto.
- Oramai comunque è diventata una forma di
istruzione standard in tutti i linguaggi di
programmazione.
74Istruzione iterative derivate dowhile
- Così come listruzione for può essere vista come
un caso particolare del più generale ciclo while,
così molte altre istruzioni condizionali sono
state proposte nei vari linguaggi di
programmazione - do while
- do L1 while (E1) ripete la lista di
istruzioni fino a che E1 non è verificata a quel
punto esce dal ciclo - La differenza importante dal ciclo while è il
punto in cui E1 viene valutata - - nel ciclo while viene valutata prima di
eseguire L1, - nel ciclo do-while è valuata
dopo aver eseguito almeno una volta il corpo
del ciclo
75Istruzione iterative derivate goto
- Molti linguaggi hanno mantenuto tra le proprie
strutture di controllo istruzioni di salto
direttamente ispirate alle istruzioni macchina
dei processori - Istruzione goto
- Sintassi
- goto LABEL
-
- LABEL L1
- Listruzione goto permette di saltare
lesecuzione del programma alla linea di codice
individuata dalla etichetta LABEL. - Sono molto poco utilizzate ed anzi i teorici
della programmazione sostengono che se siamo
costretti ad utilizzare un istruzione goto,
abbiamo sicuramente sbagliato qualcosa
nellimpostare lalgoritmo
76Funzioni e procedure
77Sottoprogrammi
- Nella creazione di un programma può accadere che
problemi simili appaiano in mole parti diverse - La soluzione più ovvia è quella di replicare il
codice in ogni parte dove è necessario, ma questa
soluzione non è certamente la più friendly - Se il codice ha un errore, infatti, esso sarà
replicato in tutte le parti in cui quel codice è
stato copiato - Se quella parte di codice viene aggiornata, sarà
necessario aggiornare tutte le parti in cui è
stato copiato - Molto poco flessibile e manutenibile
- uso di sottoprogrammi
78Sottoprogrammi
- Sottoprogrammi parti di codice separate dal
flusso principale del programma che possono
essere richiamate dove necessario senza replicare
il codice ogni volta - Un sottoprogramma deve essere innanzitutto
definito, cioè deve essere dichiarato da qualche
parte ciò che esso esegue, su cosa lo esegue e
cosa restituisce (se restituisce qualcosa) - Una volta definito, esso deve essere
implementato, cioè scritto il corpo del
sottoprogramma, con le variabili e le istruzioni
che permettono di eseguire il compito per cui è
stato progettato - Infine, una volta dichiarato e implementato, esso
può essere richiamato ogni qualvolta è richiesta
una operazione di quel tipo
79Funzioni e procedure
- Ma cosa sono di preciso i sottoprogrammi?
- Ogni sottoprogramma è un blocco più o meno lungo
di codice che riceve in ingresso alcuni parametri
(anche nessuno) e può restituire un valore - I parametri che riceve in ingresso sono i valori
che il chiamante passa alla procedura sono
quindi variabili in cui a run-time vengono
memorizzati i valori attuali dei parametri - Il sottoprogramma utilizza questi parametri per
eseguire il suo compito ed eventualmente
restituisce a sua volta al chiamante un valore
80Funzioni e procedure
- Formalmente due tipologie di sottoprogrammi sono
state individuate - Le funzioni, che in analogia con le funzioni
matematiche, ricevono alcuni valori in ingresso e
restituiscono un valore in uscita - Le procedure, che ricevuti alcuni dati in
ingresso, eseguono un compito che non può
concettualmente essere associato ad una funzione
(es. stampa, aggiornamento ecc) - Diciamo subito che la distinzione è abbastanza
formale e che chiamare entrambi funzioni o
procedure non comporta alcun problema - Le procedure, in efetti possono essere assimilate
a funzioni che non restituiscono alcun valore, ma
eseguono semplicemente un compito
81Funzioni e procedure
- I parametri che la funzione utilizza per
ricevere dati dallesterno sono variabili
locali - Ovvero esistono localmente alla funzione
- Come tali devono avere un tipo ed un nome
(univoco allinterno della funzione) - Anche il valore di ritorno della funzione è un
dato di un certo tipo, quindi nella descrizione
della funzione sarà necessario specificare che
tipo di dato essa ritorna
82Le funzioni
- Una funzione è una parte di programma individuato
da una etichetta che riceve in ingresso dei
parametri e ritorna un risultato, terminando
sempre il suo compito - I parametri dingresso sono chiamati
semplicemente parametri della funzione - Il valore di uscita è detto valore di ritorno
della funzione - Come accennato in precedenza una funzione deve
essere prima definita, ovvero deve venire
specificato la struttura della funzione stessa - il nome univoco della funzione
- i parametri che riceve in ingresso
- il tipo di dato che essa restituisce
83Dichiarazione di funzione
- Prima di implementare una funzione è necessario
dichiarare la sua interfaccia con lesterno, il
modo cioè con cui essa è invocata ed il tipo di
risultato che ritorna - La dichiarazione di una funzione specifica il
prototipo della funzione - Es
- double potenza( int x, int y) ? è una funzione
che riceve due parametri di tipo int (x e y) e
restituisce un double - Come la dichiarazione di una variabile non
inizializzava la variabile, così la dichiarazione
di una funzione non realizza la funzione - In questo modo per ora è stato dichiarata
solamente la interfaccia della funzione
84Dichiarazione di funzione
- La dichiarazione di una funzione (prototipo) è
utile per far capire al compilatore che la
funzione esiste e che forma ha - La funzione poi, potrebbe essere implementata in
qualsiasi altro file del progetto e compilata
dopo il file principale, ma limportante è che
nel file principale sia riportato il prototipo
della funzione che viene utilizzata altrimenti il
compilatore non può continuare - Questo metodo permette di scomporre il progetto
in più file in cui implementare le varie
procedure, organizzandole in maniera più
intelligente - E importante che quando una funzione viene
utilizzata in qualche file, sia specificato in
quel file il prototipo di quella funzione - Il linker si occupa di collegare il corpo della
funzione nei punti in cui la funzione è chiamata
85Dichiarazione di funzione
- La sintassi comune di una dichiarazione di
funzione è la seguente - tipo_di_dato nome_della_funzione (lista di
parametri) - Dove
- lista di parametri lista di dichiarazioni di
variabili visibili dallinterno e dallesterno
della funzione - Es.
- double potenza( int x, int y)
- Questa riga di codice dice che da qualche parte
esiste una funzione potenza che riceve in
ingresso due interi, esegue qualcosa e ritorna un
double.
86Dichiarazione di funzione
- Se una funzione (o procedura) non ritorna alcun
valore, si utilizza un tipo di dato apposito void
- void stampa(String file)
- È possibile infine avere funzioni che non
richiedono parametri, poiché quello che devono
fare è indifferente da valori esterni - void stampa()
- In molti linguaggi (C) è possibile specificare
valori di default ai parametri della funzione - Valori che se non forniti alla funzione
87Valori di default
- Se un parametro normalmente assume un certo
valore (K) e solo poche volte varia, è possibile
inserire il valore K come valore di default se
quel parametro non è fornito, la funzione
utilizza il valore di default - double potenza( int x, int y)
- Ad esempio
- double ris potenza(4,2) ? 16
- NB se un parametro ha un valore di default,
allora tutti i parametri seguenti devono avere un
valore di default, altrimenti durante la chiamata
con valore di default il compilatore non sa più a
quale parametro fare riferimento - double potenza( int x2, int y) ? se chiamiamo
int rpotenza(4) il compilatore non sa se
associare 4 ad x o a y.
88Implementazione della funzione
- La funzione deve comunque essere implementata
(cioè realizzata) da qualche parte nel progetto - La definizione del prototipo infatti non
specifica cosa deve fare la funzione - Limplementazione di una funzione avviene
dichiarando il prototipo della funzione stessa ed
iniziando immediatamente dopo un blocco di
istruzioni - double potenza( int x, int y)
- Lista di istruzioni
-
- Il blocco nellesempio qui sopra è delimitato
dalle parentesi graffe, come avviene nel C, C e
JAVA.
89Implementazione della funzione
- Allinterno del corpo di una funzione, possono
essere dichiarate nuove variabili, utilizzando
anche nomi di variabili utilizzati in altre
funzioni - Ogni variabile interna ad una funzione ha infatti
una visibilità limitata a quella funzione ed una
volta usciti dalla funzione le variabili vengono
deallocate - Le varibili allocate allinterno di una funzione
sono chiamate variabili locali, per distinguerle
dalle variabili globali che sono variabili
dichiarate fuori da ogni altra funzione, compresa
la principale e sono quindi visibili sempre ed
ovunque - Se allinterno di una funzione viene utilizzato
un nome di variabile globale, ogni riferimento a
quel nome viene reindirizzato alla variabile
locale
90Istruzione return
- Quando la funzione deve ritornare un valore è
necessario al suo interno contenga una istruzione
di ritorno valore che termina la funzione e
restituisce un dato conforma al prototipo - il valore viene restituito tramite listruzione
return - Sintassi
- return VALORE o VARIABILE
- Il tipo di valore restituito deve coincidere con
il tipo di valore specificato nella dichiarazione
della funzione, altrimenti il compilatore ritorna
errore.
91Istruzione return
- Il valore di ritorno può essere una valore
costante oppure il contenuto di una variabile - Nel secondo caso la variabile dovrà essere dello
stesso tipo del dato da ritornare - ES
- double potenza( int x, int y)
- double ris1
- for (int i0iltyi)
- risrisx
-
- return ris
- ris0
-
92Istruzione return
- Quando la funzione non deve ritornare alcun
valore listruzione return non è richiesta è
però possibile inserirlo senza specificare il
dato ritornato - void stampa(String file)
- L1
- return
-
93Istruzione return
- Listruzione return può essere utilizzata anche
per interrompere lesecuzione della funzione
senza attendere la fine della lista di
istruzioni - ad esempio può accadere che se una condizione è
verificata, la funzione ritorna un valore X,
altrimenti esegue altre operazioni e ritorna un
valore Y - In questo caso avremo due istruzioni return, ma
eseguite in maniera esclusiva (o luna o laltra) - ES.
- double potenza( int x, int y)
- if (y0)
- return 1
-
- double r(calcola la potenza xy)
- return r
-
94Chiamate alle funzioni
- Una volta dichiarata ed implementata, una
funzione può essere richiamata laddove è
necessario - la chiamata di una funzione è semplicemente il
nome della funzione seguito dai parametri da
passare - Es. double rpotenza( 2, 4)
- in questo caso r assume il valore ritornato
dalla funzione potenza con i parametri x2 e y4 - È possibile passare anche variabili invece che
valori numerici costanti - Es.
- int a2int b4double rpotenza(a,b)
95Chiamate alle funzioni
- Il prototipo deve necessariamente essere
rispettato - double potenza( int x, int y) ? double potenza(
2, 3) - se viene omesso qualche parametro, il
compilatore dà errore - il compilatore cerca una funzione con prototipo
diverso e non la trova
96Passaggio di parametri
97Passaggio di parametri
- Cosa succede quando si passa una variabile come
parametro ad una funzione? Ossia, se allinterno
della funzione modifico il valore di quel
parametro, il valore della variabile originaria
viene modificato ? - La risposta è dipende !!
- Esistono due modi per il passaggio dei parametri
- Per valore
- Per riferimento
98Passaggio di parametri per valore
- Quando è richiesto che i valori delle variabili
non siano modificati dal corpo della funzione,
quello che avviene è che il valore della
variabile viene copiato nella variabile-parametro
della funzione. In questo modo - ogni modifica alla variabile locale non si
riflette sulla variabile esterna - ogni modifica fatta alle variabili locali, però,
va persa alluscita dalla funzione e solo il
risultato ritornato è visibile dallesterno della
funzione - Questo metodo di passaggio dei parametri va sotto
il nome di passaggio per copia o per valore
99Passaggio di parametri per riferimento
- Quando invece è richiesto che le variabili
interne alla funzione siano non una copia di
quelle esterne, ma la stessa cosa, si parla di
passaggio per riferimento - In questo caso, la variabile locale assume il
riferimento in memoria alla variabile esterna - Quindi ogni modifica ad essa è una modifica alla
cella di memoria originale e permane anche alla
chiusura dalla funzione - Normalmente questa situazione è richiesta quando
si ha un sottoprogramma che deve effettuare delle
operazioni su un dato e non ritorna alcun valore - ES. aggiornamento del fatturato totale, ecc
- in questo caso il fatturato totale, una volta
aggiornato deve rimanere tale, quindi è
necessario operare laggiornamento sulla
variabile originale e non su una copia del valore
100Passaggio di parametri
- Notiamo che il passaggio per valore richiede la
copia della variabile nella nuova variabile
locale, mentre il riferimento ha un costo
costante, viene copiato comunque un indirizzo di
memoria - Se stiamo passando dati strutturati
particolarmente complessi, array di grandi
dimensioni, ecc il passaggio per valore può
essere molto più lento del passaggio per
riferimento - E anche vero però che il passaggio per valore è
molto più safety rispetto allaltro, dato che
qualsiasi modifica, anche quella non voluta ed
erronea non si riflette sul dato originario
101Passaggio di parametri
- Il metodo utilizzato per il passaggio dei
parametri cambia da linguaggio a linguaggio - Il C e C utilizzano il passaggio per valore, ma
forniscono dei tipi di dato che permettono di
realizzare il passaggio per riferimento
(puntatori e riferimenti) - Il JAVA utilizza il passaggio per riferimento. Il
passaggio per valore può comunque essere
effettuato costruendo ogni volta una copia del
parametro e passare quella alla funzione.
102Funzioni e procedure
- La differenza sul metodo di passaggio dei
parametri stabilisce la principale differenza tra
una funzione ed una procedura - Funzione lo scopo principale è quello di
utilizzare alcuni valori in ingresso per produrre
un valore di uscita, senza modificare nullaltro
? passaggio per valore - Procedura lo scopo principale è quello di
operare delle operazioni sui parametri in
ingresso che abbiano una semantica tale da
poterle racchiudere in un corpo unico ? passaggio
per riferimento - Comunque la differenza è solamente formale
- In C o in JAVA non esistono metodi per
dichiarare una funzione piuttosto che una
procedura. In realt