Función que se devuelve a sí misma

7 minutos de lectura

¿Es posible declarar algún tipo de función? func_t que devuelve ese tipo, func_t?

En otras palabras, ¿es posible que una función se devuelva a sí misma?

// func_t is declared as some sort of function pointer
func_t foo(void *arg)
{
  return &foo;
}

O tendria que usar void * y encasillamiento?

  • @Baratong Imagine implementar una máquina de estado donde cada estado está representado por una función. La función, al procesar el estado, devuelve una función con el nuevo estado. Esto requeriría declarar una función que devuelva su propio tipo, algo que AFAIK es imposible en C.

    – usuario4815162342

    8 de julio de 2013 a las 22:02

  • @ user4815162342 sí, es posible, vea mi respuesta para más detalles.

    – roland

    20 sep 2016 a las 16:10

avatar de usuario
ouah

No, no puede declarar tipos de funciones recursivas en C. Excepto dentro de una estructura (o una unión), no es posible declarar un tipo recursivo en C.

Ahora para el void * solución, void * solo se garantiza que contiene punteros a objetos y no punteros a funciones. Ser capaz de convertir punteros de función y void * está disponible sólo como una extensión.

  • ¿Qué pasa si usó la función de declaración hacia adelante?

    –Lews Therin

    8 de julio de 2013 a las 21:46

  • Eso es lo que estaba pensando, pero no estoy seguro de cómo hacerlo.

    – Drew McGowen

    8 de julio de 2013 a las 21:47

  • @LewsTherin No sé cómo ayudaría. ¿Algún ejemplo?

    – ouah

    08/07/2013 a las 21:52

  • Por lo que vale, en la configuración habitual de “máquina de estado con puntero de función para hacer el avance de la máquina de estado”, puede tener una función que devuelva un struct state_machine objeto en el que hay un puntero a función tiene tipo struct state_machine (*fp)(args). Una función foo puede entonces “regresar a sí mismo” con: struct state_machine ret; ret.fp = foo; ... return ret;

    – torek

    9 de julio de 2013 a las 5:49

  • ¿Qué tal agregar que todos los tipos de punteros de función son convertibles de ida y vuelta?

    – Deduplicador

    30 de julio de 2014 a las 14:49

Una posible solución con estructuras:

struct func_wrap
{
    struct func_wrap (*func)(void);
};

struct func_wrap func_test(void)
{
    struct func_wrap self;

    self.func = func_test;
    return self;
}

Compilando con gcc -Wall no dio advertencias, pero no estoy seguro de si esto es 100% portátil.

  • Compile con -pedantic para advertir sobre el uso de funciones no estándar.

    – Chris

    17/03/2014 a las 20:19

  • @Chris compila con gcc -Wall -Werror -Wextra -pedantic -c func.c -std=c89 sin problema

    – Panzi

    27 de julio de 2014 a las 19:56

  • ¡Esto es brillante, sin casting en absoluto, tipos limpios! Debería ser la respuesta preferida, ya que proporciona no solo explicaciones, sino también una solución adecuada. ¡Me gusta la forma C++ de pasar objetos de función (más o menos…)!

    – Ingmar

    9 de abril de 2019 a las 9:35


avatar de usuario
insecto del milenio

No puedes lanzar punteros de función a void* (pueden ser de diferentes tamaños), pero eso no es un problema ya que podemos convertir a otro tipo de puntero de función y volver a convertirlo para obtener el valor original.

typedef void (*fun2)();
typedef fun2 (*fun1)();

fun2 rec_fun()
{
    puts("Called a function");
    return (fun2)rec_fun;
}

// later in code...
fun1 fp = (fun1)((fun1)rec_fun())();
fp();

Producción:

