*
* Veniamo ora all'utilizzo dei puntatori.
* Premetto che i puntatori non sono presenti nel Java, quindi non troverete
* molti esempi... e se usate questo tutorial solo come preparazione al Java,
* vi consiglio di saltare l'argomento.
* Un puntatore e' una variabile contenente un indirizzo di memoria.
* Questo indirizzo puo' essere l'indirizzo dove si trova un'altra variabile,
* un'array, una funzione o qualsiasi altra cosa.
* Se una variabile contiene l'indirizzo di un'altra variabile, si dice che
* la prima punta alla seconda.
* Occorre a questo punto chiarire cosa e' un indirizzo di memoria.
* La memoria si misura in byte, per cui un Kb sono 1024 byte e 1MB sono
* un milione di byte circa. Un Kb sono 1024 byte perche' si basa sulle
* potenze del 2 (2,4,8,16,32,64,128....): questi numeri si trovano
* continuamente, infatti ogni bit in piu' o in meno che si ha a disposizione
* nella notazione binaria (0 o 1 solamente) raddoppia le possibilita'
* numeriche componibili.
* Un byte a sua volta e' composto da 8 bit, per cui 2 byte sono 16 bit e
* 4 byte sono 32bit.
* La memoria e' organizzata come una serie consecutiva di byte, come fosse
* un unico grande array monodimensionale di tipo char.
* In realta' tra processori diversi e modi diversi di operare di questi ci
* possono essere delle variazioni, come la segmentazione in blocchi di 64k,
* ma la regola della fila di byte consecutivi e' universale.
* Quando si usa il sistema operativo (DOS/WIN o altri) non ci accorgiamo di
* questo, semplicemente possiamo sapere quanta memoria e' libera, e cio'
* viene espresso in byte.
* Avendo stabilito che la memoria e' una serie di byte consecutivi, come
* si fa a distinguere un byte da un'altro?
* Abbiamo fatto il paragone con un array monodimensionale: se ricordate
* per accedere ai singoli byte si deve specificare un indice, ossia il
* numero del byte, considerando il primo come lo 0, il secondo come l'1
* e cosi' via.
* Ebbene, la memoria e' organizzata in questo modo: ogni byte ha un
* indirizzo, come se la memoria fosse una strada con le case tutte uguali
* e solo da un lato, ognuna delle quali e' un byte: il numero civico di
* quelle case e' il loro indirizzo.
* Per esempio, l'indirizzo 0 indica il primo byte della memoria, mentre
* l'indirizzo 100 indica il 101esimo byte di memoria.
* Quindi, l'indirizzo 100 dista dal 115 ben 16 byte!
* E' ovvio che in sistemi con molta memoria, questa puo' non essere un
* unico blocco da 0 ad x, ma ci possono essere vari blocchi di 1MB o piu'
* non consecutivi.
* Abbiamo detto che i puntatori "puntano" ad un indirizzo.
* Quando compiliamo un programma in C, anche se non ce ne accorgiamo, non
* facciamo che far scrivere in memoria le istruzioni assembly relative
* alle funzioni che chiamiamo, assieme a blocchi di dati, ossia le
* variabili e gli array. Ogni funzione e ogni variabile o array sara'
* quindi posizionata ad un certo indirizzo preciso in memoria, che noi
* non conosciamo, perche' tutto avviene automaticamente.
* Mettiamo che la variabile pippo, di tipo intero, venga posizionata
* all'indirizzo 1000: in questo caso quando scriviamo o leggiamo pippo,
* fisicamente si legge e si scrive alla locazione 1000.
* Quindi un puntatore che "punta" a pippo, conterra' l'indirizzo 1000,
* ossia l'indirizzo di pippo.
* In altri termini contiene la sua posizione in memoria.
* Occorre specificare un'altra cosa: non tutti i tipi di variabile
* occupano lo stesso spazio in memoria.
* Per esempio, le variabili char occupano 1 solo byte, mentre gli int ne
* occupano 2, e i float ben 4.
* Questo significa che se abbiamo un array composto da 4 elementi di tipo
* char, l'array in totale sara' lungo 4 byte.
* Se abbiamo un array composto da 4 elementi di tipo int, occupera' ben
* 8 byte, ossia 2*4.
* Se infine l'array fosse composto da 4 elementi di tipo float, la
* lunghezza complessiva sarebbe di 4*4=16 byte.
* Supponendo che quest'ultimo array di tipo float si trovi a partire
* dall'indirizzo 1000, il secondo elemento inizia all'indirizzo 1004, il
* terzo all'indirizzo 1008, il quarto a 1012. L'array quindi va da 1000
* a 1016.
* Quando operiamo con un array, pero', basta mettere il numero di elemento
* come indice, e il C a seconda del tipo di dato salta 1, 2 o 4 byte per
* individuare l'elemento successivo.
* Quando si opera con i puntatori occorre sapere queste cose, perche'
* al momento della dichiarazione di un puntatore si deve stabilire a che
* tipo di dato puntera', per cui quando lo incrementeremo saltera'
* 1,2 o 4 byte a seconda del tipo a cui punta. Ad esempio:
*
* int *conta1;
* char *conta2;
*
* *conta1++ * punta al prossimo int (incrementa di 2 bytes)
* *conta2++ * punta al prossimo char (incrementa di 1 byte)
*
* Inoltre, quando si fanno degli assegnamenti con un puntatore, saranno
* trasferiti 1,2, 4 o piu' bytes dall'indirizzo a cui punta, a seconda
* del tipo a cui punta. Se il puntatore e' stato definito come puntatore
* di tipo char, e si copia in una variabile int, sara' trasferito solo
* 1 byte (non 2) nella variabile int, dall'indirizzo a cui puntava il
* puntatore. Se puntava un dato di tipo float avrebbe copiato troppo, 4
* bytes. Questi errori portano a malfunzionamenti seri dei programmi e
* di tutto il sistema operativo con pericoli di inchiodamento.
* Veniamo alla sintassi di dichiarazione di un puntatore:
*
* tipo *nomepunt;
*
* Ad esempio:
*
* int *puntapippo;
*
* La dichiarazione e' simile a quella delle variabili, ma il nome deve
* essere preceduto da un asterisco.
* Ricordiamo che il tipo indica il tipo di dato a cui puo' puntare.
* Come usiamo un puntatore?
* Esistono 2 operatori puntatore, che devono precedere le variabili e i
* puntatori in questo modo:
*
* &pippo; * *pippo
*
* Vediamoli in dettaglio:
*
* & Indica l'INDIRIZZO (la posizione) in memoria della variabile
*
* * Fa accedere al CONTENUTO della variabile puntata.
* Da non confondere con la moltiplicazione.
*
* Quindi, una volta definito un puntatore, se vogliamo immetterci
* l'indirizzo di una variabile, dobbiamo usare &:
*
* int *puntapippo; * Definisco un puntatore di interi
* int pippo; * Definisco una variabile intera
*
* puntapippo = &pippo; * Immetto nel puntatore puntapippo
* * l'indirizzo di pippo, ossia lo faccio
* * puntare a pippo.
*
* Allo stesso modo, ora che puntapippo punta a pippo, posso accedere
* indirettamente a pippo tramite *puntapippo:
*
* *puntapippo = 100; * Scrivo 100 nella variabile pippo tramite
* * il puntatore puntapippo.
*
* printf("Pippo vale %d ", pippo); * Pippo vale 100! *
*
* Vediamo funzionare questo programmino di esempio.
*/
#include <stdio.h> /* Includiamo la libreria standard */
/* Funzione principale e inizio del programma */
int main(void) /* Funzione principale, eseguita per prima */
{ /* Inizio della funzione main() */
int *puntapippo; /* Definisco un puntatore di interi */
int pippo; /* Definisco una variabile intera */
pippo = 50; /* Assegno a pippo il valore 50 */
puntapippo = &pippo; /* Immetto nel puntatore puntapippo
* l'indirizzo di pippo, ossia lo faccio
* puntare a pippo. */
/* Allo stesso modo, ora che puntapippo punta a pippo, posso accedere
* indirettamente a pippo tramite *puntapippo: */
*puntapippo = 100; /* Scrivo 100 nella variabile pippo tramite
* il puntatore puntapippo. */
printf("Pippo vale %d ", pippo); /* Pippo vale 100! */
return(0); /* la funzione main restituisce uno 0 intero */
} /* Fine della funzione main() */
/* Una nota: avete presente la sintassi dello scanf, in cui la variabile
* deve essere preceduta da &?
* Ebbene, la funzione scanf vuole in entrata l'indirizzo della variabile
* dove salvare il dato introdotto da tastiera.
* Come vedete i misteri si stanno chiarendo tutti.
*/
