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.