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

3 comments

@Lewis Kelsey 2020-03-21 13:17:09

& and * are idempotent operations on a symbol declared as a function in C which means func == *func == &func == *&func and therefore *func == **func

It means that the type int () is the same as int (*)() as a function parameter and a defined func can be passed as *func, func or &func. (&func)() is the same as func(). Godbolt link.

A function is really an address, therefore * and & have no meaning, and instead of producing an error, the compiler chooses to interpret it as the address of func.

& on a symbol declared as a function pointer however will get the address of the pointer (because it now has a separate purpose), whereas funcp and *funcp will be identical

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

@Rafael Eyng 2020-01-30 15:52:08

@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 2020-01-30 16:01:19

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

Related Questions

Sponsored Content

9 Answered Questions

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

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

26 Answered Questions

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

1 Answered Questions

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

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

13 Answered Questions

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

  • 2008-09-22 15:55:52
  • Rob
  • 350572 View
  • 540 Score
  • 13 Answer
  • Tags:   c++ templates

21 Answered Questions

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

23 Answered Questions

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

11 Answered Questions

[SOLVED] How do function pointers in C work?

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

24 Answered Questions

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

14 Answered Questions

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

Sponsored Content