为什么指向函数的指针可以用这种方式进行强制转换
why the pointer to function can be cast in this way?
#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 *(*)())myfun2
将myfun2()
转换为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_CHAR
和ARG_TYPE_INT
是您自己定义的常量,return_type
和arg_type
是struct 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 *
可以是隐式的。
- 强制转换为void*显式
- 强制转换为void*(*(隐式
- 如何将包含epoch时间的十六进制字符串转换为time_t
- 将字符指针十六进制转换为字符串并保存在文本文件C++中
- 如何将一个ostringstream十六进制字符串字符对转换为单个unit8t等价的二进制值
- 为什么mpfr_printf与十六进制浮点(%a转换说明符)的printf不同
- 用c++把小数点转换成八进制
- 如何在C++中将十六进制字符串转换为文本数据
- 是什么导致了这种使用三进制而不是短整型的有符号int到无符号int转换
- 使用 sprintf 将十六进制0xAABBCC转换为字符串"AA:BB:CC"
- 以线程安全的方式转换 C/C++ 中时区名称字符串的时区偏移量
- 如何将带有十六进制值的 std::string 转换为 std::vector<无符号字符>
- 将字符串(可以是十进制字符串或十六进制字符串)转换为整数C++
- 如何以滑动窗口方式从 std::bitset 读取位并将它们转换为 int?
- 在Arduino中将字符串转换为(逗号分隔的十六进制)字符串到无符号字符数组
- 将 std::string( "x3BxDEx7C" ) 转换为可读的十六进制字符串?
- 将十六进制转换为 DEC
- C++ 将十六进制字符串表示形式转换为十六进制字符串表示形式
- 字符数组到十六进制字符串的转换 - 意外输出
- C++将 wwn 字符串转换为识别为十六进制的数据类型
- 一种体面的方式来转换const列表参考参数,然后传递到另一个函数
- 需要一个更快的方式来转换cv::Mat成一维矢量形式