Puntatori

  1. gli indirizzi non sono esattamente dei numeri
  2. per esempio, int p, a; e poi p=&a; genera un errore (warning)
  3. il tipo di un indirizzo non è int
  4. l'indirizzo di una variabile int è di tipo int *
  5. quindi, int *p; int a; e poi p=&a; non genera errori

Nella pagine precedente si è detto che l'indirizzo, cosí come lo spazio occupato da una variabile, sono dei semplici numeri. Questo è in realtà vero solo per il numero di byte occupati, che è realmente un numero intero.

Per quello che riguarda l'indirizzo, non è del tutto esatto che si tratta di un numero. Per capire meglio questo fatto, ricordiamo che una variabile, oltre ad avere una zona di memoria associata, ha anche un tipo. Per esempio, due variabili a e b possono entrambe occupare quattro byte in memoria, ma essere una di tipo int e una di tipo float. Quello che cambia non è la occupazione di memoria, ma il modo in cui questi quattro byte vengono interpretati.

Questa differenza si riflette anche sui tipi degli indirizzi. Per esempio l'indirizzo di una variabile di tipo int non è un intero. È invece una variabile di un nuovo tipo, il puntatore a intero. In pratica, questo si può vedere immediatamente se si cerca di compilare il seguente programma nocast.c.

/*
  Dimostrazione che l'indirizzo di una variabile
non e' un numero intero: compilando questo
programma si genera un errore (warning) di
incompatibilita' fra tipi.
*/

int main() {
  int a;
  int b;

  b=&a;

  return 0;
}

Compilando, si ottiene un messaggio di questo genere:

nocast.c: In function `main':
nocast.c:12: warning: assignment makes integer from pointer without a cast

Questo è il tipico errore che si ottiene quando si cerca di assegnare a una variabile un valore di un altro tipo (per esempio, assegnando a una variabile intera un valore reale). È quindi chiaro che la istruzione b=&a contiene un tentativo di assegnare a una variabile intera un valore di un tipo non compatibile. Quindi, il tipo della espressione &a non è compatibile con gli interi.

La regola generale sui puntatori è la seguente:

l'indirizzo di una variabile di un tipo T è di tipo T *, che viene detto tipo ``puntatore a T''.

Dato che a è un intero, il suo indirizzo è di tipo int *, ossia di tipo ``puntatore a int''. A prima vista, questa differenza di tipi può sembrare una particolarità inutile del C. Il motivo per cui è invece necessaria risulterà chiara nel seguito.

Per ogni tipo che è possibile definire in C, ad esso viene associato il corrispondente tipo puntatore, che è anche esso un tipo. Per esempio, dato che in C esiste il tipo int, esiste automaticamente anche il tipo puntatore ad int, che si denota con int *. È importante notare che questo è un tipo come tutti gli altri. È quindi possibile definire delle variabili di tipo puntatore a intero, nel seguente modo:

  int *p;

Dal momento che gli indirizzi di variabili sono di tipo puntatore (al tipo della variabile), nella variabile p si può mettere l'indirizzo di una variabile di tipo int. Questa operazione assegna a una variabile di tipo puntatore a intero p il valore dell'indirizzo di una variabile intera, che è anche esso un puntatore a intero. Quindi, la istruzione p=&a non genera nessun errore di incompatibilitè fra tipi.

La compilazione del programma nocastpunt.c, programma del resto inutile, non genera nessun errore. Si riporta qui sotto il testo del programma.

/*
  Una variabile definita di tipo int * e' un puntatore a
intero. Anche l'indirizzo di una variabile intera e' un
puntatore a intero. Quindi, p=&a non genera nessun errore,
perche' p e &a sono dello stesso identico tipo.
*/

int main() {
  int a;
  int *p;

  p=&a;

  return 0;
}

Se vogliamo memorizzare l'indirizzo di una variabile di tipo int in un'altra variabile p, quest'ultima deve essere dichiarata non come int (anche se gli indirizzi sembrano essere interi), ma come puntatore a intero int *. Lo stesso vale per tutti gli altri tipi.

Questo discorso vale anche per gli altri tipi definiti in C. Per esempio, l'indirizzo di una variabile float va messo in una variabile di tipo float *, mentre l'indirizzo di una variabile char va messo in una variabile char *. Si noti che, in ogni caso, l'indirizzo di una variabile è comunque un numero intero (anche se la variabile contiene un valore reale!) Il programma seguente altri.c contiene delle variabili puntatore a tipi diversi da int

/*
  Le variabili float* contengono indirizzi di variabili
float. Lo stesso per char*
*/

int main() {
  float f;
  float *p;
  char c;
  char *t;

  p=&f;
  c=&t;

  return 0;
}