关于C ++函数指针

时间:2016-08-25 05:29:49

标签: c++ function-pointers

我的源文件名以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 *
  • 为什么&f2void *(*)()
  • void *(*)()甚至意味着什么?

3 个答案:

答案 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 *(*)()的意思。它是指向函数的指针的“强制转换”形式,它接受一个不确定的参数列表(但不是一个带有省略号...的可变参数)并返回一个指向函数的指针。

haccks asked

  

你能否在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.ccpf19.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。

答案 1 :(得分:2)

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

虽然您的问题的评论也是正确的,但是如果代码编译为代码,您的代码应该在两种情况下编译。标准严格禁止这种转换,但错误信息仍然是c ++错误而不是c编译器错误,而且从/到void * /函数指针的转换是一个常见的扩展

  

J.5.7函数指针强制转换

     
      
  1. 指向对象或void的指针可以转换为指向函数的指针,允许数据   作为函数调用(6.5.4)。
  2.   
  3. 可以将指向函数的指针强制转换为指向对象的指针或使其无效,从而允许a   要检查或修改的功能(例如,通过调试器)(6.5.4)。
  4.   

虽然并不能保证所有系统都支持这一点,但大多数系统都支持这种功能。

作为进行编辑的可能原因是

  1. 您直接调用了c ++编译器,例如g++
  2. 您使用 .cpp 扩展名为源文件命名,而不是 .c
  3. 如果是源代码,则错误有意义,因为不允许此类转换,但在c中这可能正确编译(并且大部分时间 )即使它是严格禁止的。

    另请注意,中的函数指针不是执行AFAIK操作的常用方法。因为当你有一个对象时,你只能创建指向静态成员的指针,这与指向普通函数的指针没什么不同。在中,回调并不常见,实际上我已广泛使用的库Qt需要(在c ++ 11标准之前)一个名为 moc 以使“回调”工作。该工具从头文件生成代码以允许回调或(模拟它们)在中工作。

答案 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>

那是独家新闻。