C++函数指针的困惑
Confusion about C++ function pointer
我的源文件名以 cpp
结尾。
以下是 2 个声明:
void (*f1)(void);
void *f2(void);
我认为:
f1
是一个有效的函数指针。
f2
是 returns 一个 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
as void *
?
- 为什么
&f2
是 void *(*)()
?
void *(*)()
是什么意思?
如果这是 c program. Because in c every pointer is convertible to void *
and back without the need for a cast. Your compiler seems to think that there is no way to convert from a pointer to void *
which is how c++ 作品,则错误消息没有意义。
虽然对您的问题的评论也是正确的,但如果您的代码被编译为 c 代码,那么您的代码在这两种情况下都应该可以编译。标准严格禁止这种转换,但错误信息仍然是 c++ 错误而不是 c 编译器错误,而且转换 from/to void *
/function pointers is a common extension
J.5.7 Function pointer casts
- A pointer to an object or to void may be cast to a pointer to a function, allowing data to
be invoked as a function (6.5.4).
- A pointer to a function may be cast to a pointer to an object or to void, allowing a
function to be inspected or modified (for example, by a debugger) (6.5.4).
尽管不能保证所有系统都支持此功能,但大多数系统都支持。
编译为 c++ 的可能原因是
- 你直接调用了c++编译器,例如
g++
- 您将源文件命名为 .cpp 扩展名而不是 .c
如果它是 c++ 源代码,错误是有道理的,因为 c++ 不允许这样的转换,但在 c 中这可能会正确编译( 并且大多数时候会) 尽管它是被严格禁止的。
另请注意,c++ 中的函数指针并不是通常的 AFAIK 处理方式。因为当你有一个对象时,你只能创建指向静态成员的指针,这与拥有一个指向普通函数的指针没有太大区别。在 c++ 中,回调并不常见,事实上,我广泛使用的库 Qt 需要(在 c++11 标准之前) 一个名为 moc 的工具,以便使“callbacks”工作。该工具从头文件生成代码以允许回调或(它们的模拟)在c++.
中工作
你说得对,第一个是指向函数的指针,第二个是函数声明。
这意味着编译器对警告的判断也是正确的。
第一次调用(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 Function pointer casts
¶1 A pointer to an object or to void
may be cast to a pointer to a function, allowing data to
be invoked as a function (6.5.4).
¶2 A pointer to a function may be cast to a pointer to an object or to void
, allowing a
function to be inspected or modified (for example, by a debugger) (6.5.4).
你问void *(*)()
是什么意思。它是指向函数的指针的 'cast' 形式,该函数采用不确定的参数列表(但不是末尾带有省略号 ...
的可变参数列表)和指向函数的指针 returns 。
haccks :
Could you please add a reference from C standard about function pointers and void pointers are not inter-convertible?
是的 — 这很简单:
6.2.5 Types
¶1 … Types are partitioned into object types (types that describe objects) and function types (types that describe functions).
6.3 Conversions
6.3.2.3 Pointers
¶1 A pointer to void
may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer to void
and back again; the result shall compare equal to the original pointer.
¶7 A pointer to an object type may be converted to a pointer to a different object type. If the
resulting pointer is not correctly aligned68) for the referenced type, the behavior is
undefined. Otherwise, when converted back again, the result shall compare equal to the
original pointer. When a pointer to an object is converted to a pointer to a character type,
the result points to the lowest addressed byte of the object. Successive increments of the
result, up to the size of the object, yield pointers to the remaining bytes of the object.
¶8 A pointer to a function of one type may be converted to a pointer to a function of another
type and back again; the result shall compare equal to the original pointer. If a converted
pointer is used to call a function whose type is not compatible with the referenced type,
the behavior is undefined.
这些是指针类型之间唯一定义的转换。在指向对象的指针和指向函数的指针之间没有任何转换,所以它是不允许的(但不一定需要诊断;它不在标准的 'constraints' 部分)。
测试代码
变体 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);
^~
$
测试:GCC 6.2.0 Mac OS X 10.11.6 El Capitan。
感谢 Dmitri for §6.3.2.3 ¶1 是相关的,附件 J.5.7.
- &f1 should be the address of a function pointer, why is it can be passed to f3 as void *?
这是正确的,这正是 它成功的原因。因为指向指针的指针可以隐式转换为 void *
,而不管它的目标指向什么数据类型。
- Why &f2 is
void *(*)()
? And what does void *(*)()
even mean?
您可能误解了函数指针表示法。 void *(*)()
是函数指针的表示法。 void *f2(void)
是一个 returns 空指针的函数。 &f2
是一个函数指针(指向那个函数)。编译器告诉您它不能将函数指针隐式转换为 void *
(根据 C 标准,这不能保证,您的编译器显然无法做到这一点)。
这就是独家新闻。
我的源文件名以 cpp
结尾。
以下是 2 个声明:
void (*f1)(void);
void *f2(void);
我认为:
f1
是一个有效的函数指针。f2
是 returns 一个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
asvoid *
?- 为什么
&f2
是void *(*)()
? void *(*)()
是什么意思?
如果这是 c program. Because in c every pointer is convertible to void *
and back without the need for a cast. Your compiler seems to think that there is no way to convert from a pointer to void *
which is how c++ 作品,则错误消息没有意义。
虽然对您的问题的评论也是正确的,但如果您的代码被编译为 c 代码,那么您的代码在这两种情况下都应该可以编译。标准严格禁止这种转换,但错误信息仍然是 c++ 错误而不是 c 编译器错误,而且转换 from/to void *
/function pointers is a common extension
J.5.7 Function pointer casts
- A pointer to an object or to void may be cast to a pointer to a function, allowing data to be invoked as a function (6.5.4).
- A pointer to a function may be cast to a pointer to an object or to void, allowing a function to be inspected or modified (for example, by a debugger) (6.5.4).
尽管不能保证所有系统都支持此功能,但大多数系统都支持。
编译为 c++ 的可能原因是
- 你直接调用了c++编译器,例如
g++
- 您将源文件命名为 .cpp 扩展名而不是 .c
如果它是 c++ 源代码,错误是有道理的,因为 c++ 不允许这样的转换,但在 c 中这可能会正确编译( 并且大多数时候会) 尽管它是被严格禁止的。
另请注意,c++ 中的函数指针并不是通常的 AFAIK 处理方式。因为当你有一个对象时,你只能创建指向静态成员的指针,这与拥有一个指向普通函数的指针没有太大区别。在 c++ 中,回调并不常见,事实上,我广泛使用的库 Qt 需要(在 c++11 标准之前) 一个名为 moc 的工具,以便使“callbacks”工作。该工具从头文件生成代码以允许回调或(它们的模拟)在c++.
中工作你说得对,第一个是指向函数的指针,第二个是函数声明。
这意味着编译器对警告的判断也是正确的。
第一次调用(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 Function pointer casts
¶1 A pointer to an object or to
void
may be cast to a pointer to a function, allowing data to be invoked as a function (6.5.4).¶2 A pointer to a function may be cast to a pointer to an object or to
void
, allowing a function to be inspected or modified (for example, by a debugger) (6.5.4).
你问void *(*)()
是什么意思。它是指向函数的指针的 'cast' 形式,该函数采用不确定的参数列表(但不是末尾带有省略号 ...
的可变参数列表)和指向函数的指针 returns 。
haccks
Could you please add a reference from C standard about function pointers and void pointers are not inter-convertible?
是的 — 这很简单:
6.2.5 Types
¶1 … Types are partitioned into object types (types that describe objects) and function types (types that describe functions).
6.3 Conversions
6.3.2.3 Pointers
¶1 A pointer to
void
may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer tovoid
and back again; the result shall compare equal to the original pointer.¶7 A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned68) for the referenced type, the behavior is undefined. Otherwise, when converted back again, the result shall compare equal to the original pointer. When a pointer to an object is converted to a pointer to a character type, the result points to the lowest addressed byte of the object. Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object.
¶8 A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer. If a converted pointer is used to call a function whose type is not compatible with the referenced type, the behavior is undefined.
这些是指针类型之间唯一定义的转换。在指向对象的指针和指向函数的指针之间没有任何转换,所以它是不允许的(但不一定需要诊断;它不在标准的 'constraints' 部分)。
测试代码
变体 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);
^~
$
测试:GCC 6.2.0 Mac OS X 10.11.6 El Capitan。
感谢 Dmitri for
- &f1 should be the address of a function pointer, why is it can be passed to f3 as void *?
这是正确的,这正是 它成功的原因。因为指向指针的指针可以隐式转换为 void *
,而不管它的目标指向什么数据类型。
- Why &f2 is
void *(*)()
? And what doesvoid *(*)()
even mean?
您可能误解了函数指针表示法。 void *(*)()
是函数指针的表示法。 void *f2(void)
是一个 returns 空指针的函数。 &f2
是一个函数指针(指向那个函数)。编译器告诉您它不能将函数指针隐式转换为 void *
(根据 C 标准,这不能保证,您的编译器显然无法做到这一点)。
这就是独家新闻。