C 对其他函数使用void(*)指针
通过C 对其他函数使用void(*)指针,c,function,pointers,language-lawyer,C,Function,Pointers,Language Lawyer,通过void(*f)(指针访问中具有不同参数列表的函数的指针是否合法?下面的程序使用gcc编译时没有警告,看起来运行正常,但它是否合法 #include <stdio.h> #include <stdlib.h> typedef void funp(); static void funcall( funp* F, int args, double x) { switch( args) { case 0:
void(*f)(
指针访问中具有不同参数列表的函数的指针是否合法?下面的程序使用gcc编译时没有警告,看起来运行正常,但它是否合法
#include <stdio.h>
#include <stdlib.h>
typedef void funp();
static void funcall( funp* F, int args, double x)
{
switch( args)
{
case 0: F(); break;
case 1: F(x); break;
}
}
static void fun0( void)
{
printf( "zero\n");
}
static void fun1( double x)
{
printf( "one\t%f\n", x);
}
int main( )
{
funcall( (funp*)fun0, 0, 17.0);
funcall( (funp*)fun1, 1, 17.0);
return EXIT_SUCCESS;
}
如果
nargs
参数与函数所使用的参数数量不匹配,这将是未定义的行为,但是如果存在匹配,是否合法?在这种特殊情况下,调用是合法的
本手册第6.7.6.3p15节详细说明了如何使两种功能类型兼容(相关部分以粗体显示):
对于要兼容的两种功能类型,两者均应规定
兼容的返回类型。此外,如果需要,参数类型将列出
两者均存在,应在参数数量和
使用省略号终止符;相应参数
应具有兼容类型如果一种类型具有参数类型列表
另一种类型由函数声明器指定
不是函数定义的一部分,并且包含空
标识符列表,参数列表不应有省略号
终端和每个参数的类型应与
应用默认值产生的类型
参数升级。如果一种类型具有参数类型列表和
其他类型由包含
(可能为空)标识符列表,两者的编号应一致
参数的类型,以及每个原型参数的类型
与应用程序生成的类型兼容
将默认参数升级为
对应的标识符。(在确定类型时)
复合类型的兼容性和可用性,每个参数都已声明
带函数或数组类型被视为具有调整后的类型
并且使用限定类型声明的每个参数都被视为具有
其声明类型的非限定版本。)
因此,您有一个类型为的typedef
:
void()
void(void)
void(double)
和具有以下类型的函数:
void()
void(void)
void(double)
这两个函数定义不使用省略号(…
),因此满足第一个条件。对于第二个条件,让我们看看默认参数提升是什么。这些在第6.5.2.2p6节中规定:
如果表示被调用函数的表达式具有
不包括原型的类型,整数
对每个参数和具有类型的参数执行
float
升级为double
。这些被称为默认值
辩论升级
第一个函数没有参数,因此是兼容的。第二个函数有一个单double
参数,它与默认参数匹配,因此也是兼容的
为了给出更多示例,以下功能也将兼容:
void f1(long);
void f2(int);
但这些都不会:
void f3(float);
void f4(char);
void f5(short);
另一个答案是,您显示的代码今天是有效的C。但由于使用了没有参数列表的函数类型,这种情况在将来的任何时候都可能发生变化 6.11未来语言方向 6.11.6函数声明器 使用带空括号的函数声明符(不是 原型格式参数类型声明器)已过时 特色
过时的功能是在未来的标准版本中可能会被删除的功能。因此,如果您希望您的代码能够经得起未来的考验,最好避免使用。正如@StoryTeller中提到的,使用带空括号的函数声明器是一种过时的功能,但可以避免:
#include <stdio.h>
#include <stdlib.h>
typedef void funp(void);
static void funcall( funp* F, int args, double x)
{
switch( args)
{
case 0:
F();
break;
case 1:
{
typedef void fn(double);
((fn *)F)(x);
}
break;
}
}
static void fun0( void)
{
printf( "zero\n");
}
static void fun1( double x)
{
printf( "one\t%f\n", x);
}
int main( void )
{
funcall( (funp*)fun0, 0, 17.0);
funcall( (funp*)fun1, 1, 17.0);
return EXIT_SUCCESS;
}
#包括
#包括
typedef void funp(void);
静态void funcall(funp*F,int参数,双x)
{
开关(args)
{
案例0:
F();
打破
案例1:
{
类型定义无效fn(双);
((fn*)F)(x);
}
打破
}
}
静态void fun0(void)
{
printf(“零\n”);
}
静态空隙fun1(双x)
{
printf(“一个\t%f\n”,x);
}
内部主(空)
{
funcall((funp*)fun0,0,17.0);
funcall((funp*)fun1,1,17.0);
返回退出成功;
}
编辑:将main
的参数列表更改为void
,以符合要求
在回答问题时: “此外,参数类型列表(如果两者都存在)应在参数数量上一致”似乎意味着funp和fun1的类型不兼容。投可以吗 答案是肯定的,可以投。从C11草案: 指向一种类型函数的指针可以转换为指向另一种类型函数的指针,然后再转换回来;结果应与原始指针进行比较。如果使用转换的指针调用类型与引用类型不兼容的函数,则行为未定义
在代码中,指向
fun1
的指针在调用funcall
时已转换为不同的函数指针类型,并在funcall
中转换回原始类型,因此可用于调用fun1
,而不是对特定问题的回答,但常用的方法是只使用一个函数指针,它接受void*
参数,然后根据实际实现对其进行不同的解释。然后,该参数可以是从NULL
到指向具有十几个参数的结构的指针的任何内容。如果调用者必须知道参数的数量、类型和顺序,那么整个想法在实践中就不那么有用了。你发布的代码中的间距让我非常生气。最好从main
中删除强制转换。通常,此类强制转换可能会导致编译器禁止诊断错误的c语言