Fondamenti di Informatica A - Massimo Bertozzi I PUNTATORI
Fondamenti di Informatica A - Massimo Bertozzi Indirizzi in memoria ✗ Le variabili rappresentano uno spazio in memoria con nome, di conseguenza sono caratterizzate da un indirizzo di memoria ✗ Questo vale anche per altri costrutti del C/C++ come, ad esempio, le funzioni. ✗ Esistono casi in cui è comodo conoscere questo indirizzo Soluzione: puntatori
Fondamenti di Informatica A - Massimo Bertozzi L`operatore & ✗ Per conoscere e accedere all'indirizzo di una variabile è possibile utilizzare l'operatore & ✗ Attenzione: non confondere con i riferimenti! int a; // definisco una variabile cout << a; // ne stampo il valore cout << &a; // ne stampo l'indirizzo &a=1FF00H; // SBAGLIATO! solo rvalue!
Fondamenti di Informatica A - Massimo Bertozzi I puntatori: definizione ✗ Esiste un tipo di variabile che permette la memorizzazione degli indirizzi di memoria: i puntatori float a; int b; float *pa; // definizione puntatore a float int *pb, npb; // npb NON è un puntatore! pa=&a; // assegnazione valore pb=&b;
Fondamenti di Informatica A - Massimo Bertozzi L'operatore * ✗ L'operatore * permette l'accesso ai dati che risiedono all'indirizzo di memoria contenuto nei puntatori float pi=3.14f; float *gg; gg=π *gg=*gg+1; cout << *gg; // stampa 4.14
Fondamenti di Informatica A - Massimo Bertozzi Puntatori e funzioni ✗ In C i puntatori sono ampiamente utilizzati come argomento di funzione, in quanto: ✗ permettono l'accesso ai dati puntati permettendone di manipolarne il valore; ✗ può essere piú efficiente; ✗ permette di gestire funzioni che restituiscono piú valori ✗ In C++ si utilizzano i riferimenti!
Fondamenti di Informatica A - Massimo Bertozzi Aritmetica dei puntatori ✗ I puntatori sono un tipo di dato scalare, come tale è possibile l'utilizzo degli operatori + e -. ✗ Attenzione alle conversioni! int a=1, b=4, c; int *pa; c=a+b; // c==5 pa=&a+b; // NON è l'indirizzo di a+4 byte
Fondamenti di Informatica A - Massimo Bertozzi Aritmetica dei puntatori (2) ✗ Sommare (o sottrarre) ad un puntatore un numero intero corrisponde a far variare l'indirizzo contenuto nel puntatore di un numero di byte corrispondenti al numero intero moltiplicato per le dimensioni del tipo di dato puntato. int a[100], *b; b=a; cout << *(b+4); // equivalente a a[4]
Fondamenti di Informatica A - Massimo Bertozzi Puntatori e array ✗ I puntatori e gli array sono strettamente imparentati, in quanto è possibile ritenere un array come un puntatore costante: int a[100]; // definizione array di int cout << a; // stampo indirizzo in memoria int *b; // puntatore a int b=a; // assegno a b l'indirizzo dell'array b=&a[0]; // come sopra a=... // ERRATO!!! cout << *a; // stampo a[0]
Fondamenti di Informatica A - Massimo Bertozzi Gli operatori [ ] ✗ Gli operatori [ e ] sono equivalenti all'utilizzo di * e dell'aritmetica dei puntatori; ✗ infatti la notazione x[y] viene sempre tradotta in *(x+y) int a[100]; cout << a[4]; // stampa il quinto elemento cout << *(a+4); // equivalente cout << 4[a]; // equivalente (strano ma vero)
Fondamenti di Informatica A - Massimo Bertozzi La costante NULL ✗ La costante NULL è utilizzata convenzionalmente come valore di un puntatore che non punta a niente di significativo. ✗ Molte funzioni predefinite che restituiscono un puntatore nel caso falliscano o non trovino alcun risultato restituiscono NULL per segnalare questa evenienza. #define NULL (0)
Fondamenti di Informatica A - Massimo Bertozzi Il puntatore a void ✗ Quando occorre accedere ad aree di memoria in maniera svincolata dal tipo di dati è possibile farlo tramite puntatori al tipo void int p[100]; void *gp; // puntatore a void gp=(void *)p; // casting OBBLIGATORIO
Fondamenti di Informatica A - Massimo Bertozzi I puntatori a puntatori ✗ Come tutte le altre variabili i puntatori sono memorizzati da qualche parte in memoria: è possibile definire puntatori a puntatori int p; // variabile intera int *pp; // puntatore a variabile di tipo int int **ppp; // puntatore a puntatore a... // prototipo funzione main int main(int argc, char **argv);
Fondamenti di Informatica A - Massimo Bertozzi Allocazione dinamica della memoria ✗ In molti casi il programmatore non conosce la quantità di dati che deve trattare. ✗ Ipotesi sulla quantità massima di dati da gestire (array): ✗ spreco memoria ✗ possibilità di sbagliare ✗ Soluzione (puntatori): ✗ allocazione dinamica della memoria
Fondamenti di Informatica A - Massimo Bertozzi L'istruzione new ✗ Tramite l'istruzione new è possibile allocare una certa quantità di memoria da utilizzare, durante l'esecuzione di un programma char a[100]; // alloco 100 caratteri (deciso nel momento di compilare) char *b; int n; cin >> n; b=new char[n]; // alloco n caratteri (deciso durante l'esecuzione
Fondamenti di Informatica A - Massimo Bertozzi L'istruzione delete ✗ Aree di memoria allocate dinamicamente possono essere rilasciate qualora non servano piú tramite l'istruzione delete: double *p; p=new double[123];... delete[] p; // libero la memoria
Fondamenti di Informatica A - Massimo Bertozzi Errori comuni ✗ L'utilizzo di puntatori è fonte di possibili errori a volte estremamente difficili da individuare: ✗ buffer overrun ✗ memory leak ✗ lingering pointer
Fondamenti di Informatica A - Massimo Bertozzi Buffer overrun ✗ Come per gli array un errore comune è quello del buffer overrun. ✗ Nel caso dei puntatori si ha anche quando il puntatore non è stato inizializzato: char a[12]; char *b; *a='X'; // equivalente a a[0]='X'; *b='X'; // Buffer overrun!!
Fondamenti di Informatica A - Massimo Bertozzi Memory leak ✗ All'area di memoria allocata tramite l'istruzione new si può accedere solo tramite il puntatore: pericolo di memory leak; ✗ puntatori a durata automatica ✗ errore nelle assegnazioni int *p; p = new int[1000];... p = new int [1000]; // memory leak
Fondamenti di Informatica A - Massimo Bertozzi Lingering pointer ✗ Si parla di lingering pointer quando si ha un puntatore che fa riferimento a un'area di memoria non piú allocata: char *read_line(){ char temp[1000]; cin.getline(temp, 1000); return temp; }
Fondamenti di Informatica A - Massimo Bertozzi Puntatori a funzione ✗ Un caso particolare di puntatori è quello dei puntatori a funzione: int a[6]; // puntatore costante a degli interi int f(float); // puntatore costante a funzione int *ap; //puntatore a degli interi int (*fp)(float); // puntatore a funzione
Fondamenti di Informatica A - Massimo Bertozzi Motivazione puntatori a funzione ✗ I puntatori di funzione sono spesso utilizzati per gestire operazioni differenti senza duplicazioni di codice (si vedano come esempio gli algoritmi di ordinamento); ✗ in C possono essere utilizzati per emulare alcune funzionalità della programmazione ad oggetti; ✗ sono tipici dei device driver per alcuni sistemi operativi.
Fondamenti di Informatica A - Massimo Bertozzi Utilizzo puntatori a funzione int f(); // funzione che restituisce int int (*fp)(); // puntatore a funzione che restituisce int int answer; fp=f; // assegnazione fp=f(); // NO! fp=&f(); // NO answer=(*fp)(); // K&R argomento se necessario answer=fp(); // ANSI C
Fondamenti di Informatica A - Massimo Bertozzi Differenze nei puntatori a funzione ✗ Il caso dei puntatori a funzione è particolare ed esistono differenze significative rispetto ai puntatori ``normali'' ✗ non esistono puntatori di puntatori a funzione ✗ la concordanza tra i tipi restituiti è obbligatoria ✗ non vale l'artitmetica dei puntatori
Fondamenti di Informatica A - Massimo Bertozzi Linee guida per la comprensione ✗ In C le dichiarazioni tendono ad essere complesse. Nel caso dei puntatori è importante ricordare le seguenti regole: ✗ L'operatore array '[]' e l'operatore di funzione '()' hanno precedenza maggiore rispetto l'operatore '*'. ✗ Gli operatori '[]' e '()' vengono raggruppati da sinistra, mentre l'operatore '*' da destra.
Fondamenti di Informatica A - Massimo Bertozzi Analisi di una dichiarazione ✗ char *x[] viene decifrata nel modo seguente: 1. x[] è un array; 2. *x[] è un array di puntatori; 3. char *x[] è un array di puntatori a char;
Fondamenti di Informatica A - Massimo Bertozzi Dichiarazioni complesse ✗ int (*x[])() viene decifrata nel modo seguente: 1. x[] è un array; 2. (*x[]) è un array di puntatori; 3. (*x[])() è un array di puntatori a funzioni; 4. int (*x[])() è un array di puntatori a funzioni che restituiscono un intero.
Fondamenti di Informatica A - Massimo Bertozzi Esempi di dichiarazioni ✗ Le seguenti dichiarazioni sono tutte corrette: 1. int *(*pap)[]; 2. int *(*pfp)(); 3. int (*fpa())[]; 4. int (*fpf())(); 5. int *(**ppf)(); 6. int (*fpf())();