C语言 C 函数指针转换为 void 指针
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5579835/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C function pointer casting to void pointer
提问by Manoj
I am trying to run the following program but getting some strange errors:
我正在尝试运行以下程序,但遇到一些奇怪的错误:
File 1.c:
文件 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:
文件 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.
此处 gfnPtr() 在使用 gcc 编译时在 64 位 suse linux 上崩溃。但它成功调用了 gfnPtr() VC6 和 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.
请帮助找出问题的原因。谢谢。
回答by Pascal Cuoq
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:
C 标准不允许将函数指针强制转换为void*. 您只能转换为另一种函数指针类型。在C11 标准中,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).
重要的是,您必须在使用指针调用函数之前转换回原始类型(从技术上讲,转换为兼容类型。“兼容”的定义在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).
请注意,由于使用它们的上下文,许多(但不是全部)C 编译器也必须遵循 POSIX 标准,它要求函数指针可以相互转换void*。这对于某些系统功能(例如dlsym)是必需的。
回答by Tronic
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).
不幸的是,该标准不允许在数据指针和函数指针之间进行转换(因为这在一些非常晦涩的平台上可能没有意义),即使 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
回答by Lindydancer
I've got three rules of thumb when it come to data pointers and code pointers:
当涉及到数据指针和代码指针时,我有三个经验法则:
- Do notmix data pointers and code pointers
- Do not mixdata pointers and code pointers
- Do not evermix data pointers and code pointers!
- 千万不能混用数据指针和代码指针
- 不要混合数据指针和代码指针
- 永远不要混合数据指针和代码指针!
In the following function:
在以下函数中:
void setCallback(const void *fnPointer)
{
gfnPtr = *((FN_GET_VAL*) (&fnPointer));
}
You have a data pointerthat you case to a function pointer. (Not to mention that you do this by first taking the address of the pointer itself, cast it to a pointer to a pointer, before de-referencing it).
您有一个指向函数指针的数据指针。(更不用说您是通过首先获取指针本身的地址,将其转换为指向指针的指针,然后再取消引用它来做到这一点的)。
Try to rewrite it as:
尝试将其重写为:
void setCallback(FN_GET_VAL fnPointer)
{
gfnPtr = fnPointer;
}
Also, you can (or should) drop the cast when setting the pointer:
此外,您可以(或应该)在设置指针时删除强制转换:
main()
{
setCallback(myfunc);
gfnPtr();
}
As an extra bonus, you could now use the normal type checks performed by the compiler.
作为额外的奖励,您现在可以使用编译器执行的正常类型检查。
回答by Kevin A. Naudé
I will suggest a possible partialexplanation.
我会建议一个可能的部分解释。
@Manoj If you examine (or can provide) the assembly listing for SetCallback generated by both compilers, we can get a definitive answer.
@Manoj 如果您检查(或可以提供)由两个编译器生成的 SetCallback 的程序集列表,我们可以获得明确的答案。
Firstly, Pascal Couq's statements are correct, and Lindydancer shows how to correctly set the callback. My answer is only an attempt at explaining the actual problem.
首先,Pascal Couq 的说法是正确的,Lindydancer 展示了如何正确设置回调。我的回答只是试图解释实际问题。
I think the problem stems from the fact that Linux and the other platform use different 64-bit models (see 64-bit models on Wikipedia). Note that Linux uses LP64 (int is 32 bit). We need more detail on the other platform. If it is SPARC64, it uses ILP64 (int is 64 bit).
我认为问题源于 Linux 和其他平台使用不同的 64 位模型(请参阅Wikipedia 上的 64 位模型)这一事实。请注意,Linux 使用 LP64(int 是 32 位)。我们需要有关其他平台的更多详细信息。如果是 SPARC64,则使用 ILP64(int 为 64 位)。
As I understand you, the problem was only observed under Linux, and went away if you introduced an int local variable. Did you try this with optimisations off or on? Most likely this hack would have no beneficial effect with optimisations on.
据我了解,该问题仅在 Linux 下观察到,如果您引入 int 局部变量,该问题就会消失。您是否在关闭或打开优化的情况下尝试过?很可能这种 hack 不会对优化产生任何有益的影响。
Under both 64-bit models, pointers should be 64-bit, regardless of whether they point to code or data. However, it is possible that this would not be the case (e.g. segmented memory models); hence, Pascal and Lindydancer's admonisions.
在两种 64 位模型下,指针都应该是 64 位的,无论它们是指向代码还是数据。然而,情况可能并非如此(例如分段内存模型);因此,Pascal 和 Lindydancer 的告诫。
If the pointers are the same size, what remains is a possible stack alignment issue. Introducing a local int (which is 32 bit under Linux) could alter alignment. This would only have an effect if void* and function pointers have different alignment requirements. A doubtful scenario.
如果指针大小相同,剩下的就是可能的堆栈对齐问题。引入本地 int(在 Linux 下为 32 位)可能会改变对齐方式。只有当 void* 和函数指针具有不同的对齐要求时,这才会产生影响。一个可疑的场景。
Nevertheless, the different 64-bit memory models are most likely the cause of what you observed. You are welcome to provide the assembly listings so that we can analyse them.
然而,不同的 64 位内存模型很可能是您观察到的原因。欢迎您提供装配清单,以便我们对其进行分析。
回答by Nergal
unlike what others say, yes you can have a void*pointer as a function pointer but the semantics are very tricky to use it with.
与其他人所说的不同,是的,您可以将void*指针作为函数指针,但语义使用起来非常棘手。
As you can see, you don't NEED to cast it as a void*, just assign it like normal. I ran your code like this and I edited it to work.
如您所见,您不需要将其转换为 a void*,只需像平常一样分配它即可。我像这样运行了你的代码,并对其进行了编辑以使其正常工作。
file1.c:
文件1.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:
文件2.c:
int main(void)
{
setCallback(myfunc);
(( unsigned long(*)(void) )gfnPtr)();
}

