关于C++函数指针的困惑

关于C++函数指针的困惑,c++,function-pointers,c,C++,Function Pointers,C,我的源文件名以cpp结尾 以下是两项声明: void (*f1)(void); void *f2(void); 我认为: f1是有效的函数指针。 f2是一个返回void*指针的函数。 然后我有另一个功能: void f3(void *p_func); 我认为: 尽管有p_func参数名,f3只是一个接受void*指针的函数,即32位机器上的32位无符号整数。 及以下两项声明: f3(&f1); //<----------It works f3(&f2); //<

我的源文件名以cpp结尾

以下是两项声明:

void (*f1)(void);

void *f2(void);
我认为:

f1是有效的函数指针。 f2是一个返回void*指针的函数。 然后我有另一个功能:

void f3(void *p_func);
我认为:

尽管有p_func参数名,f3只是一个接受void*指针的函数,即32位机器上的32位无符号整数。 及以下两项声明:

f3(&f1); //<----------It works
f3(&f2); //<----------Compiler error
我的问题是:

&f1应该是函数指针的地址,为什么它可以作为void*传递给f3? 为什么&f2是void**? 空**到底是什么意思?
如果这是一个程序,则错误消息没有意义。因为每个指针都可以转换为void*和back,而不需要强制转换。您的编译器似乎认为无法将指针转换为void*,这就是工作原理

虽然对您的问题的评论也是正确的,但您的代码在这两种情况下都应该编译,只要它编译为代码。标准严格禁止这种转换,但错误消息仍然是C++错误,而不是C编译器错误,并且从//空函数指针转换为通用扩展< /P> J.5.7函数指针强制转换

指向对象或void的指针可以强制转换为指向函数的指针,从而允许数据 作为功能6.5.4调用。 指向函数的指针可以强制转换为指向对象或void的指针,从而允许 例如,由调试器6.5.4检查或修改的函数。 虽然不能保证所有系统都支持这一点,但大多数系统都支持

可能需要编译的原因如下

您直接调用C++编译器,例如G++ 您将源文件的扩展名命名为.cpp,而不是.c 如果它是源代码,这个错误是有意义的,因为它不允许这样的转换,但是在c语言中,这可能会正确编译,并且在大多数情况下会正确编译,即使它是严格禁止的


还要注意的是,中的函数指针并不是完成任务的常用方法。因为当您有一个对象时,您只能创建指向静态成员的指针,这与使用指向普通函数的指针没有太大区别。实际上,回调并不常见,在c++11标准之前,我已经广泛使用了一个库,它需要一个名为moc的工具,以使回调正常工作。该工具从头文件生成代码,以允许回调或模拟它们工作。

如果这是一个程序,则错误消息没有意义。因为每个指针都可以转换为void*和back,而不需要强制转换。您的编译器似乎认为无法将指针转换为void*,这就是工作原理

虽然对您的问题的评论也是正确的,但您的代码在这两种情况下都应该编译,只要它编译为代码。标准严格禁止这种转换,但错误消息仍然是C++错误,而不是C编译器错误,并且从//空函数指针转换为通用扩展< /P> J.5.7函数指针强制转换

指向对象或void的指针可以强制转换为指向函数的指针,从而允许数据 作为功能6.5.4调用。 指向函数的指针可以强制转换为指向对象或void的指针,从而允许 例如,由调试器6.5.4检查或修改的函数。 虽然不能保证所有系统都支持这一点,但大多数系统都支持

可能需要编译的原因如下

您直接调用C++编译器,例如G++ 您将源文件的扩展名命名为.cpp,而不是.c 如果它是源代码,这个错误是有意义的,因为它不允许这样的转换,但是在c语言中,这可能会正确编译,并且在大多数情况下会正确编译,即使它是严格禁止的


还要注意的是,中的函数指针并不是完成任务的常用方法。因为当您有一个对象时,您只能创建指向静态成员的指针,这与使用指向普通函数的指针没有太大区别。实际上,回调并不常见,在c++11标准之前,我已经广泛使用了一个库,它需要一个名为moc的工具,以使回调正常工作。该工具从头文件生成代码,以允许回调或模拟回调。第一个是指向函数的指针,第二个是函数声明,这是对的

这意味着编译器对于警告也是正确的

第一个调用f3&f1传递函数指针的地址,该地址可转换为void*,与我前面的注释相反。因此,不需要任何错误。它是指向函数指针的指针,因此是数据对象,而不是函数指针。省略&将得到与第二次调用相同的错误

第二个调用f3和f2传递一个指向函数的指针,函数指针和空指针不可相互转换。傅前 NCION的名称是多余的,在上下文中有点误导。您可以在函数名中添加任意数量的*,或单个*,或将它们全部省略,并且它们被视为相同的-这是标准C中更奇怪的方面之一。另请参见

