By Jimmy


2011-08-01 00:37:26 8 Comments

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)();
}

2 comments

@madumlao 2016-03-09 04:58:07

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.

@James McNellis 2011-08-01 00:38:47

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.

@Jimmy 2011-08-01 00:43:52

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?

@Dennis Zickefoose 2011-08-01 00:59:50

@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.

@James McNellis 2011-08-01 01:01:48

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.

@Jimmy 2011-08-01 01:05:45

@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 2011-08-01 01:11:24

@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?

@James McNellis 2011-08-01 01:15:47

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 2011-08-01 01:21:02

(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?" That question deals with C++0x features, but the answers do a pretty good job explaining the difference between rvalues and lvalues as well.).

@Seth Carnegie 2011-08-01 01:28:37

I disagree. The more *'s, the less merry.

@James McNellis 2012-03-04 05:58:03

Please do not edit the syntax of my examples. I have picked the examples very specifically to demonstrate features of the language.

@MauganRa 2013-12-04 14:02:29

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.

@James McNellis 2013-12-11 08:50:20

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.

@Lundin 2015-11-30 15:21:40

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.".

Related Questions

Sponsored Content

21 Answered Questions

[SOLVED] Why should I use a pointer rather than the object itself?

  • 2014-03-03 11:54:16
  • gEdringer
  • 294526 View
  • 1521 Score
  • 21 Answer
  • Tags:   c++ pointers c++11

1 Answered Questions

[SOLVED] The Definitive C++ Book Guide and List

  • 2008-12-23 05:23:56
  • grepsedawk
  • 2222961 View
  • 4247 Score
  • 1 Answer
  • Tags:   c++ c++-faq

23 Answered Questions

[SOLVED] Why can't variables be declared in a switch statement?

12 Answered Questions

[SOLVED] Storing C++ template function definitions in a .CPP file

  • 2008-09-22 15:55:52
  • Rob
  • 305066 View
  • 475 Score
  • 12 Answer
  • Tags:   c++ templates

11 Answered Questions

[SOLVED] How do function pointers in C work?

  • 2009-05-08 15:49:17
  • Yuval Adam
  • 767463 View
  • 1159 Score
  • 11 Answer
  • Tags:   c function-pointers

25 Answered Questions

[SOLVED] Why do we need virtual functions in C++?

24 Answered Questions

[SOLVED] Image Processing: Algorithm Improvement for 'Coca-Cola Can' Recognition

9 Answered Questions

[SOLVED] Pointers in C: when to use the ampersand and the asterisk?

  • 2010-01-19 15:41:06
  • Pieter
  • 187414 View
  • 264 Score
  • 9 Answer
  • Tags:   c pointers

14 Answered Questions

[SOLVED] Why are function pointers and data pointers incompatible in C/C++?

Sponsored Content