int a = b; |
a
, si accede la zona di memoria della variabile b
, si legge il valore in essa contenuto e lo si copia nella cella della variabile a
. Tale modello non è del tutto conforme alle operazioni realmente eseguite a un programma, ma è sufficiente per schematizzare qualitativamente il funzionamento di esso, riguardo le variabili.
Dopo la precedente breve introduzione, vediamo cosa è un puntatore: così come la variabile è l'astrazione software della cella di memoria, un puntatore è l'astrazione software dell'indirizzo di tale cella. Si tratta dunque di uno strumento che ci fornisce un nuovo accesso ad una variabile: non tramite valore, ma tramite indirizzo. I puntatori sono delle variabili come tutte le altre (aventi a loro volta degli indirizzi di memoria ...), dichiarate (o definite) grazie al primo derivatore di tipi che incontriamo: il segno di moltiplicazione *
, applicato al tipo della variabile al momento della creazione di essa.
int* a |
int *a |
puntatore a variabile intera | |
double* a |
double *a |
puntatore a variabile reale | |
char* a |
char *a |
puntatore a variabile carattere |
*
a destra del tipo o a sinistra della variabile) sono equivalenti; in generale si consiglia di adottare la prima delle due, che meglio evidenzia il fatto che *
sia un costruttore di tipo. Si faccia attenzione però che l'operatore *
si applica solo alla prima variabile dichiarata, cioè:
// ex5_1_1.cpp void main() { char* a; double *b; int* c, d; // ATTENZIONE }nello statement
int* c, d
la variabile c
è un puntatore ad interi, mentre la variabile d
è una variabile intera! Per evitare simili errori, è conveniente dichiarare sempre una sola variabile per statement; negli esempi presentati in questo testo non adotteremo tala regola per motivi di publishing.
Se abbiamo una variabile intera, possiamo assegnarle un qualunque valore
intero, perché essa è l'astrazione di una cella di memoria contenente
numeri interi; cosa possiamo invece assegnare ad un puntatore? Essendo esso
l'astrazione dell'indirizzo di una variabile, possiamo assegnargli solo
indirizzi di variabili, le quali abbiano lo stesso tipo del puntatore: se abbiamo dichiarato un puntatore con int* a
, possiamo assegnare ad a
solo indirizzi di variabili intere, altrimenti il compilatore tornerà un messaggio di errore. Nasce però un problema: come facciamo a sapere gli indirizzi delle variabili che abbiamo creato? Non c'è nessun modo, se non quello di utilizzare il nuovo operatore indirizzo: &
(si legge at, che in inglese vuol dire ``presso''), il quale ha come unico compito quello di tornare l'indirizzo della variabile cui è applicato. Vediamo un esempio di indirizzi di variabili:
// ex5_1_2.cpp #include <iostream.h> void main() { int n = 19; double x = 2.71; cout << &n << "\n"; cout << &x << "\n"; }
esempio di output:
0xbffff844
0xbffff83c
Il programma ci ha stampato due indirizzi; qualche osservazione:
cout
nel sistema esadecimale, di cui non abbiamo trattato; si può immaginare comunque di cosa si tratti: un sistema di numerazione posizionale, il quale ha 16 cifre (0-9,A-F), mentre il sistema decimale ne ha 10 (0-9)e quello binario solo 2 (0,1); per convenzione i numeri in formato esadecimale hanno il prefisso ``0x'';