¿Cómo funcionan estas dos variaciones de syntax de llamada de puntero de función?

Soy relativamente nuevo en C, y me pareció intrigante que las siguientes llamadas a la función de puntero compilen y funcionen bien. Uno con y otro sin quitar la referencia al puntero de función antes de llamarla.

#include  #include  void func() { puts("I'm a func"); } int main(void) { void (*f)() = func; f(); (*f)(); return EXIT_SUCCESS; } 

Creo que entiendo que (*f)() es la forma “oficial” de llamar a un puntero de función, pero ¿por qué simplemente llamar a f() funciona? ¿Es ese azúcar sintáctico de las últimas versiones de C?

Este es un pedazo de azúcar sintáctico / semántico que, AFAIK, ha funcionado desde las primeras versiones de C. Tiene sentido si piensas que las funciones son punteros al código.

La única regla especial necesaria para hacer que los punteros de función funcionen de esta manera es que al dirigir un puntero de función se devuelve el mismo puntero (porque de todos modos no se puede manipular el código en el estándar C): cuando f es un puntero de función, entonces f == (*f) == (**f) , etc.

(Aparte: tenga cuidado con una statement como void (*f)() . Una lista de argumentos vacía denota una statement de función anterior a C89 de estilo antiguo que coincide solo con el tipo de devolución. Prefiere void (*f)(void) para tipo de seguridad.)

Una expresión de llamada de función siempre tiene la forma “puntero de función”, “paréntesis de ronda”, “argumentos”, “paréntesis de ronda”. Para no tener que deletrear (&printf)("Hello World\n") cada vez que hay 1 , hay una regla separada por la cual una expresión que denota una función decae con el puntero de función correspondiente.

Como el puntero de una función se puede anular para dar una expresión que denota una función otra vez, esto volverá a decaer, por lo que puede mantener la desreferenciación y habrá una gran cantidad de decaimiento:

 (&f)(); // no decay (decay does not happen when the expression // is the operand of &) f(); // one decay (*f)(); // two decays (**f)(); // three decays 

1) Early Perl tiene una syntax funcional como esa.

La norma C 2011 dice en la cláusula 6.3.2.1, párrafo 4:

Excepto cuando es el operando del operador sizeof , el operador _Alignof , o unario & operator, un designador de función con el tipo ” función que devuelve tipo ” se convierte en una expresión que tiene el tipo ” puntero a función que devuelve el tipo ” .

Esto significa que, si f designa una función, entonces, en una llamada como f() , f se convierte automáticamente a &f , dando como resultado (&f)() . Esta es en realidad la forma “adecuada” de llamar a una función, porque la expresión de llamada de función requiere un puntero a una función.

Ahora considera lo que sucede en *f . La f se convierte automáticamente a &f , así que tenemos *&f . En esta expresión, el resultado del operador * es una función, f ; simplemente revertir la operación realizada por & . Entonces *&f es lo mismo que f . Podemos repetir esto indefinidamente: **f se convierte automáticamente a **&f , que es *f , que se convierte automáticamente a *&f , que es f , que se convierte automáticamente a &f .

En C puedes llamar a tu función como:

 f(); (*f)(); (**f)(); (********f)(); (*****************************f)(); 

todos son validos En C, la desreferenciación o la toma de la dirección de una función solo se evalúa como un puntero a esa función, y la desreferenciación de un puntero de función simplemente se evalúa nuevamente al puntero de función. C está diseñado de tal manera que tanto el identificador del nombre de la función como el puntero de la función de retención de variables significan lo mismo: dirección a la memoria de CÓDIGO . Y permite saltar a esa memoria usando la syntax de call () en un identificador o variable.
Y el último pero el menos, el estándar dice que:

C11: 6.3.2.1:

4 Un designador de función es una expresión que tiene un tipo de función. Excepto cuando es el operando del operador sizeof , el _Alignof operator, 65) o el unary & operator, un designador de función con tipo ” función que devuelve tipo ” se convierte en una expresión que tiene tipo ” puntero a función que devuelve tipo ”.