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