Università dell’Insubria Facoltà di Scienze Matematiche, Fisiche e Naturali di Varese Corso di Laurea in Informatica Anno Accademico 2004/05 Laboratorio di Linguaggi lezione IV Marco Tarini Laboratorio di Linguaggi • docente: Marco Tarini e-mail: [email protected] • ricevimento: Martedì 14:30 - 17:30 o anche su appuntamento • libro di testo consigliato: Kelley Al, Pohl Ira: "C Didattica e Programmazione" ("A Book on C") quarta edizione - anche la terza va bene Marco Tarini ‧ Laboratorio di Linguaggi ‧ 2004/05 ‧ Università dell’Insubria Assegnare i Puntatori • In memoria, un puntatore è un indirizzo di memoria – (...di una variabile) – (...di cui e' noto il tipo) • Bene, ma quale indirizzo? – Modo 1: prendere l'indirizzo di una variabile esistente • il puntatore punterà a quella variabile – Modo 2: allocare (riservare, prenotare) della memoria libera • il puntatore punterà ad una nuova variabile, memorizzata nella memoria così riservata • la nuova variabile è allocata dinamicamente! Marco Tarini ‧ Laboratorio di Linguaggi ‧ 2004/05 ‧ Università dell’Insubria Assegnare i Puntatori • Modo 1: prendere l'indirizzo di una variabile esistente – il puntatore punterà a quella variabile • Operatore "ampersand" ( • Esempio: il puntatore p punta all'indirizzo di memoria dove vive la variabile d & ) double d = 9.0; double *p; p = &d; Marco Tarini ‧ Laboratorio di Linguaggi ‧ 2004/05 ‧ Università dell’Insubria Uso dei Puntatori come Parametri • vi ricordate quel problemino? void raddoppia (int { x = x * 2; } x) void raddoppia (int* x) { *x = *x * 2; } int main(){ int incassi = 5; raddoppia( incassi ); ... } int main(){ int incassi = 5; raddoppia( &incassi ); ... } compila, ma non funziona non raddoppio "incassi", ma una sua copia temporanea funziona Remember: in C i paramatri sono passati sempre per copia ! Marco Tarini ‧ Laboratorio di Linguaggi ‧ 2004/05 ‧ Università dell’Insubria Uso dei Puntatori come Parametri • un'altra motivazione possibile: efficienza int eta_fra_10_anni (Persona pp) { return pp.eta + 10; } inefficiente int eta_fra_10_anni (Persona * pp) { return pp->eta + 10; } efficiente In questi casi, però, meglio aggiungere anche la keyword const : int eta_fra_10_anni (const Persona * pp) { return pp->eta + 10; } • più informazione presente nel codice per il programmatore (come un commento) • più ottimizzazioni possibili da parte del compilatore • più controllo di errori a tempo di compilazione (per esempio se per sbaglio si tenta di cambiare il valore del parametro) Marco Tarini ‧ Laboratorio di Linguaggi ‧ 2004/05 ‧ Università dell’Insubria Uso dei Puntatori come Parametri • Riassumendo: void Procedura( TipoParametro x) { ... } void Procedura( TipoParametro* x) { ... } void Procedura( const TipoParametro* x) { ... } Marco Tarini ‧ Laboratorio di Linguaggi ‧ passaggio di parametro per copia (l'unico possibile in C) tecnicamente, un altro passaggio di parametro per copia ma ciò che si copia è un puntatore, cioè un riferimento! in pratica, (simulazione di) un passaggio per riferimento 2004/05 ‧ Università dell’Insubria Uso dei Puntatori come Parametri • Riassumendo: void Procedura( TipoParametro x) { ... } usare quando la procedura non deve cambiare il valore del parametro void Procedura( TipoParametro* x) { ... } usare quando la procedura deve cambiare il valore del parametro void Procedura( const TipoParametro* x) { ... } (chiaramente, lo stesso vale per le funzioni) Marco Tarini ‧ Laboratorio di Linguaggi ‧ usare quando la procedura non deve cambiare il valore del parametro ...ma sarebbe troppo oneroso fare la copia del parametro (parametro voluminoso!) 2004/05 ‧ Università dell’Insubria Assegnare i Puntatori • In memoria, un puntatore è un indirizzo di memoria – (...di una variabile) – (...di cui e' noto il tipo) • Bene, ma quale indirizzo? – Modo 1: prendere l'indirizzo di una variabile esistente • il puntatore punterà a quella variabile – Modo 2: allocare (riservare, prenotare) della memoria libera • il puntatore punterà ad una nuova variabile, memorizzata nella memoria così riservata • la nuova variabile è allocata dinamicamente! Marco Tarini ‧ Laboratorio di Linguaggi ‧ 2004/05 ‧ Università dell’Insubria Allocazione void* malloc(unsigned int n); funzione malloc = m-emory alloc-ation 1 - alloca n bytes di memoria. 2 - restituisce l' indirizzo della memoria appena allocata • sotto forma di puntatore generico ! • " " puntatore generico, puntatore void* senza tipo, in pratica, un semplice indirizzo di memoria Marco Tarini ‧ Laboratorio di Linguaggi ‧ 2004/05 ‧ Università dell’Insubria Allocazione: esempio int* p; p = malloc( ? 4 ); Errore di tipo! A sx abbiamo un (int*) mentre a dx un (void*) Il tipo è diverso, ma si tratta sempre di un indirizzo di memoria! Soluzione: basta fare un type-cast: int* p; p = (int*) malloc(4); Marco Tarini ‧ Laboratorio di Linguaggi ‧ 2004/05 ‧ Università dell’Insubria Allocazione: e se la memoria finisce? void* malloc(unsigned int n); Se non c'è più memoria, l'allocazione "fallisce" e malloc restituisce il valore speciale NULL • semanticamente, NULL è un "puntatore che non punta a nulla" • in memoria, NULL è rappresentato dal valore 0 Il valore resituito dalle malloc va controllato ! int* p; p = (int*) malloc(4); if (p == NULL) { /* finita memoria ... */ } Marco Tarini ‧ Laboratorio di Linguaggi ‧ oppure, più coincisamente if (!p) { 2004/05 ‧ Università dell’Insubria Allocazione typedef struct { \*blah blah... un sacco di campi, array...*\ } TipoStrano TipoStrano* p; p = (TipoStrano *) malloc(sizeof(TipoStrano)); Il costrutto sizeof è estremamente utile con le malloc. Usare sempre, anche con i tipi base • int, short, float, double... • remember: il C non prescrive quanti bytes occupano! Marco Tarini ‧ Laboratorio di Linguaggi ‧ 2004/05 ‧ Università dell’Insubria Dellocazione void free(void* p); libera la memoria che era stata allocata all'indirizzo p. Nota: p deve essere il risultato di una malloc! int* p; p = (int*) malloc(sizeof(int)); ... /* uso *p */ free(p); se mi dimentico di deallocare, ho un cosiddetto memory leak Remember: non c'è alcuna garbage collection in C ! Marco Tarini ‧ Laboratorio di Linguaggi ‧ 2004/05 ‧ Università dell’Insubria Allocazione e Deallocazione: esempio k viene automaticamente allocato (i 4 bytes di memoria necessari al suo immagazzinamento vengono "prenotati"). int proc() { int k; k=15; k viene inizializzato (a 15) ... /* lavora con k */ return 0; all'uscita dalla procedura, i 4 bytes sono resi di nuovo disponibili }; k viene esplicitamente allocato. (a tempo di esecuzione, si trovano i 4 bytes di memoria necessari al suo immagazzinamento. La locazione viene memorizzata in k). k viene inizializzato (a 15) all'uscita dalla procedura, dobbiamo rendere i 4 bytes di nuovo disponibili esplicitamente Marco Tarini ‧ Laboratorio di Linguaggi ‧ usando l'allocazione dinamica: int proc() { int* k; k = (int*)malloc( sizeof(int) ); *k = 15; ... /* lavora con *k */ free(k); return 0; }; 2004/05 ‧ Università dell’Insubria Vi ricordate quell'altro problemino... • Cosa succede, se non si sa a priori* quanti elementi di un array ci serviranno**? – * quando scriviamo il programma – ** a tempo di esecuzione • Necessità allocazione dinamica di array. Marco Tarini ‧ Laboratorio di Linguaggi ‧ 2004/05 ‧ Università dell’Insubria Allocazione di vettori (void*) calloc(unsigned int n, unsigned int size); calloc = c-ontiguous alloc-ation Alloca n elementi contigui ciascuno grande size. In pratica, alloca un area di memoria grande n x size Per il resto funziona come malloc Esempio: int* p; p = (int*) calloc(100000,sizeof(int) ); Alloca un vettore di 100000 interi. Marco Tarini ‧ Laboratorio di Linguaggi ‧ 2004/05 ‧ Università dell’Insubria Allocazione di vettori • Ricordiamoci sempre: B) vettore allocato dinamicamente: int* v = (int*) calloc(100000,sizeof(int) ); A) fixed size vector: int v[100000]; • In entrambi i casi: • ho un vettore di 100000 interi • posso scrivere ad esempio: v[2]= v[0] + 3 * v[1] ; Marco Tarini ‧ Laboratorio di Linguaggi ‧ 2004/05 ‧ Università dell’Insubria Allocazione di vettori • Ricordiamoci sempre: B) vettore allocato dinamicamente: int* v = (int*) calloc(100000,sizeof(int) ); A) fixed size vector: int v[100000]; • Differenza 1: dimensione variabile • se x è una var intera, posso scrivere: int* v = (int*) calloc(x,sizeof(int) ); • ma non posso scrivere: int v[x]; qua è richiesta una costante! Marco Tarini ‧ Laboratorio di Linguaggi ‧ 2004/05 ‧ Università dell’Insubria Allocazione di vettori • Ricordiamoci sempre: B) vettore allocato dinamicamente: int* v = (int*) calloc(100000,sizeof(int) ); A) fixed size vector: int v[100000]; • Differenza 2: se ho allocato, devo deallocare int* v = (int*) calloc(100000,sizeof(int) ); ... /* usa v */ free(v); Marco Tarini ‧ Laboratorio di Linguaggi ‧ 2004/05 ‧ Università dell’Insubria Allocazione di vettori • Ricordiamoci sempre: B) vettore allocato dinamicamente: int* v = (int*) calloc(100000,sizeof(int) ); A) fixed size vector: int v[100000]; • Differenza 3: fixed size = più efficiente • • • • • il solito prezzo da pagare per l'uso dei puntatori... 0xAA000000 + 2 x sizeof(int) ...maggiorato ma precalcolato staticamente mettiamo che v valga 0xAA000000 : v[2] se fixed: READ TEMP 0xAA000008 compilazione se dinamco: READ TEMP0 0xAA000000 v[2] compilazione Marco Tarini ‧ Laboratorio di Linguaggi ‧ ADD TEMP0 READ TEMP 8 TEMP0 2004/05 ‧ Università dell’Insubria Allocazione di vettori • Ricordiamoci sempre: B) vettore allocato dinamicamente: int* v = (int*) calloc(100000,sizeof(int) ); A) fixed size vector: int v[100000]; • Differenza 4: • vengono allocati in zone diverse della memoria... • come vedremo nella lezione sulla gestione della memoria Marco Tarini ‧ Laboratorio di Linguaggi ‧ 2004/05 ‧ Università dell’Insubria Puntatori: operazioni a basso livello Esercizio: un numero in virgola mobile a doppia precisione di solito, ma dipende dall'implementazione • Sappiamo che un double occupa 8 bytes. • Dato un double, quale è il valore di questi 8 bytes? • Per esempio, quali 8 bytes compongono il valore 3.14159256 ? Marco Tarini ‧ Laboratorio di Linguaggi ‧ 2004/05 ‧ Università dell’Insubria Puntatori: operazioni a basso livello 1B903C32 1B903C33 1B903C34 1B903C35 1B903C36 1B903C37 1B903C38 1B903C39 1B903C3A 1B903C3B 1B903C3C spazio riservato per la variabile d (8 bytes) int main() { dobule d; Marco Tarini ‧ Laboratorio di Linguaggi ‧ 2004/05 ‧ Università dell’Insubria Puntatori: operazioni a basso livello 1B903C32 1B903C33 1B903C34 1B903C35 1B903C36 1B903C37 1B903C38 1B903C39 1B903C3A 1B903C3B 1B903C3C codifica di 3.14159256 spazio riservato per la variabile d (8 bytes) un puntatore a Byte che farà da vettore di Bytes ma il tipo "Byte" non è definito! Definirlo, ad es fuori dalla proc "main" (vedere lez. 2) int main() { dobule d; d = 3.14159256; Byte * v ; Marco Tarini ‧ Laboratorio di Linguaggi ‧ 2004/05 ‧ Università dell’Insubria Puntatori: operazioni a basso livello 1B903C32 1B903C33 1B903C34 1B903C35 1B903C36 1B903C37 1B903C38 1B903C39 1B903C3A 1B903C3B 1B903C3C vc[0]o dv[1] v[6]2 5v6 [7] i f i vc[2]a dv[3] i 3v.[4]1 4v[5] 159 spazio riservato per la variabile d (8 bytes) int main() { dobule d; int i; d = 3.14159256; Byte * v ; v = & (Byte*) d; & d; for (i=0; i< i<8; sizeof(double); i++) i++) printf("%d,",v[i]); system("Pause"); }; Marco Tarini ‧ Laboratorio di Linguaggi ‧ 2004/05 ‧ Università dell’Insubria Puntatori: operazioni a basso livello • A casa, provate questo programma, e scopriamo: – quali sono gli 8 bytes che compongono il double 3.14159256 – quali sono gli 8 bytes che compongono il double 6.022 x 1023 • e, adattando il programma agli interi: – quali sono i 4 bytes che compongono l'int +1000000 – quali sono i 4 bytes che compongono l'int +1 – quali sono i 4 bytes che compongono l'int -1 • a seconda di quale architettura viene usata, potremmo trovare risposte diverse! – domanda: sarebbe potuto succedere in Java? Marco Tarini ‧ Laboratorio di Linguaggi ‧ 2004/05 ‧ Università dell’Insubria Puntatori • Molto potenti... – vettori di dimensione determinata dinamicamente – passaggio di parametri per riferimento • in un linguaggio che prevede passaggio solo per copia – possibilità di scrivere codici più efficienti – controllo diretto delle risorse, a basso livello – strutture dati flessibili • per esempio: typedef struct { char nome[20]; char cognome[20]; int eta; Persona* padre, madre; } Persona; Marco Tarini ‧ Laboratorio di Linguaggi ‧ 2004/05 ‧ Università dell’Insubria Puntatori • Molto potenti... "Ad un grande potere corrisponde una grande responsabilità." – lo zio di Spiderman Marco Tarini ‧ Laboratorio di Linguaggi ‧ 2004/05 ‧ Università dell’Insubria