Home > Software engineering >  C function pointer at declaration and argument
C function pointer at declaration and argument

Time:09-05

I am confused about using C function pointers.

using fn_p1 = void(int);  // function pointer
using fn_p2 = void (*)(int);

void functional(fn_p1 f) {
    f(1);
}

void callback(int value){
    // do something
}

int main() {
    fn_p2 f = callback; //works
    fn_p1 f1 = static_cast<fn_p1>(f); //does not work
    fn_p1 f2 = callback; //does not work
    fn_p1 f2 = static_cast<fn_p1>(callback); //does not work

    functional(f);  // works, the argument is form of void(*)(int)
    f(1);           // works
    
    functional(*f);  // works, the argument is dereferenced, void(int)
    (*f)(1);         // works
    return 0;
}

I know there is no difference if you call a function pointer with f(1), (*f)(1), or (*****f)(1). I don't get how functional(f); works but fn_p1 f1 = static_cast<fn_p1>(f); and its variants can not since they define the function pointer as using fn_p1 = void(int);. Could anyone explain how the function pointer works or how the compiler deals with it?

CodePudding user response:

The most useful error that the IDE should give you is on the line fn_p1 f2 = callback;:

Illegal initializer (only variables can be initialized) [illegal_initializer]

(This is the message I get from clangd.)

That means literally that an entity of type void(int) (or more in general someReturnType(someArgTypes...)) is not a variable.

Indeed, in C functions are not first class, which means that you can't pass them to function and don't get them in return from function; when you think you are successfully doing so, you're in reality passing or taking back function pointers.

In other words there's no such a thing in C as "a function value". Functions are not values that you can assign to.

When you write

fn_p1 f2 = callback; // assuming using fn_p1 = void(int);

you are truly trying to create a variable of type void(int). But such a thing doesn't exist, hence the error.

The static_casts don't work for fundamentally the same reason.


As regards

void functional(fn_p1 f) {
    f(1);
}

function-to-(function)pointer decaying is happening. f is truly of type fn_p1*.

You can easily verify it by writing an invalid statement in functional in order to trigger a compiler error telling you what the type of f is, like this

void functional(fn_p1 f) {
    int foo = f;
    f(1);
}

Clangd answers this

Cannot initialize a variable of type 'int' with an lvalue of type 'fn_p1 ' (aka 'void ()(int)') [init_conversion_failed]

which indirectly tells you that f is of type fn_p1*, so you better write that instead of fn_p1 as the type of the parameter of functional, at least for clarity (similarly to how you should prefer writing T* instead of T[] as a parameter type in a function taking a c-style array of Ts).


If you truly want to assign functions to variables, you should look at lambdas, structs operator(), std::function, and the topic of function objects in general.

  • Related