Solo gli operatori *
, []
e ()
hanno un significato nelle dichiarazioni (C ++ aggiunge &
, ma non entreremo in questo qui).
Nella dichiarazione
int *p;
il int
-ness di p
è specificato dallo specificatore del tipo int
, mentre il puntatore-ness di p
è specificato dal dichiaratore *p
.
Il tipo di p
è "puntatore a int
"; questo tipo è completamente specificato dalla combinazione dello specificatore di tipi int
più il dichiaratore *p
.
In una dichiarazione, il dichiaratore introduce il nome della cosa dichiarata ( p
) insieme a informazioni di tipo aggiuntive non specificate dallo specificatore di tipo ("puntatore a"):
T v; // v is a single object of type T, for any type T
T *p; // p is a pointer to T, for any type T
T a[N]; // a is an N-element array of T, for any type T
T f(); // f is a function returning T, for any type T
Questo è importante - il puntatore, l'array-ness e la funzionalità sono specificati come parte del dichiaratore, non lo specificatore di tipo 1 . Se scrivi
int* a, b, c;
sarà analizzato come
int (*a), b, c;
quindi solo a
sarà dichiarato come puntatore a int
; b
e c
sono dichiarati come% normaleint
s.
Gli operatori *
, []
e ()
possono essere combinati per creare tipi arbitrariamente complessi:
T *a[N]; // a is an N-element array of pointers to T
T (*a)[N]; // a is a pointer to an N-element array of T
T *(*f[N])(); // f is an N-element array of pointers to functions
// returning pointer to T
T *(*(*(*f)[N])())[M] // f is a pointer to an N-element array of pointers
// to functions returning pointers to M-element
// arrays of pointers to T
Tieni presente che *
, []
e ()
rispettano le stesse regole di precedenza nelle dichiarazioni che fanno nelle espressioni. *a[N]
è analizzato come *(a[N])
in entrambe le dichiarazioni e le espressioni.
La cosa veramente importante da comprendere in questo è che la forma di una dichiarazione corrisponde alla forma dell'espressione nel codice. Tornando al nostro esempio originale, abbiamo un puntatore a un intero chiamato p
. Se vogliamo recuperare il valore intero, usiamo l'operatore *
per dereferenziare p
, in questo modo:
x = *p;
Il tipo di espressione *p
è int
, che segue dalla dichiarazione
int *p;
Allo stesso modo, se abbiamo un array di puntatori a double
e vogliamo recuperare un valore specifico, indichiamo nell'array e dereferenziamo il risultato:
y = *ap[i];
Ancora, il tipo di espressione *ap[i]
è double
, che segue dalla dichiarazione
double *ap[N];
Quindi, perché ++
non ha un ruolo in una dichiarazione come *
, []
o ()
? O qualsiasi altro operatore come +
o ->
o &&
?
Bene, in pratica, perché la definizione della lingua lo dice. Mette da parte solo *
, []
e ()
per giocare qualsiasi ruolo in una dichiarazione, dal momento che devi essere in grado di specificare il puntatore, l'array e i tipi di funzione. Non esiste un tipo "increment-this" separato, quindi non è necessario che ++
faccia parte di una dichiarazione. Non esiste un tipo "bitwise-this", quindi non c'è bisogno di unary &
, |
, ^
o ~
per far parte di una dichiarazione. Per i tipi che utilizzano l'operatore di selezione membri .
, utilizziamo i tag struct
e union
nella dichiarazione. Per i tipi che utilizzano l'operatore ->
, utilizziamo i tag struct
e union
in combinazione con l'operatore *
nel dichiaratore.
- Naturalmente, puoi creare nomi typedef per i tipi di puntatore, array e funzione, come
typedef int *iptr;
iptr a,b,c; // all three of a, b, and c are pointers to int
ma ancora, è il dichiaratore *iptr
che specifica il puntatore del nome typedef.