Puntatori in C e C++ La memoria • La memoria del calcolatore è suddivisa in due parti: o Stack (statica) o Heap (dinamica) • Nello Stack vengono immagazzinate tutte le variabili • Es int x; o riserva sullo stack due byte per la variabile x • L’allocazione sullo stack è statica (non è possibile modificare il numero di byte assegnati a una variabile) Heap • E’ una memoria dinamica • Il programmatore può allocare e deallocare la memoria a suo piacimento. • La gestione diretta della memoria (allocazione e deallocazione) è un'operazione molto delicata che, se compiuta in modo errato, può portare ad errori runtime spesso difficili da individuare. • La gestione dell'Heap avviene tramite i puntatori. Puntatore • • • • • • Una variabile puntatore è una variabile che contiene l'indirizzo di memoria di un'altra variabile. Utilità dei puntatori: o Nelle funzioni (passaggio per referenza, che permette la modifica degli argomenti in input); o Per l’allocazione dinamica della memoria (definizione di strutture dati di dimensione variabile) La dichiarazione di una variabile puntatore definisce il tipo della variabile puntata, la specifica di puntatore (*) e il nome della variabile Es: int *px; o dichiara una variabile px che è un puntatore a un intero In C++, per conoscere l'indirizzo di una variabile, è sufficiente far precedere al nome della variabile l'operatore &. Esempio: int *px; // px è un puntatore a un int int x; // x è una variabile int px = & x; // px punta alla zona di memoria di x Operare con i puntatori • Dichiarazione e definizione di un puntatore che punta a una locazione di memoria contenenti un certo tipo di dato: • tipo *nome_variabile • es. int *x; char *y; • & (operatore unario che restituisce l’indirizzo di memoria dell’operando) • Es. se la variabile n è allocata all’indirizzo 1200. • Dopo l’esecuzione dell’istruzione: • x=&n • il valore di x è 1200. Operazioni con i puntatori • Dopo l’istruzione px=&x si crea un’associazione fra le due variabili • x=5 e *px=5 risultano equivalenti: inseriscono il valore 5 nella zona di memoria riservata a x int a = 5; int b = 8; int* pa = &a; int* pb = &b; a 5 pa b 8 pb Esempio #include <iostream> using namespace std; int main() { int a; // a è un intero int *aPtr; // aPtr è un puntatore a un intero a = 7; aPtr = &a; // aPtr punta allo stesso indirizzo di a cout << "Indirizzo di a: " << &a << " Valore di aPtr " << aPtr << endl; cout << "Valore di a: " << a << " Valore di *aPtr: " << *aPtr << endl; cout << "Notare che * e & sono uno l'inverso dell'altro" << endl; cout << "&*aPtr = " << &*aPtr << " *&aPtr = " << *&aPtr << endl; } Operatori • * (operatore unario che restituisce il valore della locazione di memoria puntata dall’operando). • Es. se la variabile x ha come valore 1200, e la locazione 1200 contiene il valore 555. • Dopo l’esecuzione dell’istruzione: y=*x; • Il valore di y è 555. • I puntatori possono essere confrontati tra di loro • * è il complemento di &. int x,y; int x,y; int *p; è equivalente a y=x; p=&x; y=*p; Allocazione dinamica della memoria in C • malloc() alloca porzioni contigue di memoria e restituisce l’indirizzo della locazione iniziale • void *malloc(numero di byte) • free() libera porzioni di memoria • void free(void *p) Es. char *p; p=malloc(1000); alloca 1000 byte in sequenza e p punta alla locazione iniziale. • La zona di memoria allocata attraverso malloc si trova in un'area di memoria speciale, detta heap (memoria dinamica). Allocazione dinamica della memoria in C++ • new <tipo> • alloca nella memoria heap un certo numero di byte sufficienti a contenere un dato di tipo <tipo> e restituisce l’indirizzo del primo byte • Es: int *px; px=new int; //alloca due byte • new <tipo>[<dimensione>] • alloca un array • Es: int *pv; pv=new int[20]; // alloca 20 interi (40 byte) • delete <variabile puntatore> • dealloca la memoria puntata dalla <variabile puntatore> • Es: delete px; NULL • La costante NULL è di tipo void* (quindi compatibile con tutti i tipi puntatore) • indica un puntatore che non punta a nulla: non può essere dereferenziato Esempio – vettore dinamico int dimesione,i; int *vet; //sarà l’array dinamico cout << "Dimensione dell’array da allocare: “; cin >> dimensione); vet = new int(dimensione); // allocazione del vettore // esempio di inserimento valori for(i=0;i<dimensione;i++) { cout << "Inserisci elemento v[" << i << "] "; cin >> vet[i]; } Puntatori a strutture • Esempio di struttura: struct punto { double x; double y; }; struct punto p1; struct punto *pun; pun=&p1; • Notazione errata *p1.x=5; • Notazione corretta (*p1).x=5; • Notazione migliore p1->x=5; Aritmetica dei puntatori • L’aritmetica dei puntatori si riferisce a un insieme di operazioni aritmetiche applicabili sui valori di tipo puntatore. • Le operazioni hanno lo scopo di consentire l'accesso a collezioni di dati omogenei conservati in posizioni contigue di memoria (esempio array). • L'aritmetica dei puntatori è tipica del linguaggio C ed è stata mantenuta in alcuni linguaggi derivati (è presente per esempio in C++ ma non in Java). Aritmetica dei puntatori in C • L'aritmetica dei puntatori del C è basata su tre operatori fondamentali, accompagnati da un certo numero di altri operatori la cui semantica è derivata da quella degli operatori principali: • + operatore binario di somma di un puntatore e un intero o derivato: ++ operatore unario di autoincremento di un puntatore o derivato: += operatore di assegnamento con somma • - operatore binario di differenza fra due puntatori • - operatore binario di sottrazione di un intero da un puntatore o derivato: --operatore unario di autodecremento di un puntatore o derivato: -= operatore di assegnamento con differenza