Strutture Dati Nicola Fanizzi Linguaggi di Dipartimento di Informatica Programmazione [010194] Università degli Studi di Bari 9 mag, 2016 Sommario 1 2 Tipi e sistemi di tipi Definizioni Sistemi di tipi Tipi scalari Tipi scalari e ordinali Booleani Caratteri 3 Numerici: interi, reali, complessi void Enumerazioni e intervalli Tipi Composti Record LdP.ITPS/[email protected] Array Insiemi Puntatori 4 Relazioni fra tipi Equivalenza Compatibilità e conversione di tipo Polimorfismo Controllo e Inferenza 5 Strutture dati e gestione della memoria Dangling reference Garbage collection Strutture Dati 9 mag, 2016 2 / 88 Agenda 1 Tipi e sistemi di tipi Definizioni Sistemi di tipi 3 Tipi Composti 4 Relazioni fra tipi 5 2 Tipi scalari LdP.ITPS/[email protected] Strutture dati e gestione della memoria Strutture Dati 9 mag, 2016 3 / 88 Introduzione Scopi dei Tipi di dato: Progetto: supporto all’organizzazione concettuale Programma: supporto alla correttezza Traduzione: supporto all’implementazione LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 4 / 88 Tipi Definizione (Tipo) Un tipo di dato è una collezione di valori omogenei, effettivamente presentabili, dotata di un insieme di operazioni che manipolano tali valori Omogeneità I valori condividono alcune proprietà strutturali Presentabilità Devono potere avere una presentazione finita per la loro scrittura es. non esiste un tipo dei numeri reali veri e propri Operazioni La stessa collezione di valori può essere impiegata in tipi diversi a seconda delle operazioni definite es. i tipi interi nei vari linguaggi LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 5 / 88 Tipi – utilità Progetto: Supporto all’organizzazione concettuale Dominare la complessità dei problemi Esplicitare i concetti tipici attraverso nuovi tipi Aumento di leggibilità (documentazione) e di sicurezza (controlli automatici) LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 6 / 88 Tipi – utilità [. . . cont.] Programma: Supporto alla correttezza Vincoli di tipo (problemi di semantica) errori HW es. salto a zona di memoria che non contiene codice errori logici es. somma intero + stringa Controlli semantica statica: Type checker come i commenti ma i controlli sono effettuabili automaticamente non risolvono tutti i problemi logici: es. formule della fisica, prima controlli sulle dim. Polimorfismo es. stessa funzione (es. sort) su strutture di tipi diversi Sicurezza: vincoli soddisfatti ma problemi rilevati in fase d’esecuzione? linguaggi sicuri e non es. dangling reference LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 7 / 88 Tipi – utilità [. . . cont.] Traduzione: Supporto all’implementazione Informazioni per la macchina astratta disponibili staticamente Dimensione richiesta per l’allocazione sia nel RdA sullo stack sia su heap Ottimizzazioni sulle op. d’accesso Calcoli statici Ind. oggetto + offset LdP.ITPS/[email protected] (no ricerca per nome) Strutture Dati 9 mag, 2016 8 / 88 Sistemi di tipi Sistema di tipi: complesso delle informazioni e delle regole che governano i tipi di un linguaggio 1 Insieme dei tipi predefiniti 2 Costrutti per definire nuovi tipi Meccanismi per il controllo dei tipi 3 Regole di equivalenza: due tipi formalmente diversi possono essere equivalenti livello semantico Regole di compatibilità: un valore di un tipo diverso da quello atteso può essere comunque utilizzato Regole di inferenza: attribuzione di un tipo ad un’espressione complessa in base alle componenti 4 se/quali vincoli controllare staticamente/dinamicamente LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 9 / 88 Sistemi di tipi - sicurezza Un sistema di tipi (e, per estensione, un linguaggio) si dice type-safe1 (sicuro a livello di tipi) quando nessun programma può ignorare le differenze tra tipi definite dal sistema/linguaggio nessun programma può generare errori inattesi derivanti da violazioni di tipo a run-time ad esempio: accesso a memoria non allocata chiamata di un valore che non si riferisce ad una funzione 1 o strongly typed (fortemente tipizzato). LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 10 / 88 Classificazione dei tipi In base ai valori (... ed alle operazioni): denotabili possono essere associati ad un nome esprimibili possono essere il risultato di una espressione complessa (più di un semplice nome) memorizzabili possono essere memorizzati in una variabile LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 11 / 88 Classificazione dei tipi [. . . cont.] Esempi Tipo delle funzioni (int -> int) valore denotabile: possiamo associare un nome ad una funzione 1 2 3 int succ (int x) { return x + 1; } valore esprimibile: solo nei ling. funzionali e non negli imperativi Funzioni risultato della valutazione di un’espressione valore memorizzabile idem (ML, HASKELL, SCHEME) LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 12 / 88 Sistemi di tipi – controlli Tipizzazione statica: controlli a compile-time (es. JAVA) Controlli anticipati Correttezza garantita per ogni sequenza d’esecuzione Controlli a run-time inutili: maggiore efficienza Progettazione più complessa se il linguaggio deve anche essere type-safe Compilazione lenta e complessa ma facilita debugging/testing Tipizzazione dinamica: controlli a run-time (es. LISP) Ogni oggetto ha un descrittore che ne contiene il tipo La macchina astratta controlla la correttezza degli operandi nelle operazioni Il compilatore ha generato codice di controllo opportuno Caratteristiche previene errori di tipo (troppo tardi?) esecuzione meno efficiente LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 13 / 88 Sistemi di tipi – controlli [. . . cont.] Osservazioni Programmi sicuri che la tipizzazione statica può male interpretare come errati: Controllo statico: più conservatore Esempio dato il frammento: int x; if (1==0) x = "errore"; 3 else x = x + 2; 1 2 l’esecuzione non causa errore ma risulta non corretto al controllo statico Controllo sui tipi in generale: problema indecidibile Per prudenza un controllo statico esclude anche casi non pericolosi, come il precedente In molti linguaggi (es. PASCAL) controllo statico+dinamico es. utilizzo di vettori con controllo degli indici a run-time LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 14 / 88 Indecidibilità del controllo sui tipi Controllo statico: più conservatore 1 2 3 int x; Proc(); x = "errore"; Se si potesse sapere sempre se Proc() termina allora si potrebbe segnalare l’errore di tipo E se Proc() non terminasse mai ? allora l’errore di tipo non si verificherebbe quindi il programma sarebbe corretto Essendo il problema della fermata indecidibile lo sarà anche quello del controllo della correttezza dei tipi LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 15 / 88 Agenda Enumerazioni e intervalli 1 2 Tipi e sistemi di tipi Tipi scalari Tipi scalari e ordinali Numerici: interi, reali, complessi void LdP.ITPS/[email protected] 3 Tipi Composti 4 Relazioni fra tipi 5 Strutture dati e gestione della memoria Strutture Dati 9 mag, 2016 16 / 88 Tipi scalari Tipi scalari (o semplici): tipi i cui valori sono atomici, i.e. non sono costituiti da aggregati di altri valori notazione type <nometipo> = <espressione>; Tipi ordinali (o discreti): dotati di una relazione d’ordine totale Valori: discreti booleani, caratteri, interi, enumerazioni ed intervalli Operazioni: precedente e successivo Utili per gli indici LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 17 / 88 Booleani Per valori logici (o di verità) Valori: uno per il vero (es. true) uno per il falso (es. false) Operazioni: congiunzione (and), disgiunzione (or), negazione (not), or esclusivo, uguaglianza Valori denotabili, esprimibili, memorizzabili Memorizzazione nelle minime unità di memoria indirizzabili LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 18 / 88 Caratteri Valori: un insieme di codici di caratteri fissato durante la progettazione del linguaggio Es: ASCII, UNICODE Operazioni: logiche uguaglianza, confronti, ordinali carattere successivo (succ) o precedente (prec) Valori denotabili, esprimibili, memorizzabili Memorizzazione con 1 (ASCII) o 2 (UNICODE) byte LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 19 / 88 Interi Valori: sottoinsieme finito dei numeri interi fissato durante la progettazione del linguaggio o della macchina astratta Di solito del tipo [−2t , +2t − 1] A volte sono possibili interi di lunghezza illimitata Operazioni aritmetiche e confronti Somma, differenza, prodotto, divisione (intera), resto, potenza Valori denotabili, esprimibili, memorizzabili Memorizzazione con un numero (pari) di byte in complemento a due LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 20 / 88 Reali numeri reali in virgola fissa Valori: sottoinsieme finito dei numeri razionali fissato durante la progettazione del linguaggio o della macchina astratta Ampiezza e granularità, ecc. dell’insieme dipendono dalla rappresentazione scelta Operazioni aritmetiche e confronti Somma, differenza, prodotto, divisione (intera), resto, esponenziale, radice quadrata Valori denotabili, esprimibili, memorizzabili Memorizzazione con un numero di 4 o 8 byte complemento a 2 numero fissato di bit per la parte decimale LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 21 / 88 Reali [. . . cont.] virgola mobile Valori: sottoinsieme finito dei numeri razionali fissato durante la progettazione del linguaggio o della macchina astratta Ampiezza e granularità, ecc. dell’insieme dipendono dalla rappresentazione scelta Operazioni aritmetiche e confronti Somma, differenza, prodotto, divisione (intera), resto, esponenziale, radice quadrata Valori denotabili, esprimibili, memorizzabili Memorizzazione con un numero di 4,8,10 byte Standard IEEE 754 LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 22 / 88 Complessi numeri complessi Valori: sottoinsieme finito dei numeri complessi fissato durante la progettazione del linguaggio Ampiezza e granularità, ecc. dell’insieme dipendono dalla rappresentazione scelta Operazioni aritmetiche e confronti Somma, differenza, prodotto, divisione (intera), resto, esponenziale, radice quadrata Valori denotabili, esprimibili, memorizzabili Memorizzazione con una coppia di reali in virgola mobile LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 23 / 88 void Valori: un solo valore void o unit (insieme singoletto) oppure { } una funzione matematica non può avere codominio vuoto sono tali le funzioni che divergono sempre Operazioni: nessuna Utile a denotare operazioni che non restituiscono un valore utile (ma hanno effetti collaterali) es. assegnamento (in molti linguaggi) ossia restituiscono un unico valore implicitamente LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 24 / 88 Enumerazioni Tipi semplici definiti dall’utente Valori: un insieme finiti di costanti caratterizzate da un proprio nome es. type giorni = (lun,mar,mer,gio,ven,sab,dom); Operazioni: confronti, operatori per raggiungere il valore precedente o il successivo Vantaggi Aumenta la leggibilità Ausilio del controllo dei tipi Memorizzazione: mappaggio sugli interi In alcuni linguaggi (C/C++, ADA, ...) si possono anche scegliere esplicitamente LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 25 / 88 Intervalli Tipi semplici definiti dall’utente Valori: sottoinsieme contiguo dei valori di un altro tipo scalare (tipo base) 1 2 type NumeriLotto = 1..90; Giorniferiali = lun..ven; Operazioni: confronti, operatori per raggiungere il valore precedente o il successivo Vantaggi Maggiore la leggibilità Ausilio al controllo dei tipi: dinamico Memorizzazione: mappati sugli interi Come per il tipo base LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 26 / 88 Agenda 1 Tipi e sistemi di tipi 2 Tipi scalari Array Insiemi Puntatori Relazioni fra tipi 4 3 Tipi Composti Record LdP.ITPS/[email protected] 5 Strutture dati e gestione della memoria Strutture Dati 9 mag, 2016 27 / 88 Tipi Composti Tipi non scalari, ottenuti per combinazione di tipi più semplici Record (o strutture): collezione di valori eterogenei Array: collezione di valori omogenei Insiemi: sottoinsiemi di un tipo (base, ordinale) Puntatori: l-valori per accedere indirettamente ad altri valori Tipi ricorsivi: definiti per ricorsione LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 28 / 88 Record (o strutture) Record: Collezione di un numero finito (e spesso ordinato) di elementi detti campi Ogni campo è caratterizzato dal nome dal tipo (anche diverso da quello degli altri campi) si può assimilare quindi ad una variabile Spesso è possibile annidare record all’interno di record LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 29 / 88 Record (o strutture) [. . . cont.] Esempio type studente = struct { int matricola; 3 float statura; 4 }; 1 2 LdP.ITPS/[email protected] Esempio annidamento type Aula = struct { char nome[5]; 3 int capienza; 4 struct { 5 char dipart[10]; 6 int tel; 7 } segreteria; 8 }; 9 ... 10 Aula a; 11 a.nome = ’B-1’ 12 a.segreteria.tel = 1234; 1 2 Strutture Dati 9 mag, 2016 30 / 88 Record (o strutture) [. . . cont.] Operazioni: selezione, indicata con “.” alcuni linguaggi ammettono assegnazione e test di uguaglianza tra interi record se non permesse bisogna procedere campo per campo l’ordine dei campi può essere significativo Memorizzazione in locazioni contigue nell’ordine di definizione Esempio nome capienza Diverse organizzazioni possibili dovute all’allineamento dipartimento Migliora l’efficienza nel reperimento LdP.ITPS/[email protected] arch. 32bit telefono Strutture Dati 9 mag, 2016 31 / 88 Record varianti e unioni Record varianti: record con campi mutuamente esclusivi Nomi e sintassi diverse per ogni linguaggio es. in PASCAL, record con una parte della struttura variabile: solo una delle varianti ammesse è significativa Esempio type studente = record nome: array [1..6] of char; 3 matricola: integer; 4 case fuoricorso: boolean of 5 true: (ultimoanno: 2000..maxint); 6 false:( inpari: boolean; 7 anno: (primo,secondo,terzo) 8 ) 9 end; 1 2 LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 32 / 88 Record varianti e unioni [. . . cont.] unioni strutture con campi mutualmente esclusivi Nomi e sintassi diverse per ogni linguaggio es. in C, struttura con un solo campo alla volta valido union per la parte variante struct studente { char[6] nome; 3 int matricola; 4 int fuoricorso; 5 union { int ultimoanno; 6 struct { int inpari; 7 int anno; 8 } studente_in_corso; 9 } campi_varianti; 10 } 1 2 LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 33 / 88 Record varianti e unioni [. . . cont.] Record varianti vs. Unioni Similarità Differenze Le varianti e le unioni condividono le stesse aree di memoria In C un campo della union è svincolato dal resto più flessibile più oneroso per il programmatore più rischioso Livelli di nomi: In PASCAL, campo come gli altri: s.inpari In C, occorre aggiungere ulteriori livelli di nomi LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 34 / 88 Record varianti e unioni [. . . cont.] Problema di sicurezza modifica tag discriminante con assegnamento ordinario (in PASCAL e C) La macchina astratta potrebbe controllare (dinamicamente) il tag per sapere se il record è usato correttamente risolve molti problemi semantici ma non tutti 1 type Tre = 1..3; ma anche var tmp: record case quale: Tre of 4 1: (a:integer); 5 2: (b:boolean); 6 3: (c:char); 7 end 8 ... 9 tmp.quale:=1; 10 tmp.a:=123; 11 write(tmp.c); 12 (* errore a run-time *) 2 3 LdP.ITPS/[email protected] tmp.quale:=1; tmp.a:=123; 3 tmp.quale:=3; 4 write(tmp.c); 5 (* semanticamente errato, 6 ma non rilevato *) 1 2 Strutture Dati 9 mag, 2016 35 / 88 Array Array: Collezione finita di elementi dello stesso tipo (tipo base) indicizzata su un intervallo di tipo ordinale (tipo indice) Specifica Nome Tipo indice Tipo dei componenti es. in C: int v[10]; Array multidimensionali: usando indici molteplici a volte come array di array op. di slicing ritagliare una “fetta”, fissando una dim. LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 36 / 88 Array [. . . cont.] Operazioni Selezione: si indica (spesso tra [...]) un’espressione il cui valore rappresenta l’indice dell’elemento da selezionare es. array monodimensionale: v[<espr>] es. array multidimensionale: m[<espr_1>][<espr_2>]...[<espr_n>] Assegnamento Test di uguaglianza ed altri test di confronto Op. dell’algebra lineare nei ling. che supportano il trattamento delle matrici Slicing slice: sezione di array costituita da elementi contigui in una data dimensione (es. un piano) in alcuni linguaggi si possono estrarre anche selezioni diagonali cornici, ecc. LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 37 / 88 Array [. . . cont.] Controlli Selezione entro i limiti del tipo indice A run-time Per un linguaggio safe: il compilatore genera controlli per ogni selezione generazione spesso disattivabile con un’opzione del compilatore Safety & security Attacchi buffer overflow LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 38 / 88 Array [. . . cont.] Memorizzazione Sezioni contigue di memoria Array monodimensionale Allocazione secondo l’ordine degli indici Array multidimensionale Ordine di riga Elementi contigui differiscono di un’unita nell’indice più a destra nella lista Ordine di colonna Elementi contigui differiscono di un’unita nell’indice più a sinistra nella lista Dividere nell’una o l’altra maniera può avere un impatto sull’efficienza del caching LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 39 / 88 Array [. . . cont.] Calcolo degli indici Dato un array m a n dimensioni di tipo T T m[L1 ,U1 ][L2 ,U2 ]...[Ln ,Un ] Sn : unità di mem. indirizzabili per elem. di T (per riga) Sn−1 = (Un − Ln + 1)Sn (slice di ordine superiore) ... S1 = (U2 − L2 + 1)S2 Per cercare l’indirizzo di m[i1 ][i2 ]...[in ] all’indirizzo iniziale, si somma l’offset: (i1 − L1 )S1 + (i2 − L2 )S2 + · · · + (in − Ln )Sn Se le dim. sono tutte note, meglio usare: i1 S1 + · · · + in Sn − (L1 S1 + · · · + Ln Sn ) n moltiplicazioni, n addizioni, n sottrazioni (che P non servono se gli indici partono da 0, i Li Si predeterminato) LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 40 / 88 Array [. . . cont.] Forma (shape): numero delle dim. e intervallo per ogni dim. Quando viene fissata ? Statica al momento della compilazione (dim. fissate) Array nel RdA (o nella mem. per le var. globali) Accesso tramite la formula precedente Elaborazione della dichiarazione Intervallo indici dipendente da un’espressione variabile Calcolo a run-time Array nel RdA: ma offset non noto! Parte fissa (offset) e parte variabile (ind. indiretto) Dinamica Limiti modificabili Allocazione sull’heap: (sulla pila non è possibile) Nel RdA: puntatore all’array LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 41 / 88 Array [. . . cont.] Dope vector: descrittore di array di forma non nota staticamente Allocato nella parte a lunghezza fissa del RdA riservata all’array Contiene: puntatore alla prima cella dell’area di mem. riservata all’array informazioni dinamiche utili (non memorizzate se determinabili staticamente) numero dimensioni limiti per ogni dim. (Li , Ui ) occupazione per ogni dim. (Si ) Accesso ad un elemento: accesso per offset tramite frame pointer al dope vector calcolo della formula precedente si somma all’indirizzo dell’inizio dell’array LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 42 / 88 Array [. . . cont.] ... lunghezza variabile m ... var locali lunghezza fissa L3 S3 L2 S2 L1 S1 dope vector di m puntatore ad m ... frame pointer ind.ritorno parametri LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 43 / 88 Insiemi Insieme: collezione di valori che costituiscono un sottoinsieme di un tipo base (universo) Solitamente il tipo base è ordinale (PASCAL) Esempi set of char S; set of Giorni IG; WE = (Sab, Dom); Operazioni Appartenenza (∈, in) Unione (+), intersezione (*), differenza (-) (a volte anche complemento) Rappresentazione vettori di bit (vettore caratteristico) Vj = 1 sse il j-esimo elemento dell’universo appartiene all’insieme Tabelle hash . . . LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 44 / 88 Puntatori Puntatore: tipo di variabili atte a contenere l-valori, direttamente manipolabili, utili a riferirsi indirettamente ad altre var. In genere è possibile indicare anche il tipo delle variabili puntate TipoBase * P; In PASCAL o ADA: possono solo puntare a variabili del tipo dato In C/C++: non c’è un vincolo stretto Ove presenti, consentono la definizione di tipi ricorsivi senza primitive apposite Nei linguaggi con variabili-riferimento, gli l-valori non possono essere manipolati direttamente valore nullo: il puntatore non punta ad alcuna variabile es. null (o nil, in PASCAL) LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 45 / 88 Puntatori [. . . cont.] Operatori Assegnamento di un valore ad un puntatore mediante Allocazione esplicita int *p; p = (int *) malloc(sizeof (int)); Costruttori (di oggetti) Operatore & float pigreco = 3.1415, *pp; pp = &pigreco; Dereferenziazione ^ (PASCAL) oppure * (in C/C++) es. precedente: float circ = 2 * r * (*pp ); Vale sia per l’l-valore sia per l’r-valore LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 46 / 88 Puntatori [. . . cont.] test di uguaglianza Creazione di strutture ricorsive Quando non previste dal linguaggio es. lista di interi (successione ordinata di dim. variabile) typedef nodo* lista_int; typedef struct { int val; 3 lista_int succ} nodo; 1 2 LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 47 / 88 Puntatori [. . . cont.] Aritmetica In C ed alcuni derivati: operazioni aritmetiche su puntatori Incremento/decremento di un puntatore: p++ / p-indirizzo incrementato di sizeof(tipobase) Sottrazione di puntatori: p1 - p2 Offset tra p1 e p2 Somma di un quantità ad un puntatore: p + n punta alla variabile con offset pari a n*sizeof(tipobase) Nociva per la type-safety del linguaggio Non c’è garanzia che in tutti i momenti un puntatore punti effettivamente ad una variabile del tipo atteso int *p; int *c; 3 p = (int*) malloc(sizeof(int)); 4 c = (char*) malloc(sizeof(char)); 5 p = p+1 6 c++; 1 2 LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 48 / 88 Puntatori [. . . cont.] Deallocazione implicita nessuno strumento per deallocare la memoria avviene quando non c’è più spazio sullo heap possibile recuperare memoria inutilizzata tecniche di garbage collection esplicita costrutto previsto dal linguaggio In C: funzione free() free(p) libera la memoria della variabile puntata da p e conviene anche assegnare: p=null; se p vale già null allora si ha un errore semantico Dangling reference: puntatori con valore diverso da null che puntano a zone non più significative memoria deallocata o ri-allocata riferimenti non validi alla pila d’esecuzione (anche con deallocazione implicita) LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 49 / 88 Tipi ricorsivi Tipi composti in cui un valore può contenere (un riferimento ad) un valore dello stesso tipo Esempi 1 2 (pseudocodice): type ListaInt = { int val; ListaInt next; } type AlberoChar = { char val; AlberoChar sinistro; 3 AlberoChar destro; } 1 2 Operazioni Selezione Test di uguaglianza LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 50 / 88 Tipi ricorsivi [. . . cont.] Rappresentati con strutture dati su heap Ling. Imperativi: Strutture concatenate (ottenute con riferimenti/puntatori) Allocazione esplicita Ling. Funzionali: espressione diretta di val. di tipi ricorsivi No deallocazione 1 ListaInt: (2,(33,(1,(4,(3,(1,21,null))))))) 2 CharAlbero: (A, 5 (B, 6 (C,null,null), 7 (D, 8 (E,null,null), 9 null 10 ), 11 (F,null,null) 12 ) 3 A 4 LdP.ITPS/[email protected] B C / / F / / D / E / / Strutture Dati 9 mag, 2016 51 / 88 Tipi di funzioni Alcuni linguaggi permettono di definire tipi di funzioni i valori di questi tipi sono funzioni tipo di funzione denotato con T f(S1 s1, S2 s2, ... , Sn sn) {...} oppure anche con S1 x S2 x ... x Sn -> T Operazioni Definizione (soprattutto funzionali) Applicazione (chiamata su parametri effettivi) Valori implementati come puntatori PASCAL, C, C++ LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 52 / 88 Agenda 1 Tipi e sistemi di tipi 2 Tipi scalari 3 Tipi Composti LdP.ITPS/[email protected] 4 Relazioni fra tipi Equivalenza Compatibilità e conversione di tipo Polimorfismo Controllo e Inferenza 5 Strutture dati e gestione della memoria Strutture Dati 9 mag, 2016 53 / 88 Equivalenza Regole utili a stabilire quando due tipi, formalmente diversi, sono intercambiabili, ossia non distinguibili nel loro uso per due tipi equivalenti, ogni espressione/valore del primo tipo è anche espressione/valore del secondo e viceversa Definizione di un nuovo tipo type nuovotipo = espressioneTipo Regole per la sua interpretazione: Definizione opaca: equivalenza per nome Definizione trasparente: equivalenza strutturale LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 54 / 88 Equivalenza per nome Ogni nuova definizione introduce un nuovo tipo Definizione (equivalenza per nome) Due tipi si dicono equivalenti per nome sse hanno lo stesso nome un tipo è equivalente solo a se stesso Esempio T1 T2 T3 T4 = = = = 1..10; 1..10; int; int; sono tutti diversi e non equivalenti LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 55 / 88 Equivalenza per nome [. . . cont.] Osservazioni vincolo indebolito in alcuni linguaggi (PASCAL) nell’es. la ridenominazione genera alias e non nuovi tipi (caso di T3 e T4) Ogni def. di tipo in un solo punto: OK dal punto di vista ingegneristico Equivalenza relativa ad un programma, non in generale LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 56 / 88 Equivalenza strutturale Definizioni trasparenti: nome come abbreviazione del nuovo tipo definito conta la sostituzione dei nomi con le definizioni dei tipi Definizione (Equivalenza strutturale) Due tipi T1 e T2 sono strutturalmente equivalenti sse hanno lo stesso nome, oppure T1 è definito con type T1 = espressione; ed espressione rappresenta un tipo equivalente a T2, oppure T1 e T2 definiti applicando lo stesso costruttore di tipo a due tipi tra loro equivalenti LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 57 / 88 Equivalenza strutturale [. . . cont.] Esempi (pseudo-codice) equivalenza di T3 e T4 type type 3 type 4 type 1 2 T1 T2 T3 T4 = = = = int; char; struct { T1 a; T2 b; } struct { int a; char b; } equivalenti? type S = struct { int a; int b; } type T = struct { int n; int m; } 3 type U = struct { int m; int n; } 1 2 equivalenti ? (ricorsione) 1 2 type R1 = struct { int a; R2 p; } type R2 = struct { int a; R1 p; } Osservazioni LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 58 / 88 Equivalenza strutturale [. . . cont.] Equivalenza strutturale non legata al singolo programma Sostituzione sempre possibile: si parla di trasparenza referenziale I linguaggi spesso adottano regole di equivalenza miste LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 59 / 88 Compatibilità Il tipo T è compatibile con il tipo S sse un valore del tipo T è ammesso in qualsiasi contesto in cui sarebbe richiesto un valore di tipo S Compatibilità più debole dell’equivalenza Due tipi equivalenti sono sempre compatibili (ma non viceversa) Relazione riflessiva, transitiva ma non simmetrica es. char e int Relazione adottata da molti linguaggi nella disciplina dell’assegnamento: tra il tipo dell’espressione (RHS) e quello della variabile (LHS) nella disciplina del passaggio parametri LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 60 / 88 Compatibilità [. . . cont.] Gradi di compatibilità: T compatibile con S 1 T e S equivalenti 2 I valori di T costituiscono un sottoinsieme dei valori di S es. tipi intervallo 3 Tutte le operazioni per S sono ammissibili sui valori di T es. record e op. di selezione “.” type S = struct {int a;} type T = struct {int a; char b;} rel. di sottotipo (nei linguaggi ad oggetti) 4 I valori di T corrispondono canonicamente a certi valori di S int e float 5 I valori di T corrispondono ad alcuni valori di S, tramite una convenzione per la trasformazione da T a S float e int, tramite arrotondamento, troncamento, . . . LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 61 / 88 Conversione di tipo Conversione implicita: operata tacitamente dalla macchina astratta si chiama anche coercizione, o conversione forzata Conversione esplicita: indicata mediante costrutti linguistici nel sorgente del programma si chiama anche cast LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 62 / 88 Coercizioni Se T compatibile con S: valori di tipo T sono ammessi dov’è atteso un valore di tipo S Conversione operata dalla m. astratta (o dal compilatore) Implementazioni Stessa rappresentazione livello sintattico, nulla da aggiungere Compatibilità canonica codice di conversione (a run-time) aggiunto dalla m. astratta es. da int a float Corrispondenza arbitraria es. dominio di T sovrainsieme di quello di S la macchina astratta inserisce codice per la conversione (e controllo dinamico per la type safety) es.: T è int e S è un intervallo di interi LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 63 / 88 Conversioni esplicite Annotazioni nel linguaggio che specificano il tipo in cui trasformare un valore di un altro tipo S s = (S)t; Possibilità Indicazione sintattica Indicazione per la macchina astratta (come prima) Vantaggi In genere, possibile anche quando basterebbe la compatibilità Maggiore leggibilità Indipendenza dal contesto sintattico Utili ad overloading e polimorfismo LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 64 / 88 Polimorfismo Sistema di tipi monomorfo: ogni oggetto ha un solo tipo Sistema di tipi polimorfo: ogni oggetto può avere più tipi Casi di polimorfismo in molti linguaggi (anche datati) operatore + int x int -> int oppure float x float -> float funzione length: non dipende dal tipo di vettore Funzioni polimorfiche indipendenti da tipi specifici Es. ordinamento di vettori (di interi, caratteri, ecc.) con uso di tipi generici: void sort(<T>[] v) Tipologie: Polimorfismo ad hoc (overloading) Polimorfismo universale p. parametrico p. di sottotipo o inclusione LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 65 / 88 Polimorfismo [. . . cont.] Polimorfismo ad hoc: overloading Nome sovraccaricato quando vi corrispondono più oggetti L’informazione del contesto aiuta a decidere staticamente es. operatore + es. funzioni con ugual nome ma tipo e numero di parametri differente Polimorfismo apparente Legato ai nomi più che agli oggetti del linguaggio es. funzioni distinte / codice distinto Può essere gestito con una fase di pre-processing assegna un nome interno diverso ai nomi sovraccarichi Overloading 6= Coercizione es. + polimorfico 1 + 2, 1.0 + 2.0, LdP.ITPS/[email protected] 1 + 2.0, 1.0 + 2 Strutture Dati 9 mag, 2016 66 / 88 Polimorfismo [. . . cont.] Polimorfismo parametrico universale: un valore può assumere tanti tipi diversi, ottenuti per istanziazione di un unico schema universale Codice di gestione unico che lavora uniformemente le differenze non contano Istanziazione automatica: compilatore o macchina astratta in base al contesto LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 67 / 88 Polimorfismo [. . . cont.] Esempi p. universale Valore null vale per tutti i tipi puntatore (T*) Tipo indicabile con <T>* void sort(<T>[] v) funzione di tipo <T>[]->void Funzione di swap() chiamata da sort() void swap(reference <T>, reference <T>) e istanziazione int * k = null; char v,w; 3 int i,j; 4 ... 5 swap(v,w); 6 swap(i,j); 1 2 LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 68 / 88 Polimorfismo [. . . cont.] Polimorfismo parametrico universale p. esplicito annotazioni esplicite: <T> Template C++, Generics JAVA 5.0+ p. implicito operato dal modulo di inferenza dei tipi Ling. di scripting Ling. funzionali: l’applicazione comporta istanziazione giusta es. fun Ide(x){return x;} è di tipo <T> -> <T> Funzioni di ordine superiore es. fun Comp(f,g,x){return f(g(x));} di tipo ((<S> -> <T>) × (<R> -> <S>) × <R>) -> <T> LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 69 / 88 Polimorfismo [. . . cont.] Non tutte le istanziazioni dello schema universale sono ammissibili, quindi: forma limitata tipica degli OOL una forma di compatibilità strutturale Nel p. universale di sottotipo: un valore può assumere una molteplicità di tipi diversi, ottenuti istanziando, in uno schema generale, un parametro con sottotipi di un tipo assegnato esprimibile tramite la notazione: ∀ S :< T.S -> void dove :< indica la rel. di sottotipo istanziabile con qualunque sottotipo S di T LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 70 / 88 Polimorfismo [. . . cont.] Implementazione gestione statica: a linking-time istanziazione funzioni polimorfe (in base ai tipi) produzione codice opportuno (più copie del template) e collegamento efficiente come per le funzioni non polimorfiche es. template C++ gestione dinamica: unica versione del codice generata rappresentazione più complessa: invece di allocare i dati sul RdA vengono allocati loro descrittori (dim., struttura, ind.) più flessibile ma meno efficiente a run-time es. in ML LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 71 / 88 Controllo di tipo Il controllo di tipo determina e controlla la compatibilità dei tipi degli oggetti: assegnazioni, dichiarazioni, uso parametri, conversioni, ... statico: modulo del compilatore (semantica statica) segue l’albero sintattico (bottom-up) determina e trasmette le informazioni sui tipi degli oggetti coinvolti dinamico: modulo di supporto al run-time LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 72 / 88 Inferenza di tipo Inferenza: deduzione dei tipi coinvolti in assenza di informazione esplicita spesso sostituisce il controllo di tipo es. in JAVASCRIPT, ML, . . . quando non si può determinare subito il tipo si utilizzano variabili di tipo: ’a es. fun f(n) { return n+1; } si può dedurre che f è di tipo int-> int caso difficile: fun g(v) { return v; } polimorfa LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 73 / 88 Inferenza di tipo [. . . cont.] Algoritmo: dato l’albero di derivazione assegnare un tipo/var. di tipo ad ogni nodo (anche variabile) risalire l’albero imponendo vincoli (uguaglianze di tipo) usare l’algoritmo di unificazione per risolvere i vincoli Esempio fun f(n) { return n+1; } <funzione> ’a = int->int fun f ( <par> ’z=int n ) <blocco> { <istruzione> } return <espr> ’x n LdP.ITPS/[email protected] Strutture Dati + ; ’x = ’y = int 1 ’y 9 mag, 2016 74 / 88 Agenda Relazioni fra tipi 4 1 Tipi e sistemi di tipi 5 2 Tipi scalari 3 Tipi Composti LdP.ITPS/[email protected] Strutture dati e gestione della memoria Dangling reference Garbage collection Strutture Dati 9 mag, 2016 75 / 88 Dangling reference Esempio 1 2 int *p = malloc(); int *q = malloc(); 3 4 5 *p = 123; *q = 321; 6 7 q=p; 8 9 free(p); 10 11 LdP.ITPS/[email protected] // q dangling reference! Strutture Dati 9 mag, 2016 76 / 88 Dangling reference - tombstone Si aggiunge un livello di indirizzamento indiretto, quello della tombstone allocazione di nuovo oggetto su heap/pila: crea tombstone copia tra puntatori: copia indirizzi di tombstone deallocazione (da heap/pila): inserimento valore nullo speciale LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 77 / 88 Dangling reference - tombstone [. . . cont.] q = p; free(p); p 123 123 q 321 321 RIP p = malloc(); q = malloc(); *p = 123; *q = 321; 321 Osservazioni spazio: memoria aggiuntiva cimitero: zona di memoria speciale per le tombstone riuso delle tombstone (garbage collection) tempo: doppia dereferenziazione eventualmente + garbage collection LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 78 / 88 Dangling reference – locks & keys lucchetto parola in memoria inizializzata con valore casuale associata all’oggetto allocato chiave parola in memoria corrispondente ad un lucchetto puntatore = indirizzo + chiave allocazione di nuovo oggetto su heap: crea lucchetto copia tra puntatori: copia di indirizzo e chiave dereferenziazione: controllo che la chiave del puntatore “apra” il lucchetto (abbia lo stesso valore) deallocazione: inserimento valore nullo speciale nel lucchetto LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 79 / 88 Dangling reference – locks & keys [. . . cont.] p = malloc(); q = malloc(); *p = 123; *q = 321; p q = p; free(p); 54321 123 54321 54321 123 54321 54321 0 12345 321 12345 54321 321 12345 54321 321 12345 q Osservazioni spazio: memoria aggiuntiva tempo : op. più efficienti che con il tombstone deallocazione automatica della mem. aggiuntiva LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 80 / 88 Garbage collection Deallocazione implicita GC: Storia dei LdP LISP ’60 −→ JAVA ’90 Fasi (non necessariamente separate) distinguere gli oggetti utilizzati dagli altri per sicurezza politica conservativa recuperare la memoria degli oggetti non utilizzati Classificazione dei GC contatori di riferimenti marcatura mark & sweep mark & compact copia LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 81 / 88 Garbage collection – contatori Un contatore (reference count) per ogni oggetto; solo per la m. astratta: inaccessibile al programmatore allocazione: inizializza il contatore a 1 assegnazione q = p; (o anche quando si esce da un ambiente locale con puntatori) incremento del contatore della var. puntata da p decremento del contatore della var. puntata da q deallocazione: quando un contatore raggiunge il valore 0 si può deallocare la memoria e restituirla alla lista libera se l’oggetto da deallocare contiene puntatori applica la procedura ricorsivamente Osservazioni vantaggi: semplicità e incrementalità svantaggi: inefficienza; GC non funziona con str. circolari LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 82 / 88 Garbage collection – mark & sweep mark marcatura oggetti “non in uso” attraversando l’heap a partire dai puntatori sulla pila (root set), visita in ampiezza/profondità degli oggetti referenziati, lungo gli archi rappresentati dai puntatori, etichettando gli oggetti attraversati come “in uso” sweep deallocazione degli oggetti marcati come “non in uso” Osservazioni (svantaggi) non incrementale: parte quando la memoria si sta esaurendo frammentazione esterna inefficiente: tempo proporzionale alla dim. dell’heap sfavorisce la località dei riferimenti in memoria LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 83 / 88 Garbage collection – pointer reversal Per visitare agevolmente strutture concatenate (es. alberi) in fase di deallocazione occorre uno stack (ricorsione) Con il rovesciamento dei puntatori basta ricordare il nodo corrente e quello precedente A B C / F / / B p.prec C D / / corrente E / / LdP.ITPS/[email protected] A stack: A B C Strutture Dati / F / / D / p.corr E / / 9 mag, 2016 84 / 88 Garbage collection – mark & compact Problema della frammentazione causato dal mark & sweep Fase di sweep =⇒ fase di compattamento oggetti spostati in zone di mem. contigue più passaggi necessari Osservazioni svantaggi più passaggi necessari: marcatura calcolo nuovi puntatori, spostamento, ... vantaggi no frammentazione località dei riferimenti lista libera monoblocco LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 85 / 88 Garbage collection – copia No fase marcatura: copia (e compattazione) dei blocchi vivi GC stop & copy: heap diviso in 2 semispazi (FromSpace,ToSpace) normalmente solo 1 in uso (FromSpace); memoria libera = unico blocco memoria esaurita: chiamata al GC a partire dal root set si copia (alg. di Cheney) nell’altro semispazio (toSpace), compattando quindi ToSpace e FromSpace si scambiano i ruoli Osservazioni vantaggi allocazione efficiente: soprattutto se è noto il fabbisogno degli oggetti vivi contemporaneamente no frammentazione LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 86 / 88 ToSpace FromSpace Garbage collection – copia [. . . cont.] root set ToSpace FromSpace root set libera LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 87 / 88 Riferimenti Gabbrielli & Martini: Linguaggi di Programmazione, McGraw-Hill. 2a edizione. Cap. 10 LdP.ITPS/[email protected] Strutture Dati 9 mag, 2016 88 / 88