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 p
está especificado por el especificador de tipo int
, mientras que el puntero-dad de p
es especificado por el declarador *p
.
El tipo de p
es "puntero a int
"; Este tipo está completamente especificado por la combinación del especificador de tipo int
má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 a
se declarará como un puntero a int
; b
y c
se declaran como regulares int
s.
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 double
y 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 struct
y union
en la declaración. Para los tipos que usan el ->
operador, usamos las etiquetas struct
y union
junto 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