Sólo el *, []y ()los operadores tienen ningún significado en las declaraciones (C ++ añade &, pero no vamos a entrar en eso aquí).
En la declaración
int *p;
la int-ness de pestá especificado por el especificador de tipo int, mientras que el puntero-dad de pes especificado por el declarador *p .
El tipo de pes "puntero a int"; Este tipo está completamente especificado por la combinación del especificador de tipo intmás el declarador *p.
En una declaración, el declarador introduce el nombre de la cosa que se declara ( p) junto con información de tipo adicional no dada por el especificador de tipo ("puntero 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
Esto es importante: puntero-ness, array-ness y function-ness se especifican como parte del declarador, no como el especificador de tipo 1 . Si tú escribes
int* a, b, c;
se analizará como
int (*a), b, c;
así que solo ase declarará como un puntero a int; by cse declaran como regulares ints.
El *, []y ()los operadores se pueden combinar para crear tipos arbitrariamente complejas:
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
Observe que *, []y ()obedezca las mismas reglas de precedencia en las declaraciones que hacen en las expresiones. *a[N]se analiza como *(a[N])en las declaraciones y expresiones.
Lo realmente importante a tener en cuenta en esto es que la forma de una declaración coincide con la forma de la expresión en el código. Volviendo a nuestro ejemplo original, tenemos un puntero a un entero llamado p. Si queremos recuperar ese valor entero, usamos el *operador para desreferenciar p, así:
x = *p;
El tipo de expresión *p es int, que se deduce de la declaración
int *p;
Del mismo modo, si tenemos una matriz de punteros doubley queremos recuperar un valor específico, indexamos en la matriz y desreferenciamos el resultado:
y = *ap[i];
Nuevamente, el tipo de expresión *ap[i] es double, que se deduce de la declaración
double *ap[N];
Entonces ¿por qué no ++jugar un papel en una declaración como *, []o ()? ¿O cualquier otro operador como +o ->o &&?
Bueno, básicamente, porque la definición del lenguaje lo dice. Solo se reserva *, []y ()para desempeñar cualquier papel en una declaración, ya que debe poder especificar punteros, matrices y tipos de funciones. No hay un tipo "increment-this" separado, por lo que no es necesario ++ser parte de una declaración. No hay "-este bit a bit" tipo, así que no hay necesidad de unario &, |, ^, o ~ser parte de una declaración tampoco. Para los tipos que usan el .operador de selección de miembros, usamos las etiquetas structy unionen la declaración. Para los tipos que usan el ->operador, usamos las etiquetas structy unionjunto con el *operador en el declarador.
- Por supuesto, puede crear nombres typedef para punteros, matrices y tipos de funciones, como
typedef int *iptr;
iptr a,b,c; // all three of a, b, and c are pointers to int
pero de nuevo, es el declarador el *iptr que especifica el puntero del nombre typedef.
void move(int *units) { ... }, es un operador de indirección. Considerado parte del tipo, también se puede escribirvoid move(int* units) { ... }, aunque prefiero el estilo anterior. Lees ambos como "puntero int". Ver también stackoverflow.com/a/8911253