248

Why do the following work?

void foo() {
    cout << "Foo to you too!\n";
};

int main() {
    void (*p1_foo)() = foo;
    void (*p2_foo)() = *foo;
    void (*p3_foo)() = &foo;
    void (*p4_foo)() = *&foo;
    void (*p5_foo)() = &*foo;
    void (*p6_foo)() = **foo;
    void (*p7_foo)() = **********************foo;

    (*p1_foo)();
    (*p2_foo)();
    (*p3_foo)();
    (*p4_foo)();
    (*p5_foo)();
    (*p6_foo)();
    (*p7_foo)();
}
Lundin
  • 195,001
  • 40
  • 254
  • 396
Jimmy
  • 4,419
  • 6
  • 21
  • 30

5 Answers5

250

There are a few pieces to this that allow all of these combinations of operators to work the same way.

The fundamental reason why all of these work is that a function (like foo) is implicitly convertible to a pointer to the function. This is why void (*p1_foo)() = foo; works: foo is implicitly converted into a pointer to itself and that pointer is assigned to p1_foo.

The unary &, when applied to a function, yields a pointer to the function, just like it yields the address of an object when it is applied to an object. For pointers to ordinary functions, it is always redundant because of the implicit function-to-function-pointer conversion. In any case, this is why void (*p3_foo)() = &foo; works.

The unary *, when applied to a function pointer, yields the pointed-to function, just like it yields the pointed-to object when it is applied to an ordinary pointer to an object.

These rules can be combined. Consider your second to last example, **foo:

  • First, foo is implicitly converted to a pointer to itself and the first * is applied to that function pointer, yielding the function foo again.
  • Then, the result is again implicitly converted to a pointer to itself and the second * is applied, again yielding the function foo.
  • It is then implicitly converted to a function pointer again and assigned to the variable.

You can add as many *s as you like, the result is always the same. The more *s, the merrier.

We can also consider your fifth example, &*foo:

  • First, foo is implicitly converted to a pointer to itself; the unary * is applied, yielding foo again.
  • Then, the & is applied to foo, yielding a pointer to foo, which is assigned to the variable.

The & can only be applied to a function though, not to a function that has been converted to a function pointer (unless, of course, the function pointer is a variable, in which case the result is a pointer-to-a-pointer-to-a-function; for example, you could add to your list void (**pp_foo)() = &p7_foo;).

This is why &&foo doesn't work: &foo is not a function; it is a function pointer that is an rvalue. However, &*&*&*&*&*&*foo would work, as would &******&foo, because in both of those expressions the & is always applied to a function and not to an rvalue function pointer.

Note also that you do not need to use the unary * to make the call via the function pointer; both (*p1_foo)(); and (p1_foo)(); have the same result, again because of the function-to-function-pointer conversion.