我注意到,我不得不使用-pedantic来让GCC抱怨它。这是附录J中记录通用扩展的标准的结果:

J.5.7函数指针强制转换 ^1指向对象或void的指针可以强制转换为指向函数的指针,从而允许数据 作为功能6.5.4调用

^2指向函数的指针可以强制转换为指向对象或void的指针,从而允许 例如,由调试器6.5.4检查或修改的函数

你问void**是什么意思。它是指向函数的指针的“强制转换”形式,该函数采用不确定的参数列表,但不是带省略号的可变参数列表。。。并返回指向函数的指针

:


能否请您添加一个C标准中关于函数指针和空指针不可相互转换的参考

是的-这很简单:

6.2.5类型 ^1…类型分为描述对象的对象类型和描述函数的函数类型

6.3转换 6.3.2.3指针 ^1指向void的指针可以转换为指向任何对象类型的指针,也可以转换为指向任何对象类型的指针。指向任何对象类型的指针可以转换为指向void的指针,然后再返回;结果应与原始指针进行比较

^7指向对象类型的指针可以转换为指向不同对象类型的指针。如果 结果指针未与引用类型正确对齐68,行为为 未定义。否则,当再次转换回时,结果应与 原始指针。当指向对象的指针转换为指向字符类型的指针时, 结果指向对象的最低寻址字节。连续增量 结果,直到对象的大小,都会产生指向对象剩余字节的指针

^8指向一种类型函数的指针可以转换为指向另一种类型函数的指针 再次输入并返回;结果应与原始指针进行比较。如果一个 指针用于调用类型与引用类型不兼容的函数, 该行为未定义

这些是指针类型之间唯一定义的转换。在指向对象的指针和指向函数的指针之间没有任何转换,因此这是不允许的,但不一定需要诊断;它不在标准的“约束”部分

测试代码 变体1 pf19.c:

编译警告错误,因为-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: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
$
<> P>与C++编译器相比,消息的措辞不同于C编译器,但其意图是相同的PF17CC是PF19.C:

的简单副本。 测试:Mac OS X 10.11.6 El Capitan上的GCC 6.2.0


由于§6.3.2.3¨1是相关的,附录J.5.7。

您是对的,第一个是指向函数的指针,第二个是函数声明

这意味着编译器对于警告也是正确的

第一个调用f3&f1传递函数指针的地址,该地址可转换为void*,与我前面的注释相反。因此,不需要任何错误。它是指向函数指针的指针,因此是数据对象,而不是函数指针。省略&将得到与第二次调用相同的错误

第二个调用f3和f2传递一个指向函数的指针,函数指针和空指针不可相互转换。函数名前面的&是多余的,在上下文中有点误导。您可以在函数名中添加任意数量的*,或单个*,或将它们全部省略,并且它们被视为相同的-这是标准C中更奇怪的方面之一。另请参见

我注意到,我不得不使用-pedantic来让GCC抱怨它。这是附录J中记录通用扩展的标准的结果:

J.5.7函数指针强制转换 ^1指向对象或void的指针可以强制转换为指向函数的指针,从而允许数据 作为功能6.5.4调用

^2指向函数的指针可以强制转换为指向对象或void的指针,从而允许 例如,由调试器6.5.4检查或修改的函数

你问void**是什么意思。它是指向函数的指针的“强制转换”形式,该函数采用不确定的参数列表,但不是带省略号的可变参数列表。。。并返回指向函数的指针

:


能否请您添加一个C标准中关于函数指针和空指针不可相互转换的参考

是的-这很简单:

6.2.5类型 ^1…类型为partit 分为描述对象的对象类型和描述函数的函数类型

6.3转换 6.3.2.3指针 ^1指向void的指针可以转换为指向任何对象类型的指针,也可以转换为指向任何对象类型的指针。指向任何对象类型的指针可以转换为指向void的指针,然后再返回;结果应与原始指针进行比较

^7指向对象类型的指针可以转换为指向不同对象类型的指针。如果 结果指针未与引用类型正确对齐68,行为为 未定义。否则,当再次转换回时,结果应与 原始指针。当指向对象的指针转换为指向字符类型的指针时, 结果指向对象的最低寻址字节。连续增量 结果,直到对象的大小,都会产生指向对象剩余字节的指针

^8指向一种类型函数的指针可以转换为指向另一种类型函数的指针 再次输入并返回;结果应与原始指针进行比较。如果一个 指针用于调用类型与引用类型不兼容的函数, 该行为未定义

这些是指针类型之间唯一定义的转换。在指向对象的指针和指向函数的指针之间没有任何转换,因此这是不允许的,但不一定需要诊断;它不在标准的“约束”部分

测试代码 变体1 pf19.c:

编译警告错误,因为-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: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
$
<> P>与C++编译器相比,消息的措辞不同于C编译器,但其意图是相同的PF17CC是PF19.C:

的简单副本。 测试:Mac OS X 10.11.6 El Capitan上的GCC 6.2.0

