Hay algunas piezas en esto que permiten que todas estas combinaciones de operadores funcionen de la misma manera.
La razón fundamental por la que todos estos trabajos es que una función (como foo
) es implícitamente convertible en un puntero a la función. Por eso void (*p1_foo)() = foo;
funciona: foo
se convierte implícitamente en un puntero a sí mismo y se le asigna ese puntero p1_foo
.
El unario &
, cuando se aplica a una función, produce un puntero a la función, al igual que produce la dirección de un objeto cuando se aplica a un objeto. Para punteros a funciones ordinarias, siempre es redundante debido a la conversión implícita de función a puntero de función. En cualquier caso, por eso void (*p3_foo)() = &foo;
funciona.
El unario *
, cuando se aplica a un puntero de función, produce la función apuntada a, al igual que produce el objeto señalado cuando se aplica a un puntero ordinario a un objeto.
Estas reglas se pueden combinar. Considere su penúltimo ejemplo **foo
:
- Primero,
foo
se convierte implícitamente en un puntero a sí mismo y el primero *
se aplica a ese puntero de función, produciendo la función foo
nuevamente.
- Luego, el resultado nuevamente se convierte implícitamente en un puntero a sí mismo y
*
se aplica el segundo , obteniendo nuevamente la función foo
.
- Luego se convierte implícitamente en un puntero de función nuevamente y se asigna a la variable.
Puede agregar tantos *
s como desee, el resultado es siempre el mismo. *
Cuantos más , mejor.
También podemos considerar su quinto ejemplo &*foo
:
- Primero,
foo
se convierte implícitamente en un puntero a sí mismo; *
se aplica el unario , cediendo foo
nuevamente.
- Luego,
&
se aplica a foo
, produciendo un puntero a foo
, que se asigna a la variable.
Sin &
embargo, solo se puede aplicar a una función, no a una función que se ha convertido en un puntero de función (a menos, por supuesto, que el puntero de función sea una variable, en cuyo caso el resultado es un puntero a puntero) a una función; por ejemplo, podría agregar a su lista void (**pp_foo)() = &p7_foo;
).
Por eso &&foo
no funciona: &foo
no es una función; Es un puntero de función que es un valor r. Sin embargo, &*&*&*&*&*&*foo
funcionaría, como lo haría &******&foo
, porque en ambas expresiones &
siempre se aplica a una función y no a un puntero de función de valor.
Tenga en cuenta también que no necesita usar el unario *
para hacer la llamada a través del puntero de función; ambos (*p1_foo)();
y (p1_foo)();
tienen el mismo resultado, nuevamente debido a la conversión de puntero de función a función.
&foo
toma la dirección defoo
, lo que da como resultado un puntero de función que apuntafoo
, como cabría esperar.&
operadores para objetos: dadoint p;
,&p
produce un punterop
y es una expresión de valor; El&
operador requiere una expresión lvalue.*
, menos alegre .&*
cancelar entre sí (6.5.3.2):"The unary & operator yields the address of its operand."
/ - /"If the operand is the result of a unary * operator, neither that operator nor the & operator is evaluated and the result is as if both were omitted, except that the constraints on the operators still apply and the result is not an lvalue."
.Creo que también es útil recordar que C es solo una abstracción para la máquina subyacente y este es uno de los lugares donde se filtra esa abstracción.
Desde la perspectiva de la computadora, una función es solo una dirección de memoria que, si se ejecuta, realiza otras instrucciones. Entonces, una función en C se modela como una dirección, lo que probablemente lleva al diseño de que una función es "la misma" que la dirección a la que apunta.
fuente
&
y*
son operaciones idempotentes en un símbolo declarado como una función en C que significafunc == *func == &func == *&func
y por lo tanto*func == **func
Significa que el tipo
int ()
es el mismo que el deint (*)()
un parámetro de función y un func definido se puede pasar como*func
,func
o&func
.(&func)()
es el mismo quefunc()
. Enlace Godbolt.Una función es realmente una dirección, por lo tanto,
*
y&
no tienen ningún significado, y en lugar de producir un error, que escoge el compilador interpretarlo como la dirección de func.&
en un símbolo declarado como un puntero de función sin embargo tomará la dirección del puntero (porque ahora tiene un propósito aparte), mientras quefuncp
y*funcp
será idénticofuente