This & symbol is called an ampersand. This & is used in a unary operator. The purpose of this address operator or pointer is used to return the address of the variable.
The fundamental rules of pointer operators are: The * operator turns a value of type pointer to T into a variable of type T . The & operator turns a variable of type T into a value of type pointer to T .
Every pointer is of some specific type. There's a special generic pointer type void* that can point to any object type, but you have to convert a void* to some specific pointer type before you can dereference it.
Example 2: Passing Pointers to FunctionsWe then passed the pointer p to the addOne() function. The ptr pointer gets this address in the addOne() function. Inside the function, we increased the value stored at ptr by 1 using (*ptr)++; . Since ptr and p pointers both have the same address, *p inside main() is also 11.
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
:
foo
is implicitly converted to a pointer to itself and the first *
is applied to that function pointer, yielding the function foo
again.*
is applied, again yielding the function foo
.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
:
foo
is implicitly converted to a pointer to itself; the unary *
is applied, yielding foo
again.&
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.
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.
&
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With