为什么指向函数的指针不强制执行该方法的签名

时间:2013-12-12 18:43:50

标签: c

我正在尝试在objective-c中获取块,并且正在学习指向函数的指针,因此这可能是一个非常明显的问题。我在Xcode 5中编译了以下内容并具有以下内容:

void hearThis(char * (*pFunc)(char *myString)){
 /*
  why does it allow me to do this? when signature for 
   helpMe doesn't take an argument
  */
    char *str=(*pFunc)("what I want as arg"); 
    printf("you are going to hear this: %s\n", str);
}


char * helpMe(){
  return "this is from helpMe";
}

int main(){
    char * (*fNewFunc)();
    fNewFunc=&helpMe;
    // we are passing a pointer to a function
    hearThis(fNewFunc);

输出:

you are going to hear this: this is from helpMe

为什么它允许我甚至在将参数传递给不带一个OR的函数时进行编译?运行时不会崩溃?

2 个答案:

答案 0 :(得分:3)

不带参数的函数声明的C语法是f(void)。你的fNewFunc是一个带有未指明参数的函数。用

替换其声明
char * (*fNewFunc)(void);

然后C可以至少警告你不匹配。 C ++会拒绝它。

编辑:正如chux在评论中指出的那样,char *helpMe()定义,因为它是一个定义,确实声明helpMe没有参数,所以没有严格的需要改变它。

答案 1 :(得分:2)

好的,我已经检查了 c99 标准,可以说这是未定义的行为


TL; DR:可以将一个函数指针转换/转换为另一个类型,但是如果调用该函数,函数类型必须兼容,否则,你得到 undefined行为即可。您没有收到警告,因为在每个步骤中您要在兼容的函数类型之间进行转换,但最终helpMepFunc不兼容。


首先:

char *helpMe(){
  return "this is from helpMe";
}

有效且在C99中与:

相同
char *helpMe(void){
  return "this is from helpMe";
}

来自§6.7.5.3(强调我的):

  

标识符列表仅声明参数的标识符   功能。 函数声明符中的空列表,它是a的一部分   该函数的定义表明该函数没有   参数即可。函数声明符中的空列表不是其中的一部分   该函数的定义指明没有关于该函数的信息   提供参数的数量或类型。

我不知道这在C89中的表现如何,我认为可能在C89中有效。

另一方面,我们有:

char * (*fNewFunc)() = helpMe;

此强制转换是有效的,因为您可以将函数指针强制转换为另一种类型的函数,并且可以毫无问题地返回。编译器不会发出警告,因为即使它们的类型兼容! (对未指定的情况无效)。

§6.3.2.3一节,第8段内容(再次强调我的):

  

指向一种类型的函数的指针可以转换为指向a的指针   另一种类型的功能又回来了;结果应该比较   等于原始指针。 如果使用转换的指针进行调用   一个类型与指向类型不兼容的函数   行为未定义。

因此,当您将指针传递给hearThis时,它再次被转换,从char *(*)();转换为char *(*)(char *);,再次,它们与编译器兼容(因此,不会发出警告)。

但是标准说如果您调用的函数类型与指向的函数类型不兼容,则行为未定义

现在问题是,它们是否兼容?

答案是。如果您将fNewFunc()的类型更改为char *(*fNewFunc)(void);,则会收到警告:

p.c: In function ‘main’:
p.c:12:5: warning: passing argument 1 of ‘hearThis’ from incompatible pointer type [enabled by default]
p.c:6:6: note: expected ‘char * (*)(char *)’ but argument is of type ‘char * (*)(void)’

在标准中还说两种功能类型兼容。它在§6.7.5.3.15中。它有点复杂:P

为什么会起作用?

可能它起作用,因为计算机不关心。它只是将参数作为寄存器推送或传递,而helpMe只是忽略它们。但是你应该知道它是未定义的行为