由于§6.3.2.3∗1与附录J.5.7相关

&f1应该是函数指针的地址,为什么它可以作为void*传递给f3? 这是正确的,这正是它成功的原因。因为指向指针的指针可以隐式转换为void*,而不管其目标指向什么数据类型

为什么&f2是void**?空**到底是什么意思? 您可能误解了函数指针表示法。void**是函数指针的符号。void*f2void是一个返回void指针的函数&f2是指向该函数的函数指针。编译器告诉您,根据C标准,它不能隐式地将函数指针转换为void*,这是不能保证的,而且您的编译器显然无法做到这一点

这就是独家新闻

&f1应该是函数指针的地址,为什么它可以作为void*传递给f3? 这是正确的,这正是它成功的原因。因为指向指针的指针可以隐式转换为void*,而不管其目标指向什么数据类型

为什么&f2是void**?空**到底是什么意思? 您可能误解了函数指针表示法。void**是函数指针的符号。void*f2void是一个返回void指针的函数&f2是指向该函数的函数指针。编译器告诉您,根据C标准,它不能隐式地将函数指针转换为void*,这是不能保证的,而且您的编译器显然无法做到这一点


这就是独家新闻。

您是否100%确定您没有编写代码而不是?&f1不是函数指针,只是指向一个对象的指针它指向的对象就是函数指针。。。所以它可以转换为void*很好&另一方面,f2是一个函数指针。。。它们不一定与Value**兼容。C++比C. Types更强大类型,它们可以在C中彼此隐式转换,而不是C++中的。一个突出的例子是void*:在C中,它是一个通用指针,除了指向函数的指针之外,所有其他指针都可以转换为void*,也可以从void*。在C++中,没有显式转换是不可能的。每个数据指针都可以隐式转换为空隙*。函数不是数据,而是指向函数的指针。所以F1的地址是指向数据的指针,F2的地址不是。你正在用C++编译器编译。我重新回答了这个问题。知道你在写什么语言是很重要的。C和C++是不一样的。你确定100%不是写代码吗?而不是& & F1不是函数指针,只是指向对象的指针,它指向的对象是函数指针…所以它可以转换为void*很好&另一方面,f2是一个函数指针。。。它们不一定与Value**兼容。C++比C. Types更强大类型,它们可以在C中彼此隐式转换,而不是C++中的。一个突出的例子是void*:在C中,它是一个通用指针和所有其他点
除了指向函数的指针之外,其他指针都可以转换为函数,也可以从函数转换为函数。在C++中,没有显式转换是不可能的。每个数据指针都可以隐式转换为空隙*。函数不是数据,而是指向函数的指针。所以F1的地址是指向数据的指针,F2的地址不是。你正在用C++编译器编译。我重新回答了这个问题。知道你在写什么语言是很重要的。C和C++是不一样的。你能不能从C标准中添加一个关于函数指针的引用,空洞指针不是可以相互转换的吗?谢谢参考。1似乎也是相关的:指向void的指针可以转换为指向任何对象类型的指针,也可以转换为指向任何对象类型的指针。指向任何对象类型的指针可以转换为指向void的指针,然后再返回;结果应与原始指针进行比较。3.15将对象定义为执行环境中的数据存储区域,其内容可以表示值。。。这表明函数不被视为对象。@Dmitri:函数与标准条款中的对象完全不同,因此有关于“指向对象类型的指针”和关于“指向函数的指针”的单独段落。§6.2.5说类型被划分为描述对象的对象类型和描述函数的函数类型。为了清楚起见,我同意你的答案。。。我认为标准的其他部分也有助于支持它。你能不能从C标准中添加一个关于函数指针和空洞指针的引用是不可转换的?谢谢参考。1似乎也是相关的:指向void的指针可以转换为指向任何对象类型的指针,也可以转换为指向任何对象类型的指针。指向任何对象类型的指针可以转换为指向void的指针,然后再返回;结果应与原始指针进行比较。3.15将对象定义为执行环境中的数据存储区域,其内容可以表示值。。。这表明函数不被视为对象。@Dmitri:函数与标准条款中的对象完全不同,因此有关于“指向对象类型的指针”和关于“指向函数的指针”的单独段落。§6.2.5说类型被划分为描述对象的对象类型和描述函数的函数类型。为了清楚起见,我同意你的答案。。。我只是认为标准的其他部分也有助于支持它。C只保证指向对象的指针可以转换为void*,这不包括函数指针。。。尽管在函数指针和对象指针之间进行转换的能力被认为是一种常见的扩展。感谢J.5.7 reference.C,它只保证指向对象的指针可以转换为void*,而void*-不包括函数指针。。。尽管在函数指针和对象指针之间进行转换的能力被认为是一个常见的扩展。感谢J.5.7参考。
$ 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
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
$
$ 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);
      ^~
$