没错,第一个是指向函数的指针,第二个是函数声明。
这意味着编译器对警告也是正确的。
第一次调用 (f3(&f1)) 传递函数指针的地址,该地址可转换为 void *(与我之前的评论相反)。所以,不需要错误。 (它是指向函数指针的指针,因此是数据对象,而不是函数指针。省略 & 会得到与第二次调用相同的错误。)
第二次调用 (f3(&f2)) 传递一个指向函数的指针,函数指针和 void 指针不可相互转换。函数名前面的& 是多余的——在上下文中会产生轻微的误导。您可以从函数名称中添加任意数量的*,或单个&(或将它们全部省略),它们的处理方式相同——标准C 的一个更奇怪的方面。(另见Why do function pointer definitions work with any number of ampersands '&' or asterisks '*'?)
我注意到我必须使用-pedantic 来让 GCC 抱怨它。这是标准在附录 J 中记录通用扩展的结果:
J.5.7 函数指针转换
¶1 指向对象或void 的指针可以转换为指向函数的指针,从而允许数据
作为函数调用 (6.5.4)。
¶2 指向函数的指针可以转换为指向对象的指针或void,允许
要检查或修改的函数(例如,由调试器)(6.5.4)。
你问void *(*)() 是什么意思。它是一个指向函数的指针的“强制转换”形式,它接受一个不确定的参数列表(但不是一个带有省略号 ... 的可变参数列表)并返回一个指向函数的指针。
haccksasked:
能否请您从 C 标准中添加关于函数指针和 void 指针不可相互转换的参考?
是的——这很简单:
6.2.5 类型
¶1 … 类型分为对象类型(描述对象的类型)和函数类型(描述函数的类型)。
6.3 转化
6.3.2.3 指针
¶1 指向void 的指针可以转换为或从指向任何对象类型的指针转换。指向任何对象类型的指针可以转换为指向void 的指针并再次返回;结果应与原始指针比较。
¶7 指向对象类型的指针可以转换为指向不同对象类型的指针。如果
结果指针未正确对齐68) 对于引用的类型,行为是
不明确的。否则,当再次转换回来时,结果将等于
原始指针。当指向对象的指针转换为指向字符类型的指针时,
结果指向对象的最低寻址字节。的连续递增
结果,直到对象的大小,产生指向对象剩余字节的指针。
¶8 指向一种函数的指针可以转换为指向另一种函数的指针
键入并再次返回;结果应与原始指针比较。如果一个转换
指针用于调用类型与引用类型不兼容的函数,
行为未定义。
这些是指针类型之间唯一定义的转换。没有关于在指向对象的指针和指向函数的指针之间进行转换的内容,因此不允许这样做(但不一定需要诊断;它不在标准的“约束”部分)。
测试代码
变体 1 (pf19.c):
void (*f1)(void);
void *f2(void);
void f3(void *p_func);
int main(void)
{
f3(&f1);
f3(&f2);
}
编译警告(由于-Werror 和-pedantic 导致的错误):
$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -pedantic -c pf19.c
pf19.c: In function ‘main’:
pf19.c:9:12: error: ISO C forbids passing argument 1 of ‘f3’ between function pointer and ‘void *’ [-Werror=pedantic]
f3(&f2); //<----------Compiler error
^
pf19.c:4:6: note: expected ‘void *’ but argument is of type ‘void * (*)(void)’
void f3(void *p_func);
^~
cc1: all warnings being treated as errors
变体 2(也是 pf19.c):
void (*f1)(void);
void *f2(void);
void f3(void *p_func);
int main(void)
{
f3(f1);
f3(&f2);
}
编译消息:
$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -pedantic -c pf19.c
pf19.c: In function ‘main’:
pf19.c:8:8: error: ISO C forbids passing argument 1 of ‘f3’ between function pointer and ‘void *’ [-Werror=pedantic]
f3(f1);
^~
pf19.c:4:6: note: expected ‘void *’ but argument is of type ‘void (*)(void)’
void f3(void *p_func);
^~
pf19.c:9:8: error: ISO C forbids passing argument 1 of ‘f3’ between function pointer and ‘void *’ [-Werror=pedantic]
f3(&f2);
^
pf19.c:4:6: note: expected ‘void *’ but argument is of type ‘void * (*)(void)’
void f3(void *p_func);
^~
cc1: all warnings being treated as errors
$
消息的措辞与 C 编译器相比 C++ 编译器不同,但意图相同(pf17.cc 是pf19.c 的简单副本):
$ g++ -O3 -g -I./inc -std=c++11 -Wall -Wextra -Werror -c pf17.cc
pf17.cc: In function ‘int main()’:
pf17.cc:8:10: error: invalid conversion from ‘void (*)()’ to ‘void*’ [-fpermissive]
f3(f1);
^
pf17.cc:4:6: note: initializing argument 1 of ‘void f3(void*)’
void f3(void *p_func);
^~
pf17.cc:9:8: error: invalid conversion from ‘void* (*)()’ to ‘void*’ [-fpermissive]
f3(&f2);
^~~
pf17.cc:4:6: note: initializing argument 1 of ‘void f3(void*)’
void f3(void *p_func);
^~
$
测试:Mac OS X 10.11.6 El Capitan 上的 GCC 6.2.0。
感谢Dmitri noting §6.3.2.3 ¶1 是相关的,以及附件 J.5.7。