James McNellis
  • 348,265
  • 75
  • 913
  • 977
  • So the function name by itself is really a function pointer, right? If so, that clears up the differences between p1, p2, p6, and p7 (i.e. there is none). What about references to function pointers? Is there a difference between p3, p4, and p5? – Jimmy Aug 01 '11 at 00:43
  • 2
    @Jimmy: Those aren't references to function pointers, they are just function pointers. `&foo` takes the address of `foo`, which results in a function pointer pointing at `foo`, as one would expect. – Dennis Zickefoose Aug 01 '11 at 00:59
  • 2
    I've updated the answer with a more correct (and far lengthier) explanation. It suffices to say that function pointers in C and C++ are bizarre. – James McNellis Aug 01 '11 at 01:01
  • @Dennis: Aha, that would make sense. Is that why you can't have two &'s next to eachother? Is that true in general that you cannot take the address of an address? It seems to me like an address should have an address that is addressable... if that abstraction makes sense. – Jimmy Aug 01 '11 at 01:05
  • @James: Yours is a truly excellent answer. I guess it turns out you make have any number of *s and &s mixed up together (not that you'd want to), just so long as you don't have two &s in a row. But if "the unary &, when applied to a function, yields a pointer to the function, just like it yields the address of an object when it is applied to an object" shouldn't you be able to chain two &s in a row? – Jimmy Aug 01 '11 at 01:11
  • 2
    You can't chain `&` operators for objects either: given `int p;`, `&p` yields a pointer to `p` and is an rvalue expression; the `&` operator requires an lvalue expression. – James McNellis Aug 01 '11 at 01:15
  • 2
    (There are a number of good explanations of what lvalues and rvalues are in the answers to ["What are rvalues, lvalues, xvalues, glvalues, and prvalues?"](http://stackoverflow.com/questions/3601602/what-are-rvalues-lvalues-xvalues-glvalues-and-prvalues) That question deals with C++0x features, but the answers do a pretty good job explaining the difference between rvalues and lvalues as well.). – James McNellis Aug 01 '11 at 01:21
  • 14
    I disagree. The more `*`'s, the _less merry_. – Seth Carnegie Aug 01 '11 at 01:28
  • 32
    Please do not edit the syntax of my examples. I have picked the examples very specifically to demonstrate features of the language. – James McNellis Mar 04 '12 at 05:58
  • When calling the function pointer, the braces are not necessary, though it provides a hint to humans that a function definition for that name isn't likely to be found. – MauganRa Dec 04 '13 at 14:02
  • 1
    There's really no reason to dereference function pointers before calling through them. The only reason I've done so in this answer is to emphasize particular syntactic features and reduce the differences between examples to the minimal number of differences required to demonstrate what I am trying to explain. E.g., in the last sentence, we could just as well say `p1_foo()` instead of `(p1_foo)()`, but the "extra" parentheses make it more syntactically similar to `(*p1_foo)()`, to which we are making a comparison. – James McNellis Dec 11 '13 at 08:50
  • 14
    As a side note, the C standard explicitly states that a combination of `&*` cancel out each other (6.5.3.2): `"The unary & operator yields the address of its operand."` /--/ `"If the operand is the result of a unary * operator, neither that operator nor the & operator is evaluated and the result is as if both were omitted, except that the constraints on the operators still apply and the result is not an lvalue."`. – Lundin Nov 30 '15 at 15:21
  • @JamesMcNellis Excellent answer. I have some questions. Question 1: does all your answer apply to C, or some part is C++ exclusive? Question 2: "For pointers to ordinary functions" - (honest question, not being picky about your writing) is there some kind of non-ordinary function? If yes, what would that be? Question 3: you mention a "function-to-function-pointer" implicit conversion. As I understand, it only goes that way, never the opposite (that is, there is no implicit "function-pointer-to-function" conversion), right? ... – Rafael Eyng Jan 30 '20 at 15:52
  • 1
    @JamesMcNellis ... Question 4: in both C and C++, the `sizeof(p1_foo)` is the size of a pointer (8 bytes, in a 64 bits machine), this is ok. But the `sizeof(foo)` itself is an error in C++ (`invalid application of 'sizeof' to a function type`), but is `1` in C. This `1` is the size of what? – Rafael Eyng Jan 30 '20 at 16:01
  • @SethCarnegie Of course James's "The more `*`s, the merrier." was a joke, and one that made my day to boot! – Lover of Structure May 10 '23 at 02:42
13

I think it's also helpful to remember that C is just an abstraction for the underlying machine and this is one of the places where that abstraction is leaking.

From the perspective of the computer, a function is just a memory address which, if executed, performs other instructions. So a function in C is itself modelled as an address, which probably leads to the design that a function is "the same" as the address it points to.

madumlao
  • 448
  • 5
  • 9
2

If you are still not very convinced with @JamesMcNellis's answer, here is a prove. This is the AST(abstract syntax tree) from Clang compiler. Abstract syntax tree is the internal representation of the program structure inside the compiler.

void func1() {};
void test() {
    func1();
    (*func1)();
    (&func1)();

    void(*func1ptr)(void) = func1;
    func1ptr();
    (*func1ptr)();
    //(&func1ptr)();//error since func1ptr is a variable, &func1ptr is its address which is not callable.
}

AST:

//func1();
|-CallExpr //call the pointer
| `-ImplicitCastExpr //implicitly convert func1 to pointer
|   `-DeclRefExpr //reference func1

//(*func1)();
|-CallExpr //call the pointer
| `-ImplicitCastExpr //implicitly convert the funtion to pointer
|   `-ParenExpr //parentheses
|     `-UnaryOperator //* operator get function from the pointer
|       `-ImplicitCastExpr //implicitly convert func1 to pointer
|         `-DeclRefExpr //reference func1

//(&func1)();
|-CallExpr //call the pointer
| `-ParenExpr //parentheses
|   `-UnaryOperator //& get pointer from func1
|     `-DeclRefExpr //reference func1

//void(*func1ptr)(void) = func1;
|-DeclStmt //define variable func1ptr
| `-VarDecl //define variable func1ptr
|   `-ImplicitCastExpr //implicitly convert func1 to pointer
|     `-DeclRefExpr  //reference func1

//func1ptr();
|-CallExpr  //call the pointer
| `-ImplicitCastExpr //implicitly convert func1ptr to pointer
|   `-DeclRefExpr //reference the variable func1ptr

//(*func1ptr)();
`-CallExpr //call the pointer 
  `-ImplicitCastExpr //implicitly convert the function to pointer
    `-ParenExpr //parentheses
      `-UnaryOperator //* get the function from the pointer
        `-ImplicitCastExpr //implicitly convert func1ptr to pointer
          `-DeclRefExpr //reference the variable func1ptr
jw_
  • 1,663
  • 18
  • 32
1

& and * are idempotent operations on a symbol declared as a function in C which means func == *func == &func == *&func and therefore *func == **func, but they have different types, so you'll get a warning.

The parameter type of a passed function address to a function can be int () or int (*)(), and it can be passed as *func, func or &func. Calling (&func)() is the same as func() or (*func)(). Godbolt link.

* and & have no meaning on a function symbol, and instead of producing an error, the compiler chooses to interpret it as the address of func in both cases. The function does not exist as a separate pointer, like an array symbol, therefore &arr is the same as arr, because it is not a physical pointer with an address at runtime, it's a logical pointer at compiler level. Furthermore *func would read the first byte of the function code, which is an a code section, and rather than produce a compiler error or allow it to be a runtime error segmentation fault, it's just interpreted by the compiler as the address of the function.

& on a symbol declared as a function pointer however will get the address of the pointer (because it is now an actual pointer variable that manifests on the stack or data section), whereas funcp and *funcp will still be interpreted to be the address of the function.

Lewis Kelsey
  • 4,129
  • 1
  • 32
  • 42
-1

When calling foo from a pointer, even the parentheses and the asterisk can be omitted, just as directly calling the function with its original name, i.e. (*p1_foo)() is equivalent to p1_foo().

Kevin Tan
  • 39
  • 3