Unlike with object pointers (inc. those to arrays), the &
and *
operators are optional when working with function pointers. Some people like to use them anyway, being of the opinion that it makes the code's use of function pointers clearer. Personally I disagree, and just use the names.
Therefore:
// given
typedef int (* F) (void);
extern int foo (void);
// the following definitions are all identical:
F fp1 = foo;
F fp2 = &foo;
F fp3 = *foo;
// as are the following call expressions:
foo ();
(&foo) ();
(*foo) ();
fp1 ();
(*fp1) ();
(******* fp1) ();
Much like the case for arrays, a function used as a value in any expression always decays to a function pointer, except when used as the immediate argument to &
. Technically this also includes function call expressions, so the thing on the left of the parentheses is always a function pointer, even when the call is directly to a statically-named function (i.e. the expression foo ()
). Similarly, dereferencing a function pointer with *
retrieves a function's "value", but the only thing a function "value" can do is immediately decay back to a pointer, which is why you can dereference it with as many stars as you like.
None of this has any effect at runtime; it only affects the expression's type.
Using a function pointer as the direct argument to &
will do something else, however: it will either create a standard double-pointer (a pointer to the variable), or will be syntactically invalid if the function expression is not an lvalue. You can stick one level of &
in front of some of those optional stars as well, in which case it prevents the result of the *
operator from immediately decaying and instead converts the type explicitly, continuing to achieve absolutely nothing.
So the simpler (and IMO clearer) way to write the code from the question would be as follows:
typedef void (*function_pointer) ();
// 1 - direct
((function_pointer)0)();
// 2 - intermediate variable
function_pointer fp = (function_pointer)0;
fp ();
Most of the stars do nothing, and the code is easier to read without them. This also shows that the second line of the question's expression is actually two operations (both generally no-ops at the machine level) - first it converts 0
to have the type function_pointer
, and then it applies the *
. (Then in a third no-op, it decays the *
away again, outside the parentheses.)