为什么指向函数的指针可以用这种方式进行强制转换

why the pointer to function can be cast in this way?

本文关键字:方式进 转换 函数 指针 为什么      更新时间:2023-10-16
#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;

为什么第一个成功了,而我的却错了?

您正在做的是部分正常,但不正常的部分可能是最重要的部分。

在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:

如果转换后的指针用于调用类型不是与引用的类型兼容,行为未定义。

因此,在您的情况下,例如,当您使用a.function =(void *(*)())myfun2myfun2()转换为a.function时,转换本身就很好。但您不能通过a.function('a')调用该函数,因为这两个函数的类型不兼容,因此您将陷入未定义的行为。

由于您将A.function定义为指向具有未指定参数数量的函数的指针,因此根据C11 6.7.6.3.15:,您可以在参数前端进行兼容性

如果一个类型有参数类型列表,而指定了另一个类型由不属于函数定义的函数声明符执行,并且包含空的标识符列表,则参数列表不应具有省略号终止符,每个参数的类型应为与应用默认参数促销

并且由于在myfun2()中只有一个类型为int的参数,这是满足的。真正让您感到困惑的是返回类型,因为void *int是不兼容的,所以您有未定义的行为。事实上,在大多数现代64位系统上,void *int的大小都不一样,所以在这样的系统上尝试这样做可能会产生不可预测的结果,即使它允许你这样做。

因为转换本身是可以的,你可以使用任何函数指针来存储任何其他的,所以如果你想的话,你可以在结构中存储关于函数类型的额外信息,然后用它转换成兼容的类型,例如:

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时填充这些成员。

a.function =(void *(*)())myfun2;

我想你认为函数名称后面的()意味着函数不接受任何参数。它实际上意味着函数可以接受任意数量的参数。

如果你想让这种毫无意义的代码是非法的,你应该按照改变你的结构

struct A {
  int data;             /* this is data */
  void *(*function)(void);  /* this is operation */
};

如果我误解了你的问题,Sergey L.已经回答了另一种解释。

函数指针和任何其他指针类型之间的转换不在C标准的范围内。C仅指定在void*和指向对象类型的指针之间进行强制转换时会发生什么(6.3.2.3(

只要类型兼容,就允许在两个不同的函数指针之间进行转换。如果它们不兼容,则调用未定义的行为(6.3.2.3/8(

我相信C和C++的工作原理完全相同。事实上,C允许在void*和指向对象的指针之间进行隐式转换,这与这个问题完全无关。


因此,为了回答您的问题:

为什么指向函数的指针可以用这种方式进行强制转换?

它不能,除非您依赖非标准扩展。

为什么第一个成功了,而我的却错了?

除非您依赖非标准扩展,否则第一个是不起作用的。

第二个可能工作,也可能不工作,这取决于特定系统上的函数调用约定。

嗯,如果你在C++中,这很好。

但在C中,void *可以是隐式的。

  1. 强制转换为void*显式
  2. 强制转换为void*(*(隐式
相关文章: