C函数指针强制转换为void指针

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

文件1.c:

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

文件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 */ } 

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

但是,如果我改变下面给出的function,它是成功的。

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

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

该标准不允许将函数指针转换为void* 。 您只能投射到另一个函数指针types。 6.3.2.3§8:

指向一种types的函数的指针可以被转换为指向另一种types的函数的指针并返回

重要的是,在使用指针调用函数之前,必须重新使用原始types(技术上,兼容types,6.2.7中的“compatible”的定义)。

不幸的是,这个标准不允许在数据指针和函数指针之间进行转换(因为这在一些非常模糊的平台上可能没有意义),尽pipePOSIX和其他人需要这样的转换。 一种解决方法不是投射指针,而是投射一个指向指针的指针(这是编译器确定的,它将在所有正常的平台上完成工作)。

 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 

数据指针和代码指针有三条经验法则:

  • 不要混合数据指针和代码指针
  • 不要混合数据指针和代码指针
  • 千万不要混合数据指针和代码指针!

在以下function中:

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

你有一个数据指针 ,你遇到一个函数指针。 (更不用说,你通过首先获取指针本身的地址来做到这一点,在去引用它之前将它转换为指向指针的指针)。

尝试将其重写为:

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

另外,你可以(或者应该)在设置指针的时候放下演员:

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

作为额外的奖励,您现在可以使用编译器执行的正常types检查。

我会build议一个可能的部分解释。

@Manoj如果您检查(或可以提供)由两个编译器生成的SetCallback的汇编列表,我们可以得到一个明确的答案。

首先,Pascal Couq的陈述是正确的,Lindydancer展示了如何正确设置callback。 我的回答只是试图解释实际的问题。

我认为问题源于Linux和其他平台使用不同的64位模型(请参阅Wikipedia上的64位模型 )。 请注意,Linux使用LP64(int是32位)。 我们需要更多的细节在另一个平台上。 如果是SPARC64,则使用ILP64(int为64位)。

据我所知,这个问题只在Linux下才被观察到,如果你引入了一个int局部variables,那么问题就消失了。 你有没有尝试优化或closures? 最有可能的这种黑客将没有有利的影响优化。

在这两种64位模型下,无论指向代码还是数据,指针都应该是64位。 但是,这可能不是这种情况(例如,分段内存模型); 因此,Pascal和Lindydancer的警告。

如果指针大小相同,剩下的就是可能的堆栈alignment问题。 引入一个本地int(在Linux下是32位)可以改变alignment。 如果void *和函数指针有不同的alignment要求,这只会有效果。 可疑的情况。

不过,不同的64位内存模型最有可能是您观察到的原因。 欢迎您提供汇编列表,以便我们可以分析它们。

不像其他人所说,是的,你可以有一个void*指针作为一个函数指针,但语义是非常棘手的使用它。

正如你所看到的,你不需要把它作为一个void*分配,就像正常分配一样。 我运行你的代码,我编辑它的工作。

file1.c中:

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

file2.c中:

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