¿Por qué usar el nombre de la función como un puntero de función equivalente a aplicar el operador de dirección al nombre de la función?

¡Es interesante que usar el nombre de la función como un puntero de función es equivalente a aplicar el operador de dirección al nombre de la función !

Aquí está el ejemplo.

typedef bool (*FunType)(int); bool f(int); int main() { FunType a = f; FunType b = &a; // Sure, here's an error. FunType c = &f; // This is not an error, though. // It's equivalent to the statement without "&". // So we have c equals a. return 0; } 

Usar el nombre es algo que ya sabemos en conjunto. Pero no puedes escribir algo como

 int a[2]; int * b = &a; // Error! 

Parece no ser consistente con otras partes del lenguaje. ¿Cuál es la razón de este diseño?

Esta pregunta explica la semántica de tal comportamiento y por qué funciona. Pero estoy interesado en por qué el lenguaje fue diseñado de esta manera.

Lo que es más interesante es que el tipo de función se puede convertir implícitamente en puntero a sí mismo cuando se usa como parámetro, pero no se convertirá en un puntero a sí mismo cuando se usa como un tipo de retorno.

Ejemplo:

 typedef bool FunctionType(int); void g(FunctionType); // Implicitly converted to void g(FunctionType *). FunctionType h(); // Error! FunctionType * j(); // Return a function pointer to a function // that has the type of bool(int). 

Dado que usted específicamente solicita la justificación de este comportamiento, aquí está lo más cercano que puedo encontrar (del documento ANSI C90 Rationale – http://www.lysator.liu.se/c/rat/c3.html#3-3- 2-2 ):

3.3.2.2 Llamadas de función

Los punteros a las funciones se pueden usar como (*pf)() o como pf() . Este último constructo, no sancionado en el Documento base, aparece en algunas versiones actuales de C, no es ambiguo, no invalida el código anterior y puede ser una taquigrafía importante. La abreviatura es útil para paquetes que presentan solo un nombre externo, que designa una estructura llena de punteros para objetos y funciones: las funciones miembro se pueden llamar como graphics.open(file) lugar de (*graphics.open)(file) . El tratamiento de los designadores de funciones puede conducir a algunas formas sintácticas curiosas, pero válidas. Dadas las declaraciones:

 int f ( ) , ( *pf ) ( ) ; 

entonces todas las siguientes expresiones son llamadas a funciones válidas:

 ( &f)(); f(); (*f)(); (**f)(); (***f)(); pf(); (*pf)(); (**pf)(); (***pf)(); 

La primera expresión en cada línea se discutió en el párrafo anterior. El segundo es el uso convencional. Todas las expresiones posteriores aprovechan la conversión implícita de un designador de función a un valor de puntero en casi todos los contextos de expresión. El Comité no vio ningún daño real al permitir estas formas; prohibir formas como (*f)() , mientras que todavía permite *a (para int a[]) , simplemente parecía más problemas de lo que valía.

Básicamente, la equivalencia entre los designadores de funciones y los punteros a las funciones se agregó para hacer que el uso de punteros a las funciones sea un poco más conveniente.

Es una característica heredada de C.

En C, está permitido principalmente porque no hay mucho más que el nombre de una función, por sí mismo, pueda significar. Todo lo que puede hacer con una función real es llamarlo. Si no lo está llamando, lo único que puede hacer es tomar la dirección. Como no hay ambigüedad, cada vez que un nombre de función no es seguido por un ( para indicar una llamada a la función, el nombre evalúa la dirección de la función.

Eso en realidad es algo similar a otra parte del lenguaje: el nombre de una matriz se evalúa como la dirección del primer elemento de la matriz, excepto en algunas circunstancias bastante limitadas (se usa como el operando de & o el sizeof ).

Como C lo permitió, C ++ también lo hace, principalmente porque lo mismo sigue siendo cierto: lo único que puede hacer con una función es llamarla o tomar su dirección, por lo tanto, si el nombre no está seguido de a ( para indicar una llamada de función , entonces el nombre evalúa la dirección sin ambigüedad.

Para las matrices, no hay disminución de puntero cuando se utiliza el operador de dirección:

 int a[2]; int * p1 = a; // No address-of operator, so type is int* int (*p2)[2] = &a; // Address-of operator used, so type is int (*)[2] 

Esto tiene sentido porque las matrices y punteros son tipos diferentes, y es posible, por ejemplo, devolver referencias a matrices o pasar referencias a matrices en funciones.

Sin embargo, con funciones, ¿qué otro tipo podría ser posible?

 void foo(){} &foo; // #1 foo; // #2 

Imaginemos que solo el # 2 proporciona el tipo void(*)() , ¿cuál sería el tipo de &foo ? No hay otra posibilidad.