Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C function pointer casting to void pointer

I am trying to run the following program but getting some strange errors:

File 1.c:

typedef unsigned long (*FN_GET_VAL)(void);

FN_GET_VAL gfnPtr;

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

File 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 */
}

Here the gfnPtr() is crashing on 64-Bit suse linux when compiled with gcc. But it successfully calling gfnPtr() VC6 and SunOS.

But if I change the function as given below, it is working successfully.

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

Please help with the cause of problem. Thanks.

like image 802
Manoj Avatar asked Sep 02 '25 15:09

Manoj


2 Answers

The C standard does not allow to cast function pointers to void*. You may only cast to another function pointer type. In the C11 standard, 6.3.2.3 §8:

A pointer to a function of one type may be converted to a pointer to a function of another type and back again

Importantly, you must cast back to the original type before using the pointer to call the function (technically, to a compatible type. Definition of "compatible" at 6.2.7).

Note that the POSIX standard, which many (but not all) C compilers have to follow too because of the context in which they are used, mandates that a function pointer can be converted to void* and back. This is necessary for some system functions (e.g. dlsym).

like image 137
Pascal Cuoq Avatar answered Sep 05 '25 13:09

Pascal Cuoq


The standard unfortunately doesn't allow casting between data pointers and function pointers (because that might not make sense on some really obscure platforms), even though POSIX and others require such casts. One workaround is not to cast the pointer but to cast a pointer to the pointer (this is OK by the compiler and it will get the job done on all normal platforms).

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 restored
like image 36
Tronic Avatar answered Sep 05 '25 13:09

Tronic