为什么这个函数指针在没有警告或错误的情况下工作?

时间:2015-02-02 16:47:53

标签: c function-pointers

知道这个电话:

pow(4);

将生成此错误消息:

 error: too few arguments to function ‘pow’

我正在学习函数的指针,当看到下面的代码工作时我感到很惊讶。但为什么呢?

#include<stdio.h>
#include<math.h>

void aux(double (*function)(), double n, double x);

int main(void)
{
    aux(pow, 4, 2); 

    aux(sqrt, 4, 0);

    return 0;
}

void aux(double (*function)(double), double n, double x)
{   
    if(x == 0)
        printf("\nsqrt(%.2f, %.2f): %f\n", n, x, (*function)(n));  
    else
        printf("\npow(%.2f, %.2f): %f\n", n, x, (*function)(n));  
}

我编译使用:

gcc -Wall -Wextra -pedantic -Wconversion -o test test.c -lm

结果是:

pow(4.00, 2.00): 16.000000

sqrt(4.00, 0.00): 2.000000

如果我将第一次调用aux的第三个参数更改为3,则结果将更改为:

pow(4.00, 3.00): 64.000000

sqrt(4.00, 0.00): 2.000000

还有一个问题。在这种情况下,声明和使用函数指针的正确方法是什么?

4 个答案:

答案 0 :(得分:37)

此:

void aux(double (*function)(), double n, double x);

function使用旧式非原型声明。空括号()表示该函数采用固定但未指定的数字和类型的参数。

C仍然允许这种声明以实现向后兼容。 ANSI C在1989年引入了原型(指定参数类型的函数声明)。在此之前,无法在函数声明中指定参数类型,并且编译器无法检查调用是否传递了正确的数字和论证的类型。

此类声明是“过时的”,这意味着可以从未来的C标准中删除对它们的支持(但是在20多年的时间里,委员会还没有将其删除)。调用具有错误数量的参数类型的函数不一定会由编译器诊断,并且行为未定义。

当一个原型而另一个没有原型时,函数类型兼容性的规则有点复杂。这些类型:

double(double)         /* function with one double parameter
                          returning double */
double(double, double) /* function with two double parameters
                          returning double */

彼此不兼容,但它们与此类型兼容:

double()   /* function with a fixed but unspecified number of parameters
              returning double */

这使得在没有编译器诊断的情况下进行错误调用成为可能。

为避免此问题,始终使用原型

void aux(double (*function)(double, double), double n, double x);

您不仅可以从编译器获得更好的诊断功能,而且不必担心非原型函数的复杂兼容性规则(如果您好奇,则会在N1570 6.7中指定。 6.3第16段。

答案 1 :(得分:9)

您的函数原型aux() ...

void aux(double (*function)(), double n, double x);

...指定第一个参数是指向返回double并接受未指定参数的函数的指针。这可以防止GCC在main()中针对该函数的调用发出有关不匹配类型的警告。

但是,函数aux()定义为其第一个参数提供了更具体的类型,该参数与您传递的实际参数不兼容。通过指针调用这些函数具有未定义的语义。几乎任何事情都可能发生,包括行为似乎是你想要的。你不能依赖任何有关这种行为的事情。

答案 2 :(得分:8)

因为您在main之前指定了aux()的原型,而function没有任何指定的参数类型。了解差异:

void f();   /* Accepts any number of arguments thanks to K&R C */
void g(void); /* No arguments accepted */
void h(int); /* Only one integer argument accepted */

如果您将aux()原型声明为:

void aux(double (*function)(double), double n, double x);
海湾合作委员会开始抱怨。

答案 3 :(得分:5)

一对空括号()告诉C编译器可以使用任何数量的参数调用该函数,但该函数本身具有特定数量的参数用于其原型。因此,如果将它与函数指针一起使用,并且传递的参数的数量和相应类型与指向函数的原型匹配,则一切都将起作用。如果你饿了参数列表或者使用不同类型的值,那么......那是未定义的行为。