Why are both `p()` and `(*p)()` valid?

Viewed 111

Say for this simple code:

int foo(void);
int (*p)(void);
p = foo;
p = &foo;
int a = p();
int b = (*p)();

In the above example, Line 3&4 are both valid, Line 5&6 are also both valid.

In fact if you try to play with variable pointers like that when you play with function pointers, your program will surely die. So why is it valid for function pointers?

2 Answers

A function (more precisely, a function designator) is converted to a pointer to a function (except when it is the operand of sizeof, _Alignof, or the unary &). So foo and &foo are the same thing because foo is automatically converted to &foo.

Similarly, p is a pointer to a function, so *p is the function, so it is automatically converted to the address of the function.

In fact, because *p is converted back to a pointer right away, you can apply * to it again, as in int b = (**p)();. And you can do that again and again and again: int b = (************p)();.

From the 2011 C standard (committee draft N1570), 6.3.2.1 4:

A function designator is an expression that has function type. Except when it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, a function designator with type “function returning type” is converted to an expression that has type “pointer to function returning type”.

This is just a consequence of the fact that C developers wanted to make it easy to use both functions like foo and pointers-to-functions like p. If you had to write foo() for one and (*p)() for the other, that would be a bit of a nuisance. Or at least more typing. Or, if you had to write (&foo)() for one and p() for the other, that is again more typing. So they just said whenever you write a function, we will automatically make it the pointer, and you can call it without typing any more.

I suppose they could have said the function call operator can accept either a function or a pointer to a function, but they chose the automatic conversion instead.

Corollary: If you do not know whether something is a function, or a pointer to a function, or a pointer to a pointer to a function, etc., just slap a hundred asterisks in front of it, and the compiler will stop when it gets to the function.

According to §6.5.2.2/1 of the C11 Standard, in a function call:

The expression that denotes the called function shall have type pointer to function returning void or returning a complete object type other than an array type.

Now, in most expressions a function designator is converted to a function pointer (§6.3.2.1/4):

A function designator is an expression that has function type. Except when it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, a function designator with type ''function returning type'' is converted to an expression that has type ''pointer to function returning type''.

So, in a function call such as int a = p();, the function designator p is converted to a function pointer, which is needed in the expression before the postfix operator () for the function call.

But, according to §6.5.3.2/4 about the indirection operator:

If the operand points to a function, the result is a function designator

So, in a function call such as int b = (*p)();, the function designator p is converted to a function pointer, and the result of the indirection operator acting on this function pointer is a function designator, which itself is converted to the function pointer needed in the postfix expression for the function call.

By §6.5.2.2/1 above, foo is converted to a function pointer in the expression p = foo. But, in the expression p = &foo, the function designator is not converted to a function pointer. Here, §6.5.3.2/3 states that:

The unary & operator yields the address of its operand. If the operand has type ''type'', the result has type ''pointer to type''.... Otherwise, the result is a pointer to the object or function designated by its operand.

So the expression &foo evaluates to a pointer to the function designated by foo, as expected.

Related