Reputation: 990
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:
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?
Upvotes: 22
Views: 1653
Reputation: 263657
This:
void aux(double (*function)(), double n, double x);
uses an old-style non-prototype declaration for 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. 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.
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). 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).
Upvotes: 38
Reputation: 11514
Because you specified prototype of aux()
before main and function
doesn't have any specified argument types. 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:
void aux(double (*function)(double), double n, double x);
GCC starts complaining.
Upvotes: 8
Reputation: 181932
Your prototype for function 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. This prevents GCC from emitting warnings about mismatched types for the calls to that function in 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. 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.
Upvotes: 9
Reputation: 162367
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. 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.
Upvotes: 5