我的源文件名以cpp
结尾。
以下是2个声明:
void (*f1)(void);
void *f2(void);
我想:
f1
是一个有效的函数指针。f2
是一个返回void *
指针的函数。然后我有另一个功能:
void f3(void *p_func);
我想:
p_func
参数名称,f3
只是一个带有void *
指针的函数,即32位机器上的32位无符号整数。低于2个陈述:
f3(&f1); //<----------It works
f3(&f2); //<----------Compiler error
编译错误是:
no known conversion from 'void *(*)()' to 'void *' for 2nd argument;
我的问题:
&f1
应该是函数指针的地址,为什么它可以作为f3
传递给void *
?&f2
是void *(*)()
? void *(*)()
甚至意味着什么?答案 0 :(得分:6)
你是对的,第一个是指向函数的指针,第二个是函数声明。
这意味着编译器对警告也是正确的。
第一个调用(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 *(*)()
的意思。它是指向函数的指针的“强制转换”形式,它接受一个不确定的参数列表(但不是一个带有省略号...
的可变参数)并返回一个指向函数的指针。
你能否在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。
答案 1 :(得分:2)
如果这是c程序,则错误消息没有意义。因为在c中,每个指针都可以转换为void *
而无需转换。您的编译器似乎认为无法将指针转换为void *
c++的工作原理。
虽然您的问题的评论也是正确的,但是如果代码编译为c代码,您的代码应该在两种情况下编译。标准严格禁止这种转换,但错误信息仍然是c ++错误而不是c编译器错误,而且从/到void *
/函数指针的转换是一个常见的扩展
J.5.7函数指针强制转换
- 指向对象或void的指针可以转换为指向函数的指针,允许数据 作为函数调用(6.5.4)。
- 可以将指向函数的指针强制转换为指向对象的指针或使其无效,从而允许a 要检查或修改的功能(例如,通过调试器)(6.5.4)。
醇>
虽然并不能保证所有系统都支持这一点,但大多数系统都支持这种功能。
作为c++进行编辑的可能原因是
答案 2 :(得分:0)
- &amp; f1应该是函数指针的地址,为什么它可以作为void *传递给f3?
这是正确的,这是完全为什么它成功。因为指向指针的指针可以隐式转换为java.util.Properties p = new Properties();
p.load(new FileInputStream(new File("my_config_file.txt")));
String driverName = (String)p.get("drivername");
,而不管其目标指向哪种数据类型。
- 为什么&amp; f2是
void *
?void *(*)()
甚至意味着什么?
您可能误解了函数指针表示法。 void *(*)()
是函数指针的表示法。 void *(*)()
是一个返回void指针的函数。 void *f2(void)
是一个函数指针(到该函数)。编译器告诉你它不能隐式地将函数指针转换为&f2
(根据C标准,这是不可保证的,并且你的编译器显然无法做到这一点)。 / p>
那是独家新闻。