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

Why does this pointer to function work without warnings or errors?

知道这个调用:

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

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

这个:

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

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

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

这样的声明是 "obsolescent",这意味着可以从未来的 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 段).

一对空括号 () 告诉 C 编译器可以使用 任意 个参数调用函数,但函数本身有一个特定的数量其原型的参数。因此,如果将它与函数指针一起使用,并且传递的参数的数量和相应类型与指向函数的原型相匹配,一切都会起作用。但是,如果您缺少参数列表或使用不同类型的值……那是未定义的行为。

因为您在 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);

GCC 开始抱怨。

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

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

... 将第一个参数指定为指向返回 double 并接受未指定参数的函数的指针。这可以防止 GCC 在 main().

中发出有关调用该函数的类型不匹配的警告

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