Operazioni sui puntatori
Dal punto di vista dell'assegnamento, una variabile di tipo puntatore si comporta
esattamente come una variabile di un qualsiasi altro tipo primitivo, basta tener
presente che il loro contenuto e` un indirizzo di memoria:
int Pippo = 5, Topolino = 10;
char Pluto = 'P';
int* Minnie = &Pippo;
int* Basettoni;
void* Manetta;
// Esempi di assegnamento a puntatori:
Minnie = &Topolino;
Manetta = &Minnie; // "Manetta"
punta a "Minnie"
Basettoni = Minnie; // "Basettoni"
e "Minnie" ora
// puntano allo stesso oggetto
I primi due assegnamenti mostrano come assegnare esplicitamente l'indirizzo
di un oggetto ad un puntatore: nel primo caso la variabile Minnie
viene fatta puntare alla variabile Topolino , nel secondo caso al
puntatore void Manetta si assegna l'indirizzo della variabile Minnie
(e non quello della variabile Topolino ); per assegnare il contenuto
di un puntatore ad un altro puntatore non bisogna utilizzare l'operatore & ,
basta considerare la variabile puntatore come una variabile di un qualsiasi
altro tipo, come mostrato nell'ultimo assegnamento.
L'operazione piu` importante che viene eseguita sui puntatori e quella di dereferenziazione
o indirezione al fine di ottenere accesso all'oggetto puntato;
l'operazione viene eseguita tramite l'operatore di dereferenzazione
* posto prefisso al puntatore, come mostra il seguente esempio:
short* P;
short int Val = 5;
P = &Val; // P punta a Val (cioe`
Val e *P
// sono
lo stesso oggetto);
cout << "Ora P punta a Val:" << endl;
cout << "*P = " << *P << endl;
cout << "Val = " << Val << endl << endl;
*P = -10; // Modifica l'oggetto puntato
da P
cout << "Val e` stata modificata tramite P:" << endl;
cout << "*P = " << *P << endl;
cout << "Val = " << Val << endl << endl;
Val = 30;
cout << "La modifica su Val si riflette su *P:" <<
endl;
cout << "*P = " << *P << endl;
cout << "Val = " << Val << endl << endl;
Il codice appena mostrato fa si` che il puntatore P riferisca
alla variabile Val , ed esegue una serie di assegnamenti sia alla
variabile che all'oggetto puntato da P mostrandone gli effetti.
L'operatore * prefisso ad un puntatore seleziona l'oggetto puntato
dal puntatore cosi` che *P utilizzato come operando in una espressione
produce l'oggetto puntato da P .
Ecco quale sarebbe l'output del precedente frammento di codice se eseguito:
Ora P punta a Val:
*P = 5
Val = 5
Val e` stata modificata tramite P:
*P = -10
Val = -10
La modifica su Val si riflette su *P:
*P = 30
Val = 30
L'operazione di dereferenzazione puo` essere eseguita su un qualsiasi puntatore
a condizione che questo non sia stato dichiarato void. In generale infatti non
e` possibile stabilite il tipo dell'oggetto puntato da un puntatore void
e il compilatore non sarebbe in grado di trattare tale oggetto.
Quando si dereferenzia un puntatore bisogna prestare attenzione che esso sia
stato inizializzato correttamente; la dereferenzazione di un puntatore inizializzato
a 0 e` sempre un errore, la dereferenzazione di un puntatore non inizializzato
causa errori non definiti (e potenzialmente difficili da scovare). Quando possibile
comunque il compilatore segnala eventuali tentativi di dereferenziare puntatori
che potrebbero non essere stati inizializzati tramite una warning.
Per i puntatori a strutture (o unioni) e` possibile utilizzare un altro operatore
di dereferenzazione che consente in un colpo solo di dereferenziare il puntatore
e selezionare il campo desiderato:
Persona Pippo;
Persona* Puntatore = &Pippo;
Puntatore -> Eta = 40;
cout << "Pippo.Eta = " << Puntatore -> Eta <<
endl;
La terza riga dell'esempio dereferenzia Puntatore e contemporaneamente
seleziona il campo Eta (il tutto tramite l'operatore -> )
per eseguire un assegnamento a quest'ultimo. Nell'ultima riga viene mostrato
come utilizzare -> per ottenere il valore di un campo dell'oggetto
puntato.
Sui puntatori e` definita una speciale aritmetica composta da somma e sottrazione.
Se P e` un puntatore di tipo T , sommare 1 a P significa
puntare all'elemento successivo di un ipotetico array di tipo T
cui P e` immaginato puntare; analogamente sottrarre 1 significa
puntare all'elemento precedente. E` possibile anche sottrarre da un puntatore
un altro puntatore (dello stesso tipo), in questo caso il risultato e` il numero
di elementi che separano i due puntatori:
int Array[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int* P1 = &Array[5];
int* P2 = &Array[9];
cout << P1 - P2 << endl; //
visualizza 4
cout << *P1 << endl; //
visualizza 5
P1+=3;
// equivale a P1 = P1 + 3;
cout << *P1 << endl; //
visualizza 8
cout << *P2 << endl; //
visualizza 9
P2-=5;
// equivale a P2 = P2 - 5;
cout << *P2 << endl; //
visualizza 4
Sui puntatori sono anche definiti gli usuali operatori relazionali:
< minore di
> maggiore di
<= minore o uguale
>= maggiore o uguale
== uguale a
!= diverso da
|