简体   繁体   English

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

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

Knowing that this call: 知道这个电话:

pow(4);

will generate this error message: 将生成此错误消息:

 error: too few arguments to function ‘pow’

I am learning pointers to functions and I got surprised when seen this code below working. 我正在学习函数的指针,当看到下面的代码工作时,我感到很惊讶。 But why? 但为什么?

#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));  
}

I compiled using: 我编译使用:

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

The result is: 结果是:

pow(4.00, 2.00): 16.000000

sqrt(4.00, 0.00): 2.000000

If I change the third parameter of the first call of aux to 3, the result changes to: 如果我将第一次调用aux的第三个参数更改为3,结果将更改为:

pow(4.00, 3.00): 64.000000

sqrt(4.00, 0.00): 2.000000

And one more question. 还有一个问题。 What is the correct way to declare and use pointers to functions in this case? 在这种情况下,声明和使用函数指针的正确方法是什么?

This: 这个:

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

uses an old-style non-prototype declaration for function . 使用旧式非原型声明来实现function The empty parentheses () mean that the function takes a fixed but unspecified number and type(s) of arguments. 空括号()表示该函数采用固定但未指定的数字和类型的参数。

C still permits this kind of declaration for backward compatibility. C仍然允许这种声明以实现向后兼容。 Prototypes (function declarations that specify the types of the parameters) were introduced by ANSI C in 1989. Prior to that, it was not possible to specify parameter types in a function declaration, and compilers could not check whether a call passed the correct number and type(s) of arguments. ANSI C在1989年引入了原型(指定参数类型的函数声明)。在此之前,无法在函数声明中指定参数类型,并且编译器无法检查调用是否传递了正确的数字和参数的类型。

Such declarations are "obsolescent", meaning that support for them could be removed from a future C standard (but in more than 20 years the committee hasn't gotten around to removing them). 这样的声明是“过时的”,这意味着可以从未来的C标准中删除对它们的支持(但是在20多年的时间里,委员会还没有去除它们)。 Calling a function with the wrong number of types of arguments will not necessarily be diagnosed by the compiler, and the behavior is undefined. 调用具有错误数量的参数类型的函数不一定会由编译器诊断,并且行为未定义。

The rules for compatibility of function types are a bit complicated when one has a prototype and the other doesn't. 当一个有原型而另一个没有原型时,函数类型兼容性的规则有点复杂。 These types: 这些类型:

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

are not compatible with each other, but they're both compatible with this type: 彼此不兼容,但它们兼容此类型:

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

which is what makes it possible to have incorrect calls without a diagnostic from the compiler. 这使得在没有编译器诊断的情况下进行错误调用成为可能。

To avoid this problem, always use prototypes : 要避免此问题,请始终使用原型

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

Not only do you get better diagnostics from your compiler, you don't have to worry about the convoluted compatibility rules for non-prototyped functions (which, if you're curious, are specified in N1570 6.7.6.3 paragraph 16). 您不仅可以从编译器获得更好的诊断,而且不必担心非原型函数的复杂兼容性规则(如果您好奇,则在N1570 6.7.6.3第16段中指定)。

Your prototype for function aux() ... 函数aux()原型...

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

... specifies the first argument to be a pointer to a function returning double and accepting unspecified arguments. ...指定第一个参数是指向返回double并接受未指定参数的函数的指针。 This prevents GCC from emitting warnings about mismatched types for the calls to that function in main() . 这可以防止GCC在main()发出有关该函数调用的不匹配类型的警告。

However, function aux() 's definition gives a more specific type for its first parameter, one that is incompatible with the actual arguments you are passing. 但是,函数aux()定义为其第一个参数提供了一个更具体的类型,一个与您传递的实际参数不兼容。 Calling those functions via the pointer has undefined semantics . 通过指针调用这些函数具有未定义的语义 Pretty much anything could happen, including that the behavior appears to be what you wanted. 几乎任何事情都可能发生,包括行为似乎是你想要的。 You cannot rely on anything about that behavior. 你不能依赖任何关于这种行为的东西。

Because you specified prototype of aux() before main and function doesn't have any specified argument types. 因为你在main之前指定了aux()原型,而且function没有任何指定的参数类型。 Learn the difference: 了解差异:

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 */

If you declare aux() prototype as: 如果将aux()原型声明为:

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

GCC starts complaining. 海湾合作委员会开始抱怨。

An empty pair of parentheses () tells the C compiler that the function may be called with any number of parameters, but that the function itself has a specific number of parameters for its prototype. 一对空括号()告诉C编译器可以使用任意数量的参数调用该函数,但该函数本身具有其原型的特定数量的参数。 So if using it with a function pointer and the number and respective types of passed parameters match prototype of the pointed-to-function everything will work. 因此,如果将它与函数指针一起使用,并且传递的参数的数量和相应类型与指向函数的原型匹配,则一切都将起作用。 If you starve the parameter list or use differently typed values though… well that's undefined behavior. 如果你饿了参数列表或使用不同类型的值,那么......那是未定义的行为。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM