为什么指向函数的指针可以这种方式转换?

时间:2014-05-07 13:22:58

标签: c++ c

#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;

为什么第一个解决了,我的错了?

4 个答案:

答案 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.functiona.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_CHARARG_TYPE_INT是您自己定义的常量,return_typearg_typestruct 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 ++,那就太好了。

但在C中,void *可以隐式转换。

  
      
  1. cast to void * explicit
  2.   
  3. 强制转换为void *(*)隐式
  4.