cpointersfunction-pointersstatic-typing

Why does a function pointer declaration need to know the types of the parameters and return value?


Why does a function declaration in C need to know the types of the pointed-to-function's parameters and return value?

A pointer declaration in c is written as follows:

returnType ( *funcPtrName ) ( paramTypes )

For example foo below:

char my_func ( int x )
{
    ...
}


int main ()
{
    char ( *foo ) ( int );

    foo = my_func;

    foo( 2 );
}

When declaring a pointer to a value, the type is used to determine the size (in bytes) of one element. For example in int* p, the int tells the compiler that the elements pointed to by p are sizeof( int ) apart. This information is used for pointer arithmetic.

However, a function pointer points to a single address and pointer arithmetic is not allowed.

So why is the extra information needed? Are the returnType and paramTypes included solely for the purpose of supporting error checking by the compiler? (Eg. to alert the user when they assign a function whose type doesn't match).


Solution

  • There are several reasons. Let’s consider the return value first.

    Suppose a program contains the code y = f(x);. After calling f, the compiler needs to get the value that f returned and assign it to y. Where does it get that value? In some systems, integer return values are passed in general registers and floating-point return values are passed in floating-point registers. So the compiler cannot know where the return value is unless it knows the type. Wide integers, such as long long int, might be passed in multiple registers. Small structures might be returned in registers, while large structures might be returned in memory, using a pointer to space the calling function provides and that it is required to pass to the called function as a hidden argument. The compiler needs to know the type to know how many bytes to get and where they are.

    Similarly, the compiler needs to know where to put the arguments when it is calling a function. The first several integer arguments might be passed in general registers, while the first few floating-point arguments might be passed in floating-point registers. Additional arguments of either type might be pushed onto the stack.

    Additionally, sometimes programmers might pass an integer expression where the function actually expects a floating-point argument, such as in pow(x, 4). When the compiler knows the argument must be a floating-point argument, it can convert it to the expected type.

    Another benefit is that, when the compiler knows the types, it can report an error if the argument does not match the expected type and cannot be implicitly converted to the expected type, and it can similarly report an error if the return type does not match.