Using void (*)() pointers for other functions

In this particular case, the calls are legal.

Section 6.7.6.3p15 of the C standard spells out what makes two function type compatible (relevant part in bold):

For two function types to be compatible, both shall specify compatible return types. Moreover, the parameter type lists, if both are present, shall agree in the number of parameters and in use of the ellipsis terminator; corresponding parameters shall have compatible types. If one type has a parameter type list and the other type is specified by a function declarator that is not part of a function definition and that contains an empty identifier list, the parameter list shall not have an ellipsis terminator and the type of each parameter shall be compatible with the type that results from the application of the default argument promotions. If one type has a parameter type list and the other type is specified by a function definition that contains a (possibly empty) identifier list, both shall agree in the number of parameters, and the type of each prototype parameter shall be compatible with the type that results from the application of the default argument promotions to the type of the corresponding identifier. (In the determination of type compatibility and of a composite type, each parameter declared with function or array type is taken as having the adjusted type and each parameter declared with qualified type is taken as having the unqualified version of its declared type.)

So you have a typedef with type:

void()

And functions with type:

void(void)
void(double)

The two function definitions don't use ellipsis (...) so that satisfies the fist condition. For the second condition, let's look at what the default argument promotions are. Those are specified in section 6.5.2.2p6:

If the expression that denotes the called function has a type that does not include a prototype, the integer promotions are performed on each argument, and arguments that have type float are promoted to double. These are called the default argument promotions.

The first function has no arguments, so it is compatible. The second function has a single double argument, which matches the default argument promotions, so it is also compatible.

To give some more examples, the following functions would also be compatible:

void f1(long);
void f2(int);

But these would not:

void f3(float);
void f4(char);
void f5(short);

As mentioned in @StoryTeller's answer, the use of function declarators with empty parentheses is an obsolescent feature, but it can be avoided:

#include    <stdio.h>
#include    <stdlib.h>

typedef void    funp(void);

static  void    funcall( funp* F, int args, double x)
{
    switch( args)
    {
        case    0:
            F();
            break;
        case    1:  
            {
                typedef void fn(double);
                ((fn *)F)(x);
            }
            break;
    }
}

static  void    fun0( void)
{
    printf( "zero\n");
}

static  void    fun1( double x)
{
    printf( "one\t%f\n", x);
}

int main( void )
{
    funcall( (funp*)fun0, 0, 17.0);
    funcall( (funp*)fun1, 1, 17.0);
    return EXIT_SUCCESS;
}

EDIT: Changed parameter list of main to void for compliance.


In answer to the query:

"Moreover, the parameter type lists, if both are present, shall agree in the number of parameters" would seem to mean that the types of funp and of fun1 are incompatible. Is it ok to cast?

The answer is yes, it is OK to cast. From C11 draft 6.3.2.3 para 8:

A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer. If a converted pointer is used to call a function whose type is not compatible with the referenced type, the behavior is undefined.

In the code, the pointer to fun1 has been converted to a different function pointer type in the call to funcall, and converted back to the original type within funcall so can be used to call fun1.


As another answer notes, the code you showed is valid C today. But that may change at any point in the future, due to the use of a function type with no parameter list.

6.11 Future language directions

6.11.6 Function declarators

1 The use of function declarators with empty parentheses (not prototype-format parameter type declarators) is an obsolescent feature.

An obsolescent feature is one that is subject to removal in future standard versions. So if you wish your code to be future proof, it's best to avoid it.