Puntatori in C e C++
La memoria La memoria del calcolatore è suddivisa in due parti: Stack (statica) Heap (dinamica) Nello Stack vengono immagazzinate tutte le variabili Es int x; 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: Nelle funzioni (passaggio per referenza, che permette la modifica degli argomenti in input); 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; 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: Notazione errata *p1.x=5; 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 derivato: ++ operatore unario di autoincremento di un puntatore derivato: += operatore di assegnamento con somma - operatore binario di differenza fra due puntatori - operatore binario di sottrazione di un intero da un puntatore derivato: --operatore unario di autodecremento di un puntatore derivato: -= operatore di assegnamento con differenza