POINTERS Una variabile “pointer” è una variabile che ha come
Transcript
POINTERS Una variabile “pointer” è una variabile che ha come
POINTERS Una variabile “pointer” è una variabile che ha come valore un indirizzo di memoria. Es. Sia y una variabile intera di valore 5 e sia yPtr una variabile pointer avente come valore l’indirizzo di y. Si dice che yPtr “punta” ad y. int y = 5; int *yPtr; yPtr = &y; 0 7fff683ecbd0 y yPtr 5 7fff683ecbd0 che si può anche disegnare così: y yPtr 5 Inoltre poichè yPtr contiene l’indirizzo di una variabile intera già allocata, allora la notazione *yPtr indica tale variabile. *yPtr yPtr Es. (prosecuzione del precedente). Con l’istruzione: il valore della variabile y passa da 5 a 20. 1 5 *yPtr = 20; Dichiarazioni di variabili pointer L’operatore * premesso al nome di una variabile dichiara che la variabile è una variabile pointer (puntatore). Es. float *xPtr; dichiara che xPtr è una variabile puntatore ad un float. • È possibile dichiarare puntatori ad ogni tipo di dato (int, float, double, char, array, …) • Unica eccezione: void *Ptr che dichiara un pointer generico. • I puntatori possono essere inizializzati a 0, NULL , nel qual caso non puntano a niente. Due nuovi operatori coinvolti con pointers 1) (address operator) & L'operatore unario & applicato ad una variabile ritorna l’indirizzo del suo operando. 2) ( indirection/dereferencing operator) * L'operatore unario * applicato ad un pointer ritorna il valore puntato dal pointer. Oss. Puntatori void non possono essere deferenziati (il compilatore non sa quanti byte dereferenziare) Oss. Gli operatori & e * sono inversi. I loro effetti si cancellano: *&yvar == yvar e &*yPtr == yPtr Oss. Gli operatori & e * hanno precedenza molto alta, paragonabile a quella di ++ -- e, come loro, associano da destra a sinistra. Osservare come si può aumentare di uno *yPtr: *yPtr += 1; ++*yPtr; (*yPtr)++; 2 OPERAZIONI ARITMETICHE VALIDE SUI POINTERS Assegnazione. Si può fare solo fra pointers dello stesso tipo, altrimenti si deve usare l'operazione cast. Es.: int y, *xPtr , *yPtr = &y; double *zPtr; xPtr = yPtr; // zPtr = (double *) xPtr; xPtr punta a y Eccezione: Un puntatore void (tipo void *pt ) non può essere assegnato ad un altro puntatore senza l’operazione di cast, mentre ogni puntatore può essere assegnato direttamente ad un puntatore void. Assegnazione della costante 0 (indicata con la costante simbolica NULL). Pointers ed interi non sono intercambiabili, ad eccezione dello 0, che il linguaggio garantisce non poter essere un indirizzo valido. Somma o sottrazione di un intero. Indipendentemente dal tipo di oggetto cui il pointer ptr punta, ptr+n (o ptr-n) punta all’n-esimo oggetto oltre (o prima di) quello puntato da ptr. Es. main() { int *pt1, *pt2, mat[10]; pt1 = mat; pt2 = pt1+2; printf(" pt1 = %p\n pt2 = %p\n pt2 - pt1 = %d\n mat = %p\n", pt1, pt2, pt2 - pt1, mat); } E una stampa può essere: pt1 pt2 pt2 mat = = = 0x7fff6a3bdbd0 0x7fff6a3bdbd8 pt1 = 2 0x7fff6a3bdbd0 Confronto e sottrazione tra due pointers. Si possono fare solo tra pointers che puntano a membri di una stessa array. Il comportamento è indefinito tra pointers a membri di array diverse. La sottrazione restituisce il numero di elementi tra i due indirizzi. Inoltre è lecito confrontare un pointer con lo 0 o NULL. Es: if (xPtr == NULL) statement; 3 Passare una variabile pointer int main() void raddoppia(int *ptr) { } int num = 5, *numPtr; numPtr = # raddoppia(numPtr); numPtr ptr num Si ha lo stesso effetto di quando si passa il nome di una array, che però è un indirizzo costante. void raddoppia(int *ptr) { *ptr += *ptr ; } /*definizione*/ int main() { int num = 5, *numPtr; numPtr = # raddoppia(numPtr); /*chiamata */ /* raddoppia(&num); chiamata alternativa */ } e il valore della variabile num del main raddoppia. 4 Es.: void scambia(int x, int y) { int temp; // definizione errata temp = x; x = y; y = temp; } /* la chiamata */ scambia (a,b); /* non scambia i valori delle variabili a e b */ void scambia(int giusta { int temp; *ptx, int *pty) //definizione temp = *ptx; *ptx = *pty; *pty = temp; } /* ora la chiamata */ scambia(&a,&b); /* scambia i valori delle variabili a e b */ 5 POINTERS E ARRAYS La relazione è così forte da permettere che ogni operazione che può essere fatta con indici di arrays può essere fatta, e più velocemente, con i pointers. Esempio. Se int a[10], *aPtr = a; allora sono uguali i valori: a[j] aPtr[j] a + j aPtr + j a[j] *(aPtr+j) Notazione simile: matrice [indice] matrice + offset puntatore[indice] puntatore + offset L'unica differenza sta nel fatto che un poiter è una variabile e quindi sono corrette le espressioni aPtr = a e aPtr++, mentre il nome di una array è un puntatore costante e quindi cose del tipo a = aPtr oppure a++ non sono corrette. È anche possibile scrivere aPtr [-1], aPtr [-2], ecc. purchè gli elementi referenziati siano validi. Oss. Sono equivalenti le seguenti dichiarazioni di parametri formali nelle funzioni: char char (invece: s[] *s /* s è pointer a char */ int int mat[][3] (*mat)[3] /* mat è pointer ad array[3] di int */ int *mat[3] /* mat è array di 3 pointers a int */ 6