parametros - Punteros de función en C: naturaleza y uso
punteros y arreglos en c (7)
Acabo de leer una pregunta interesante que me hace pensar en dos cosas más:
- ¿Por qué debería alguien comparar los indicadores de función, dado que por concepción, la singularidad de las funciones está garantizada por sus diferentes nombres?
- ¿El compilador ve los punteros de función como punteros especiales? Quiero decir, ¿los ve como, digamos, punteros para
void *
o tiene información más rica (como el tipo de devolución, el número de argumentos y los tipos de argumentos?)
- ¿Por qué debería alguien comparar los indicadores de función, dado que por concepción, la singularidad de las funciones está garantizada por sus diferentes nombres?
Un puntero de función puede señalar diferentes funciones en diferentes momentos en un programa.
Si tiene una variable como
void (*fptr)(int);
puede señalar cualquier función que acepte un int
como entrada y devuelva el void
.
Digamos que tienes:
void function1(int)
{
}
void function2(int)
{
}
Puedes usar:
fptr = function1;
foo(fptr);
o:
fptr = function2;
foo(fptr);
Es posible que desee hacer cosas diferentes en foo
dependiendo de si fptr
apunta a una función u otra. Por lo tanto, la necesidad de:
if ( fptr == function1 )
{
// Do stuff 1
}
else
{
// Do stuff 2
}
- ¿El compilador ve los punteros de función como punteros especiales? Quiero decir, ¿los ve como, digamos, punteros para anular * o tiene información más rica (como el tipo de devolución, el número de argumentos y los tipos de argumentos?)
Sí, los punteros a las funciones son punteros especiales, diferentes de los punteros que apuntan a los objetos.
El tipo de un puntero de función tiene toda esa información en tiempo de compilación. Por lo tanto, proporcione un puntero a la función, el compilador tendrá toda esa información: el tipo de retorno, el número de argumentos y sus tipos.
¿Por qué alguien compararía punteros? Considere la siguiente situación:
Usted tiene una variedad de indicadores de función, digamos que es una cadena de respaldo y debe llamar a cada uno de ellos. La lista finaliza con un puntero de función
NULL
(o centinela). Debe comparar si ha llegado al final de la lista comparando con este puntero centinela. Además, este caso justifica la preocupación previa de OP por el hecho de que diferentes funciones deberían tener diferentes punteros, incluso si son similares.¿El compilador los ve de manera diferente? Sí. La información de tipo incluye toda la información sobre los argumentos y el tipo de devolución.
Por ejemplo, el compilador rechazará / deberá rechazar el siguiente código:
void foo(int a); void (*bar)(long) = foo; // Without an explicit cast
¿Imagine cómo implementaría una funcionalidad similar a la de WNDCLASS
?
Tiene un lpszClassName
para distinguir clases de ventana entre sí, pero digamos que no necesita (o no tiene) una cadena disponible para distinguir diferentes clases entre sí.
Lo que tiene es el procedimiento de clase de ventana lpfnWndProc
(de tipo WindowProc
).
Entonces, ¿qué harías si alguien llama a RegisterClass
dos veces con el mismo lpfnWndProc
?
Necesita detectar re-registros de la misma clase de alguna manera y devolver un error.
Ese es un caso en el que lo más lógico es comparar las funciones de devolución de llamada.
¿Por qué debería alguien comparar indicadores de función? Aquí hay un ejemplo:
#include <stdbool.h>
/*
* Register a function to be executed on event. A function may only be registered once.
* Input:
* arg - function pointer
* Returns:
* true on successful registration, false if the function is already registered.
*/
bool register_function_for_event(void (*arg)(void));
/*
* Un-register a function previously registered for execution on event.
* Input:
* arg - function pointer
* Returns:
* true on successful un-registration, false if the function was not registered.
*/
bool unregister_function_for_event(void (*arg)(void));
El cuerpo de register_function_for_event
solo ve arg
. No ve ningún nombre de función. Debe comparar los indicadores de función para informar que alguien está registrando la misma función dos veces.
Y si desea admitir algo como unregister_function_for_event
para complementar lo anterior, la única información que tiene es la dirección de la función. Entonces, necesitaría pasarlo y compararlo para permitir la eliminación.
En cuanto a la información más rica, sí. Cuando el tipo de función contiene un prototipo, es parte de la información de tipo estático. Tenga en cuenta que en C un puntero a la función puede declararse sin un prototipo, pero es una característica obsoleta.
1) Hay muchas situaciones. Tomemos como ejemplo la implementación típica de una máquina de estados finitos:
typedef void state_func_t (void);
const state_func_t* STATE_MACHINE[] =
{
state_init,
state_something,
state_something_else
};
...
for(;;)
{
STATE_MACHINE[state]();
}
Es posible que deba incluir algún código adicional en la persona que llama para una situación específica:
if(STATE_MACHINE[state] == state_something)
{
print_debug_stuff();
}
2) Sí, el compilador de C los ve como tipos distintos. De hecho, los punteros de función tienen una seguridad de tipo más estricta que otros tipos de C, porque no se pueden convertir implícitamente a / desde void*
, como pueden hacerlo los punteros a los tipos de objeto. (C11 6.3.2.3/1). Tampoco se pueden convertir explícitamente en / from void*
; al hacerlo, invocarían extensiones no estándar.
Todo en el puntero de función importa para determinar su tipo: el tipo del valor de retorno, el tipo de los parámetros y el número de parámetros. Todos estos deben coincidir, o dos punteros a funciones no son compatibles.
La parte clásica sobre los punteros de función ya se discute en la respuesta de los demás:
- Al igual que otros indicadores, los punteros a la función pueden señalar diferentes objetos en diferentes momentos, por lo que compararlos puede tener sentido.
- Los punteros para funcionar son especiales y no deben almacenarse en otros tipos de punteros (ni siquiera
void *
e incluso en lenguaje C). - La parte enriquecida (firma de función) se almacena en el tipo de función: el motivo de la oración anterior.
Pero C tiene un modo de declaración de función (legado). Además del modo de prototipo completo que declara el tipo de retorno y el tipo para todos los parámetros, C puede usar el llamado modo de lista de parámetros , que es el antiguo modo K & R. En este modo, la declaración solo declara el tipo de devolución:
int (*fptr)();
En C, declara un puntero a la función que devuelve un int
y acepta parámetros arbitrarios . Simplemente será un comportamiento indefinido (UB) usarlo con una lista de parámetros incorrecta.
Entonces este es el código legal C:
#include <stdio.h>
#include <string.h>
int add2(int a, int b) {
return a + b;
}
int add3(int a, int b, int c) {
return a + b + c;
}
int(*fptr)();
int main() {
fptr = add2;
printf("%d/n", fptr(1, 2));
fptr = add3;
printf("%d/n", fptr(1, 2, 3));
/* fprintf("%d/n", fptr(1, 2)); Would be UB */
return 0;
}
¡No pretendas que te aconsejé que lo hicieras! Ahora se considera como una característica obsoleta y debe evitarse. Solo te estoy advirtiendo contra eso. En mi humilde opinión solo podría tener algunos casos excepcionales de uso aceptable.
Los punteros de función son variables. ¿Por qué debería alguien comparar variables, dado que por concepto, la singularidad de las variables está asegurada por sus diferentes nombres? Bueno, a veces dos variables pueden tener el mismo valor y quieres saber si es el caso.
C considera que los punteros a funciones con la misma lista de argumentos y el valor de retorno son del mismo tipo.