C函数指针转换为void指针

Man*_*noj 20 c pointers function-pointers

我试图运行以下程序,但得到一些奇怪的错误:

档案1.c:

typedef unsigned long (*FN_GET_VAL)(void);

FN_GET_VAL gfnPtr;

void setCallback(const void *fnPointer)
{
    gfnPtr = *((FN_GET_VAL*) (&fnPointer));
}
Run Code Online (Sandbox Code Playgroud)

档案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 */
}
Run Code Online (Sandbox Code Playgroud)

这里使用gcc编译时,gfnPtr()在64位suse linux上崩溃.但它成功调用了gfnPtr()VC6和SunOS.

但如果我改变下面给出的功能,它就能成功运行.

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

请帮助解决问题的原因.谢谢.

Pas*_*uoq 37

该标准不允许将函数指针强制转换为void*.您可能只会转换为另一个函数指针类型.6.3.2.3§8:

指向一种类型的函数的指针可以被转换为指向另一种类型的函数的指针并且再次返回

重要的是,在使用指针调用函数之前必须强制转换回原始类型(技术上,为兼容类型.在6.2.7中定义"compatible").

  • 这取决于您使用的标准 - POSIX 标准要求 C 编译器支持将函数指针安全地转换为 void * 并再次返回。 (2认同)

Tro*_*nic 14

遗憾的是,标准不允许在数据指针和函数指针之间进行转换(因为这可能在一些非常模糊的平台上没有意义),即使POSIX和其他人需要这样的转换.一种解决方法是不转换指针,而是转换指针指针(编译器可以这样做,它将在所有普通平台上完成工作).

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
Run Code Online (Sandbox Code Playgroud)

  • “因为这在一些真正不知名的平台上可能没有意义”真正不知名的平台,如 Linux、Mac OS X、iOS、Microsoft Windows 和 Android,它们具有数据执行保护。通过数据执行保护,代码和数据不可互换。注意:POSIX 要求强制转换为 void 指针,但不要求此类结果指针可用,除非转换回函数指针。 (2认同)
  • @JonathanBaldwin您所说的仅在代码指针大小与数据指针大小不同的平台上才有意义。指针只是一个地址,因此永远不在可执行区域中。但是,该地址指向的数据可能在可执行区域中。因此,您可能无法读取指针指向的内存。但是您肯定可以使用此方法将其从函数指针转换为void *,并从void *转换为函数指针。除非平台的函数指针大小与数据指针大小不同,否则它将起作用。 (2认同)

Lin*_*cer 9

当涉及到数据指针和代码指针时,我有三条经验法则:

  • 千万不能混用数据指针和代码指针
  • 不要混合数据指针和代码指针
  • 不要永远混合数据指针和代码指针!

在以下功能中:

void setCallback(const void *fnPointer)
{
    gfnPtr = *((FN_GET_VAL*) (&fnPointer));
}
Run Code Online (Sandbox Code Playgroud)

你有一个数据指针,你需要一个函数指针.(更不用说你通过首先获取指针本身的地址,将它转换为指向指针的指针,然后取消引用它)来实现这一点.

尝试将其重写为:

void setCallback(FN_GET_VAL fnPointer)
{
     gfnPtr = fnPointer;
}
Run Code Online (Sandbox Code Playgroud)

此外,您可以(或应该)在设置指针时删除强制转换:

main()
{
   setCallback(myfunc);
   gfnPtr();
}
Run Code Online (Sandbox Code Playgroud)

作为额外的奖励,您现在可以使用编译器执行的常规类型检查.