En C, *
se llama operador de indirección o operador de desreferencia. Entiendo cómo funciona cuando se usa en una declaración. Tiene sentido escribir *p
o * p
, considerando que es un operador unario.
Sin embargo, a veces en una declaración, *
se utiliza a.
void move(int *units) { ... }
o
int *p = &x;
Me parece extraño que aquí se use un operador. No puedes hacer cosas como
void move(int units++) { ... }
donde ++
también es un operador unario. ¿Es este *
también el operador de indirección, o *
tiene un significado diferente, donde dice algo sobre el tipo de puntero? (Si este es el caso, estoy considerando usarlo, por ejemplo, int* units
en declaraciones y, de lo int x = *p;
contrario, por claridad)
En esta respuesta se dice que
El estándar C solo define dos significados para el operador *:
- operador de indirección
- operador de multiplicación
También he visto personas que afirman que en *
realidad es parte del tipo. Esto me confunde
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/8911253Respuestas:
Las piezas más pequeñas de la lengua C son fichas de léxico , tales como palabras clave (por ejemplo
int
,if
,break
), los identificadores (por ejemplomove
,units
) y otros.*
es un elemento léxico llamado puntuador (como lo son, por ejemplo{}()+
). Un signo de puntuación puede tener diferentes significados según cómo se use:En el capítulo 6.5 estándar de C11 sobre expresiones,
*
se define como operador unario (sección 6.5.3.2, para indirección) y como operador multiplicativo (sección 6.5.5), exactamente como lo ha descrito. Pero*
solo se interpreta como operador de acuerdo con las reglas gramaticales que hacen expresiones válidas.Pero también hay un capítulo 6.2 sobre conceptos, que explica que los punteros a un tipo son tipos derivados. La sección 6.7.6.1 sobre los declaradores de puntero es más precisa:
De hecho, esto forma
*
parte de un tipo derivado (*
significa que hay "puntero", pero siempre necesita otro tipo para decir a qué tipo de cosa señala algo).Observación: No hay contradicción en esto. Hace un uso tan diferenciado del elemento del lenguaje léxico todos los días cuando habla inglés: "un identificador " designa algo y " manipular " significa hacer algo, dos usos gramaticales completamente diferentes del mismo elemento léxico.
fuente
En C, la indirección en una declaración se lee mejor como parte de la variable que como parte del tipo. Si tengo:
esta declaración podría interpretarse como lectura: declare una variable, llamada
a
, que cuando se desreferenciada es de tipoint
.Esto es importante cuando se declaran múltiples variables a la vez:
Ambas declaraciones son equivalentes, y en ambos
a
yb
no son los mismos tipos -a
es un puntero a un entero yb
es un int. Pero la declaración (2) parece que el "tipo de puntero" es parte del tipo cuando en realidad es la declaración de*a
.fuente
Para agregar a las otras respuestas correctas:
Es solo una expresión de declaración que refleja posibles expresiones de uso. ¡IIRC, creo que Kernigan o Ritchie lo llamaron un experimento! Aquí hay un texto de apoyo marginal:
(Mi énfasis)
http://codinghighway.com/2013/12/29/the-absolute-definitive-guide-to-decipher-c-declarations/
Entonces, sí, tiene razón, estos son de hecho los mismos operadores aplicados a las expresiones de declaración, aunque como ha observado, solo algunos de los operadores tienen sentido (por ejemplo, ++ no).
Tal estrella es de hecho parte del tipo de la variable individual que se declara; sin embargo, como han señalado otros carteles, no (más precisamente, no puede) transferirse a otras variables que se declaran al mismo tiempo (es decir, en la misma declaración de declaración): cada variable comienza de nuevo con el mismo tipo base que el objetivo (eventual), y luego obtiene sus propios operadores de puntero, matriz y función (oportunidad de aplicar o no) para completar su tipo.
Esta es la razón por la cual los programadores experimentados tienden a preferir (1)
int *p;
vs.int* p;
y (2) declarar solo una variable por declaración, aunque el lenguaje permita más.Para agregar a eso
int (*p);
es una declaración legal, estos padres simplemente se agrupan y en este caso simple, y no hacen nada diferenteint *p
. Esto es lo mismo que decir que una expresión (no declarativa)(i) >= (0)
es la mismai >= 0
, ya que siempre se pueden agregar legalmente()
.He mencionado que como otro ejemplo, sin embargo, de por qué los programadores prefieren una forma sobre la otra, por lo que considerar
int (*p);
vsint(* p);
. Quizás pueda ver cómo el paréntesis, la estrella, etc. se aplican a la variable, no al tipo base (es decir, agregando todos los paréntesis permitidos).fuente
En esta declaración de función:
el
*
es parte de tipo, es decirint*
. No es un operador. Es por eso que mucha gente lo escribiría como:fuente
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
la
int
-ness dep
está especificado por el especificador de tipoint
, mientras que el puntero-dad dep
es especificado por el declarador*p
.El tipo de
p
es "puntero aint
"; Este tipo está completamente especificado por la combinación del especificador de tipoint
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"):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
se analizará como
así que solo
a
se declarará como un puntero aint
;b
yc
se declaran como regularesint
s.El
*
,[]
y()
los operadores se pueden combinar para crear tipos arbitrariamente complejas: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 desreferenciarp
, así:El tipo de expresión
*p
esint
, que se deduce de la declaraciónDel mismo modo, si tenemos una matriz de punteros
double
y queremos recuperar un valor específico, indexamos en la matriz y desreferenciamos el resultado:Nuevamente, el tipo de expresión
*ap[i]
esdouble
, que se deduce de la declaraciónEntonces ¿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 etiquetasstruct
yunion
en la declaración. Para los tipos que usan el->
operador, usamos las etiquetasstruct
yunion
junto con el*
operador en el declarador.*iptr
que especifica el puntero del nombre typedef.fuente
Es cierto que hay dos significados para el
*
operador . No es cierto que esos sean los únicos significados de la*
ficha .En una declaración como
el
*
no es un operador; Es parte de la sintaxis de una declaración.La sintaxis de la declaración de C está pensada para reflejar su sintaxis de expresión, por lo que la declaración anterior se puede leer como "
*p
es de tipoint
", de lo que podemos inferir quep
es de tipoint*
. Sin embargo, esta no es una regla firme, y no se menciona en ninguna parte del estándar. En cambio, la sintaxis de las declaraciones se define por sí sola, por separado de la sintaxis de las expresiones. Es por eso que, por ejemplo, la mayoría de los operadores que pueden aparecer en expresiones no pueden aparecer con los mismos significados en las declaraciones (a menos que sean parte de una expresión que es parte de la declaración, como el+
operador enint array[2+2];
).Un caso en el que el principio del "uso de los espejos de declaración" no funciona del todo es:
donde
arr[10]
sería de tipoint
si existiera .Y, de hecho, hay otro uso del
*
token. Se puede declarar un parámetro de matriz[*]
para indicar una matriz de longitud variable. (Esto se agregó en C99.) No*
es una multiplicación ni una desreferencia. Sospecho que se inspiró en la sintaxis comodín de shell.fuente
int *p = 0;
es similar al uso * p = 0; `pero los significados son muy diferentes.int*
).