#include <stdio.h>
struct A {
int data; // this is data
void *(*function)(); // this is operation
};
void myfun1() {
printf("this is fun()\n");
}
int myfun2(int a) {
printf("this is fun(%d)\n", a);
return a;
}
char myfun3(int a) {
printf("this is fun(%c)\n", a);
return a;
}
int main(void) {
struct A a;
a.function = (void *)myfun2;
a.function('a');
a.function = (void *)myfun3;
a.function('a');
return 0;
}
我想知道为什么
a.function = (void *)myfun2;
因为我首先想到的可能是
a.function =(void *(*)())myfun2;
为什么第一个解决了,我的错了?
答案 0 :(得分:2)
你正在做的是部分确定,但是那些不可能的位可能是最重要的一点。
在C中,void *
是任何对象类型的通用指针(C11 6.3.2.3.1):
指向
void
的指针可以转换为指向任何指针的指针 对象类型。
将指向函数的指针转换为void *
是未定义的行为。函数不是对象。
可以将函数转换为一种类型的函数,转换为指向另一种函数的指针,然后再转回,而不会丢失信息(C11 6.3.2.3.8)。在您的情况下,您在一种情况下将int (*)(int)
转换为void *(*)()
。 (void *(*)())
强制转换是正常的,(void *)
案例不合适。
当您遇到麻烦时,您尝试从转换后的类型中实际调用该函数。再次,从C11 6.3.2.3.8:
如果转换的指针用于调用其类型不是的函数 与引用类型兼容,行为未定义。
因此,在您的情况下,当您将myfun2()
转换为a.function
并a.function =(void *(*)())myfun2
时,转换本身就可以了。但是你不能做的是通过a.function('a')
调用该函数,因为这两个函数的类型不兼容,所以你将进入未定义的行为。
由于您将A.function
定义为具有未指定数量的参数的函数的指针,因此根据C11 6.7.6.3.15:
如果一种类型具有参数类型列表,则指定另一种类型 通过函数声明符,它不是函数定义的一部分 包含空标识符列表,参数列表不得 有一个省略号终止符,每个参数的类型应为 与应用程序产生的类型兼容 默认参数促销
由于int
中只有一个myfun2()
类型的参数,因此满足。真正搞砸的是返回类型,因为void *
和int
不兼容,所以你有不确定的行为。实际上,在大多数现代64位系统中,void *
和int
的大小不一样,所以即使它会让你在这样的系统上尝试这样做,也会产生不可预测的结果。 / p>
因为转换本身没问题,你可以使用任何函数指针来存储任何其他函数指针,所以如果你想在架构中存储有关函数类型的其他信息,并在以后使用它转换为兼容类型,例如:
if ( a.return_type == RET_TYPE_INT && a.arg_type == ARG_TYPE_INT ) {
int (*fp)(int) = (int(*)(int)) a.function;
int n = fp('a');
/* Do something with n */
}
else if ( a.return_type = RET_TYPE_CHAR && a.arg_type == ARG_TYPE_INT ) {
char (*fp)(int) = (char(*)(int)) a.function;
char ch = fp('a');
/* Do something with ch */
}
依此类推,其中RET_TYPE_CHAR
和ARG_TYPE_INT
是您自己定义的常量,return_type
和arg_type
是struct A
的其他成员您在设置a.function
时填充。
答案 1 :(得分:1)
a.function =(void *(*)())myfun2;
我猜你在函数名称隐含函数之后认为()
不接受任何参数。它实际上意味着函数可以采用任意数量的参数。
如果您希望这些无意义的代码是非法的,您应该按照以下内容更改结构
struct A {
int data; /* this is data */
void *(*function)(void); /* this is operation */
};
如果我错误地解释了你的问题,谢尔盖L.已经回答了另一种解释。
答案 2 :(得分:1)
函数指针和任何其他指针类型之间的转换超出了C标准的范围。 C仅指定在void *和指向对象类型的指针(6.3.2.3)之间进行转换时会发生什么。
只要类型兼容,就允许两个不同函数指针之间的转换。如果它们不兼容,则调用未定义的行为(6.3.2.3/8)。
我相信C和C ++的工作方式完全相同。 C允许void *和指向 object 的指针之间的隐式转换这一事实与问题完全无关。
所以回答你的问题:
为什么可以以这种方式转换函数指针?
除非您依赖非标准扩展,否则不能。
为什么第一个解决了,我的错了?
除非您依赖非标准扩展,否则第一个不起作用。
第二个可能有效,也可能不起作用,具体取决于特定系统上的函数调用约定。
答案 3 :(得分:-2)
但在C中,void *
可以隐式转换。
- cast to void * explicit
- 强制转换为void *(*)隐式
醇>