Fundición del puntero de la función C para anular el puntero

Estoy intentando ejecutar el siguiente progtwig pero obteniendo algunos errores extraños:

Archivo 1.c:

typedef unsigned long (*FN_GET_VAL)(void); FN_GET_VAL gfnPtr; void setCallback(const void *fnPointer) { gfnPtr = *((FN_GET_VAL*) (&fnPointer)); } 

Archivo 2.c:

 extern FN_GET_VAL gfnPtr; unsigned long myfunc(void) { return 0; } main() { setCallback((void*)myfunc); gfnPtr(); /* Crashing as value was not properly assigned in setCallback function */ } 

Aquí el gfnPtr () se cuelga en suse linux de 64 bits cuando se comstack con gcc. Pero está llamando con éxito a gfnPtr () VC6 y SunOS.

Pero si cambio la función como se indica a continuación, funciona correctamente.

 void setCallback(const void *fnPointer) { int i; // put any statement here gfnPtr = *((FN_GET_VAL*) (&fnPointer)); } 

Por favor ayuda con la causa del problema. Gracias.

    El estándar no permite emitir punteros a la función void* . Solo puede convertir a otro tipo de puntero a función. 6.3.2.3 §8:

    Un puntero a una función de un tipo se puede convertir en un puntero a una función de otro tipo y viceversa

    Es importante destacar que debe regresar al tipo original antes de usar el puntero para llamar a la función (técnicamente, a un tipo compatible. Definición de “compatible” en 6.2.7).

    Desafortunadamente, el estándar no permite la conversión entre los punteros de datos y los punteros a las funciones (debido a que puede no tener sentido en algunas plataformas realmente poco claras), aunque POSIX y otros requieren tales conversiones. Una solución no es lanzar el puntero sino lanzar un puntero al puntero (esto está bien para el comstackdor y hará el trabajo en todas las plataformas normales).

     typedef void (*FPtr)(void); // Hide the ugliness FPtr f = someFunc; // Function pointer to convert void* ptr = *(void**)(&f); // Data pointer FPtr f2 = *(FPtr*)(&ptr); // Function pointer restred 

    Tengo tres reglas generales cuando se trata de punteros de datos y punteros de código:

    • No mezcle punteros de datos ni punteros de código
    • No mezcle punteros de datos ni punteros de código
    • ¡ Nunca mezcles punteros de datos y punteros de código!

    En la siguiente función:

     void setCallback(const void *fnPointer) { gfnPtr = *((FN_GET_VAL*) (&fnPointer)); } 

    Usted tiene un puntero de datos que usa para un puntero de función. (Sin mencionar que usted hace esto tomando primero la dirección del puntero en sí, colóquelo en un puntero a un puntero, antes de quitarle la referencia).

    Intenta reescribirlo como:

     void setCallback(FN_GET_VAL fnPointer) { gfnPtr = fnPointer; } 

    Además, puede (o debería) soltar el elenco cuando configure el puntero:

     main() { setCallback(myfunc); gfnPtr(); } 

    Como bonificación adicional, ahora puede usar las verificaciones de tipo normales realizadas por el comstackdor.

    Sugeriré una posible explicación parcial .

    @Manoj Si examina (o puede proporcionar) la lista de ensamblaje para SetCallback generada por ambos comstackdores, podemos obtener una respuesta definitiva.

    En primer lugar, las declaraciones de Pascal Couq son correctas, y Lindydancer muestra cómo establecer correctamente la callback. Mi respuesta es solo un bash de explicar el problema real.

    Creo que el problema se debe al hecho de que Linux y la otra plataforma usan diferentes modelos de 64 bits (ver modelos de 64 bits en Wikipedia ). Tenga en cuenta que Linux usa LP64 (int es 32 bit). Necesitamos más detalles sobre la otra plataforma. Si es SPARC64, usa ILP64 (int es 64 bit).

    Según tengo entendido, el problema solo se observó en Linux, y desapareció si introdujo una variable local int. ¿Intentó esto con las optimizaciones de vez en cuando? Lo más probable es que este hack no tenga ningún efecto beneficioso con las optimizaciones.

    En ambos modelos de 64 bits, los punteros deben ser de 64 bits, independientemente de si apuntan a código o datos. Sin embargo, es posible que este no sea el caso (por ejemplo, modelos de memoria segmentada); de ahí, las advertencias de Pascal y Lindydancer.

    Si los punteros son del mismo tamaño, lo que queda es un posible problema de alineación de stack. La introducción de un int local (que es de 32 bits en Linux) podría alterar la alineación. Esto solo tendría un efecto si void * y los punteros de función tienen diferentes requisitos de alineación. Un escenario dudoso.

    Sin embargo, es probable que los diferentes modelos de memoria de 64 bits sean la causa de lo que observaste. Le invitamos a proporcionar los listados de ensamblaje para que podamos analizarlos.

    a diferencia de lo que otros dicen, sí, puede tener un puntero void* como puntero a una función, pero la semántica es muy difícil de usar.

    Como puede ver, NO NECESITA lanzarlo como un void* , simplemente asígnelo como lo hace normalmente. Ejecuté su código así y lo edité para que funcione.

    archivo1.c:

     typedef unsigned long (*FN_GET_VAL)(void); extern FN_GET_VAL gfnPtr; void setCallback(const void *fnPointer) { gfnPtr = ((FN_GET_VAL) fnPointer); } 

    archivo2.c:

     int main(void) { setCallback(myfunc); (( unsigned long(*)(void) )gfnPtr)(); }