Called a function
Called a function
Called a function

  • Conversión de punteros de función a void * es una extensión: es Reconocido según el estándar, funciona en casi todos los compiladores del mundo real, incluso si el estándar no lo exige. No está prohibido ni indefinido, por lo que “no se puede” es una palabra incorrecta.

    – Leushenko

    8 de julio de 2013 a las 23:29

  • @Leushenko "even if the standard doesn't demand it" deténgase aquí: si el estándar C no exige algo (es decir, no lo define), el comportamiento no está definido, por lo tanto no puedes. (Lo verifiqué: el estándar C no define conversiones de punteros de función a punteros de datos). Si la conversión funciona en su código, entonces no está escribiendo C, está escribiendo en su superconjunto (que no es C)

    – bicho del milenio

    9 de julio de 2013 a las 0:24

  • @Leushenko Que yo sepa, la única garantía sobre las conversiones de puntero de función en C99 es “Un puntero a una función de un tipo puede convertirse en un puntero a una función de otro tipo y viceversa; el resultado se comparará igual al puntero original. Si se utiliza un puntero convertido para llamar a una función cuyo tipo no es compatible con el tipo apuntado, el comportamiento no está definido” (6.3.2.3:8). ¿Dónde “reconoce” el estándar C99 las conversiones a void *?

    – Pascal Cuoq

    9 de julio de 2013 a las 0:55

  • @Leushenko Además, todo lo que no está definido es indefinido: “El comportamiento indefinido se indica de otro modo en esta Norma Internacional mediante las palabras ‘comportamiento indefinido’ o mediante la omisión de cualquier definición explícita de comportamiento” (4:2)

    – Pascal Cuoq

    9 de julio de 2013 a las 0:56


  • J.5.7: Un puntero a un objeto oa void puede convertirse en un puntero a una función, lo que permite invocar datos como una función (6.5.4). Un puntero a una función puede convertirse en un puntero a un objeto o anularse, lo que permite inspeccionar o modificar una función (por ejemplo, mediante un depurador) (6.5.4). C99 y C11.

    – Leushenko

    9 de julio de 2013 a las 2:26


avatar de usuario
roland

En otras palabras, ¿es posible que una función se devuelva a sí misma?

Depende de lo que entiendas por “en sí mismo”; si te refieres a un puntero a sí mismo, ¡entonces la respuesta es sí! Si bien no es posible que una función devuelva su tipo, una función puede devolver un puntero a sí misma y este puntero se puede convertir al tipo apropiado antes de llamar.

Los detalles se explican en la pregunta comp.lang.c faq: Función que puede devolver un puntero a una función del mismo tipo.

Revisa mi respuesta para más detalles.

Supongamos la definición de la función

T f(void)
{
  return &f;
}

f() devuelve un valor de tipo Tpero el tipo de expresión &f es “puntero a la función que regresa T“. No importa lo que T es, la expresión &f siempre será de una diferente, incompatible escribe T (*)(void). Incluso si T es un tipo de puntero a función como Q (*)(void)la expresion &f terminará siendo “puntero-a-función-retorno-puntero-a-función”, o Q (*(*)(void))(void).

Si T es un tipo integral que es lo suficientemente grande como para contener un valor de puntero de función y conversión de T (*)(void) para T y de vuelta a T (*)(void) es significativo en su plataforma, usted puede que ser capaz de salirse con la suya con algo como

T f(void)
{ 
  return (T) &f;
}

pero puedo pensar en al menos un par de situaciones en las que eso no funcionará en absoluto. Y, sinceramente, su utilidad sería extremadamente limitada en comparación con el uso de algo como una tabla de búsqueda.

C simplemente no fue diseñado para tratar funciones como cualquier otro elemento de datos, y los punteros a funciones no son intercambiables con los punteros a tipos de objetos.

  • Si insistes en votar negativo, al menos dímelo. por qué para que pueda arreglar lo que creas que es el error.

    – Juan Bode

    18 de noviembre de 2013 a las 12:36

avatar de usuario
Jugador Grady

que tal algo como esto:

typedef void* (*takesDoubleReturnsVoidPtr)(double);

void* functionB(double d)
{
    printf("here is a function %f",d);
    return NULL;
}

takesDoubleReturnsVoidPtr functionA()
{
    return functionB;
}

int main(int argc, const char * argv[])
{
    takesDoubleReturnsVoidPtr func = functionA();
    func(56.7);
    return 0;
}

  • Si insistes en votar negativo, al menos dímelo. por qué para que pueda arreglar lo que creas que es el error.

    – Juan Bode

    18 de noviembre de 2013 a las 12:36

avatar de usuario
robocop

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

typedef void *(*fptr)(int *);
void *start (int *);
void *stop (int *);
void *start (int *a) {
         printf("%s\n", __func__);
         return stop(a);
}
void *stop (int *a) {
         printf("%s\n", __func__);
         return start(a);
}
int main (void) {
         int a = 10;
         fptr f = start;
         f(&a);
         return 0;
}

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad