# Puntatori (superiori)

I Puntatori
Un puntatore è una variabile che contiene l'indirizzo di una cella di memoria dove, di solito, è registrata un'altra variabile. I puntatori non sono tutti uguali, ma possono essere di tipi diversi (il tipo deve corrispondere al tipo della variabile puntata), un puntatore di tipo intero serve allora solo per puntare a variabili di tipo intero. I puntatori possono essere di diversi tipi, il tipo corrisponde al tipo di variabile puntata. Per dichiarare un puntatore si scrive iltipodelpuntatore *nomedellavariabile per dichiarare un puntatore di tipo int allora si scrive
p e' il nome del nostro puntatore di tipo intero, il comando poteva essere scritto anche come int* p che anche se di solito non utilizzato evidenzia che p e' una variabile di tipo int* cioe' un puntatore a intero. Per ottenere l'indirizzo di una variabile si usa l'operatore & prima del nome della variabile di cui si vuole ottenere l'indirizzo, allora
carica in p l'indirizzo della variabile a
Il programma stamperà:
1001
7
1004
int *p; float z=10; p=&z;
Questo è un esempio di scrittura sbagliata perché z è di tipo float mentre il puntatore è di tipo intero ma il puntatore può puntare solo variabili del suo tipo.
Quando si usano operazioni aritmetiche (hanno senso solo le operazioni di sottrazione e addizione) fra puntatori e costanti si deve fare attenzione perché i calcoli sono diversi da quelli svolti con le normali variabili. Se scrivo p=p+1; questo comando non aggiunge una unità a p bensì internamente fa questo calcolo p=p+1*sizeof(tipodatopuntatore) dove sizeof(tipodatopuntatore) corrisponde alla dimensione in byte del tipo di dato del puntatore, ad esempio se il puntatore è intero allora sizeof(int) vale 4, se p vale 1000 dopo il comando p=p+1; assume il valore 1004, se avessi scritto p=p+5; e p valeva 1000 avrei ottenuto dopo l'esecuzione del comando 1020, funziona in questo modo perché se si crea una variabile intera nella memoria ram questa occupa 4 byte (4 celle consecutive) e se questa variabile si trova partendo dalla cella di indirizzo 1000 (occupa quindi la 1000, 1001, 1002, 1003) una ipotetica variabile intera successiva si troverebbe a partire dalla cella 1004 (occupando le celle 1004, 1005, 1006, 1007), p=p+1; deve intendersi come incrementa la variabile p del numero di celle che servono per memorizzare un intero, p=p+2; deve intendersi come incrementa la variabile p del numero di celle che servono per memorizzare 2 interi e cosi via. Visto che normalmente sono questi gli spostamenti che vogliamo fare (a multipli della dimensione del dato puntato), l'aritmetica sui puntatori rispetta questo funzionamento.
L'uso degli operatori di confronto < <= > >= applicati a 2 puntatori pa e pb che puntino alla variabile a e alla variabile b non ha significato, perché quando viene eseguito il programma le posizioni dove vengono caricate le due variabili a e b sono arbitrarie (scelte dal sistema operativo) e quindi può capitare che per uno stesso programma nella prima esecuzione l'indirizzo di a sia 234 e quello di b 765 e nell'esecuzione successiva l'indirizzo di a sia 500 e quello di b 344, e allora domandarsi se pa<pb dà un risultato che può cambiare ad ogni esecuzione del programma.
Esistono anche puntatori generici (tipo void), a cui può essere associato l'indirizzo di una variabile qualsiasi, ma poi per usare tramite il puntatore il valore contenuto nella cella puntata bisogna fare una operazione di cast
* Quando si scrive int *p; int * rappresenta il tipo del dato (un puntatore a intero)
* Quando scrivo z=a*5; il * e' l'operatore di moltiplicazione (pensando che a e b siano due variabili intere)
* Quando scrivo c= *p se p e' un puntatore *p rappresenta il valore della cella puntata e il simbolo * viene detto operatore di derefenziamento
Quindi attenzione lo stesso simbolo * ha significati diversi in base all'operazione e alle variabili coinvolte
E' possibile dichiarare anche puntatori a puntatori ad esempio
Quando un puntatore non punta a nulla (non ho ancora assegnato l'indirizzo una variabile) il suo valore viene posto a NULL, quindi se p==NULL vuol dire che il puntatore non punta a niente e non ha senso cercare il valore puntato tramite il comando *P
La funzione malloc serve per creare delle strutture dati durante l'esecuzione del programma, ad esempio se voglio caricare una immagine di 120 bytes nella memoria RAM posso scrivere questo codice
malloc alloca una struttura di 120 byte e ne restituisce un indirizzo , su questo indirizzo (tipo void) faccio il cast per dire che deve intendersi come l'indirizzo di tipo int, che viene salvato in p
La funzione sizeof restituisce invece la dimensione in byte del tipo passato oppure della variabile passata quindi c= sizeof(int) carica 4 nella variabile c, posso anche scrivere c=sizeof(a) che carica in c la dimensione della variabile a in byte.
Esiste una forte analogia fra puntatori e vettori ( e più in generale con gli array) Il nome di un vettore è un puntatore statico che contiene l’indirizzo dell’inizio del vettore. Statico significa che l'indirizzo associato al nome del vettore non si può modificare nel corso del programma, a differenza di un normale puntatore a cui invece possiamo modificare (se serve) il valore dell'indirizzo associato.
Vett=1006
È sbagliato scrivere: vett=&a oppure vett=1234 perché vett è un puntatore costante. Per sottolineare l'analogia fra puntatori e vettori si ricorda che e' possibile usare la notazione dei vettori sui puntatori e viceversa
State attenti poi che *(p+1) e' diverso come significato da *p+1 , nel primo caso prendo l'indirizzo di p lo incremento (ipotizzo p un puntatore a intero) di 4 e accedo al valore puntato, mentre nel secondo caso accedo al valore puntato da p e gli aggiungo 1.